Android子线程与主线程通信

时间:2022-08-22 05:07:41

想必大家都知道andorid中有以下规定:

1.主线程不能做耗时操作。

2.主线程中不能做联网操作。当然这是4.0以后的规定,4.0之前还是可以联网的。但是不管怎样我们还是要严格按照规定来写,哪怕你用的2.3的。

3.子线程是不能直接更新Ui的

下面我们就围绕这三个问题来讨论:

主线程中不能做耗时操作。android中规定不能阻塞Ui线程超过5s钟,否则会报“应用程序无法响应”异常。但是有的时候必须做耗时操作,比如说楼主在做音乐播放器的时候,要同步更新更新时间。所以必须要没1s中刷一下,这是你怎么都避免不了的吧,没办法啊,还能怎么办,开线程呗。同理主线程联网也是不可避免吧,但是官方又规定了主线程不能联网啊,还能怎么办,开线程呗。

首先我们要明确,对于第一第二个问题,我们做这些事的主要目的是什么,无非就是要个结果呈现出来而已。这就是更新Ui。

这就牵出了第三个问题,也是最主要的问题。在子线程更新Ui,但是子线程又不能直接更新Ui。

鉴于此,andorid提供了三种方式来解决问题:

方式一:使其他线程访问Ui线程,并委托后者更新Ui。对于这种方式,有三种方法可以起到作用。

Activity.runOnUiThread(Runnable),View.post(runnable),View.postDelayed(runnable)。

这种方式最简单,比如说下面这例子

About_Activity.this.runOnUiThread(new Runnable() {


@Override
public void run() {
newVersion.setVisibility(View.VISIBLE);
newVersion.setText(responseRes);
// 是否更新版本,必须放这里才能保证方法里面的得到的newVersion不为空
yesOrNoUpdataApk();
}
});

后两种方法跟Activity.runOnUiThread(Runnable)类似。

这种需要更新的Ui较少,或者说线程较少的情况。就非常适合用方式一,楼主也非常喜欢用方式一。但是对于复杂操作,方式一就显得很无力了,这样就需要使用方式二的线程间通信来处理。

方式二:在线程间进行通信,让想更新Ui的其他线程给Ui线程发送信息,Ui线程根据消息更新Ui。它的核心就是handlerhandler是线程通讯工具类。用于传递消息。它有两个队列:
1.消息队列
2.线程队列
消息队列使用sendMessage和HandleMessage的组合来发送和处理消息。
线程队列类似一段代码,或者说一个方法的委托,用户传递方法。使用post,postDelayed 添加委托,使用 removeCallbacks移除委托。

由上面的特性我们可以简单看出handler类似一个容器对象,它携带了消息的集合和委托的集合。java里没有委托delegate的概念,但是可以通过class来持有一个可执行的方法代理。
handler更像是一个传递者,在另外的线程里和主线程之间传递消息和可执行的代码。它不仅仅携带了数据,而且封装了一些操作行为,比如说在适当的时机(...)来执行线程队列里的“委托”的代码。 

handler可能是和消息队列交互的,我们在new Handler实例化对象时,这个对象应该就和主线程的消息队列建立了关系。当我们使用handler.Post(runnabler1),发送一个委托的方法runnabler1代理给handler时,主消息队列会在适当的时候执行这个runnabler1里的委托方法,即执行了runnabler.run方法。

Handler对于Message的处理不是并发的。一个Looper 只有处理完一条Message才会读取下一条,所以消息的处理是阻塞形式的。但是如果用不同的Looper则能达到并发的目的。Service 中,onStart的执行也是阻塞的。如果一个startService在onStart执行完成之前,再次条用startService也会阻塞。如果希望能尽快的执行onStart则可以在onStart中使用handler,因为Message的send是非阻塞的。如果要是不同消息的处理也是并发的,则可以用不同的Looper实例化Handler。 

1. Message Message消息 
理解为线程间交流的信息,处理数据后台线程需要更新UI,则发送Message内含一些数据给UI线程。

2. Handler Handler处理者 
是Message的主要处理者,负责Message的发送,Message内容的执行处理。后台线程就是通过传进来的Handler对象引用来sendMessage(Message)。而使用Handler,需要implement 该类的 handleMessage(Message) 方法,它是处理这些Message的操作内容,例如Update UI。 通常需要子类化Handler来实现handleMessage方法。

3. Message Queue Message Queue消息队列 
用来存放通过Handler发布的消息,按照先进先出执行。 每个message queue都会有一个对应的Handler。Handler会向message queue通过两种方法发送消息:sendMessage或post。这两种消息都会插在message queue队尾并按先进先出执行。但通过这两种方法发送的消息执行的方式略有不同:通过sendMessage发送的是一个message对象,会被Handler的handleMessage()函数处理;而通过post方法发送的是一个runnable对象,则会自己执行。 

4. Looper Looper是每条线程里的Message Queue的管家。 
Android没有Global的Message Queue,而Android会自动替主线程(UI线程)建立Message Queue,但在子线程里并没有建立Message Queue。所以调用Looper.getMainLooper()得到的主线程的Looper不为NULL,但调用Looper.myLooper()得到当前线程的Looper就有可能为NULL。

一段典型的使用Looper完成线程间通信的代码如下:

class calThread extends Thread {

Handler handler;

@Override
public void run() {

//非Ui线程中要创建Looper对象,必须调用prepare()
Looper.prepare();
handler = new Handler() {
@Override
public void handleMessage(Message msg) {
// 在这里处理接收到的消息
}
};
Looper.loop();
}
}

默认情况下Handler会与其被定义时所在线程的Looper绑定,比如Ui线程中定义,是与Ui线程的Looper绑定。mainHandler=new Handler()等价于new Handler(Loop.myLooper)。

方式三:使用android提供的AsyncTask。

对于这种方式,楼主只想说到目前为止我用的非常少,掌握前两者已经足够了,当然在这里可以提供个好的连接,大家参考参考。

http://www.cnblogs.com/devinzhang/archive/2012/02/13/2350070.html

好了,楼主也是andorid初学者,这也是我的处女博客,欢迎大家提出不同的意见,我们共同学习,共同进步。Android子线程与主线程通信Android子线程与主线程通信我还不造怎么编辑文本格式呢,写的这么垃圾大家凑合着看看吧。好了上课去了。