您的位置:首页 > 移动开发 > Android开发

Android中Handler消息处理机制原理

2016-05-27 11:55 585 查看
大家在编写android项目的时候应该用到Handler了吧.

一般做法应该都是先在主线程中创建一个Handler实例 

Handler handler = new Handler{

public void handleMessage(Message msg) {
//做相应处理
}

}

然后通过handler.sendMessage(Message msg)发送消息.

最后在handleMessage()回调中做相应的处理.

那么这里有几个疑问:什么是主线程?发送消息到哪里去了?handleMessage()为什么会被回调?

1.主线程也就是UI线程,所有的处理用户消息,以及绘制界面的工作都在该线程中完成的,UI线程是从ActivityThread运行的, 


分析点击android桌面app图标启动应用程序的过程 这篇文章第三十二步和三十三步中可以看到,第一次启动应用程序时,系统会为应用程序创建一个进程,从ActivityThread的main()方法开始执行,这个main()也就是UI线程的开始.


2.主线程中用handler发送消息消息发送到哪去了?这里我们来看一下main()方法,


在frameworks/base/core/java/android/app/ActivityThread.java中


public static void main(String[] args) {
......
Looper.prepareMainLooper();
......
Looper.loop();

throw new RuntimeException("Main thread loop unexpectedly exited");
}

这里调用了两个方法Looper.prepareMainLooper()和Looper.loop();


首先看第一个:

在frameworks/base/core/java/android/os/Looper.java中

public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
接着看prepare()函数:

private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper(quitAllowed));
}

这里有一个sThreadLocal对象,这是什么呢

public class Looper {
private static final String TAG = "Looper";

// sThreadLocal.get() will return null unless you've called prepare().
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();........

它是Looper类的一个静态成员.ThreadLocal类作用是提供"线程局部存储",简单的理解是:ThreadLocal对象内部会根据调用prepare()函数的线程的id保存一个数据对象,这个数据对象就是所谓的"线程局部存储对象",像这里就是主线程调用的,那么就为主线程保存一个数据对象,这个数据对象就是Looper(上面的泛型就是Looper),第一次调用prepare()函数,sThreadLocal肯定还没有存储的,所以通过sThreadLocal.set()函数设置进去,对应get()函数就是取出来.所以从上面prepare()函数可以看出来,prepare()函数在一个线程中只能调用一次,因为第二次调用该函数,get()获取的就不是null,就会报异常.

一般一个应用程序都在一个进程中,所以ActivityThread类的main()函数只会执行一次,所以prepare()函数只会执行一次.

通过上面分析我们知道,每个线程通过prepareMainLooper()函数调用prepare()函数,设置一个Looper对象,那么它们保存的Looper对象是不相同的.

好了,继续看到主线程的Looper对象是如何构造的.sThreadLocal.set(new Looper(quitAllowed));

private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mRun = true;
mThread = Thread.currentThread();
}


这里MessageQueue是一个消息队列,上面handler发送的消息就是发送到这个队列里了.消息队列采用排队的方式对消息进行处理,即先到的消息会先得到处理,但如果消息本身指定了被处理的时刻,则必须等到该时刻才能处理该消息.对于消息队列我就没有深究了.

这里就是给Looper对象创建了一个消息队列.

然后回到prepareMainLooper()函数,接着看sMainLooper = myLooper();

public static Looper myLooper() {
return sThreadLocal.get();
}


这一步其实就是把prepare()函数设置的Looper对象赋值给sMainLooper变量.

ActivityThread类的main()函数里面的prepareMainLooper()函数做了两件事,第一是创建了一个MessageQueue(消息队列),第二是创建了一个Looper对象,一个线程只有一个Looper对象,因此只有一个MessageQueue(消息对象).

ActivityThread类的main()函数里面的另一个函数Looper.loop();

public static void loop() {
final Looper me = myLooper();
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
final MessageQueue queue = me.mQueue;

// Make sure the identity of this thread is that of the local process,
// and keep track of what that identity token actually is.
Binder.clearCallingIdentity();
final long ident = Binder.clearCallingIdentity();

for (;;) {...
这个函数的就是从主线程的Looper对象的消息队列中不断的读取和发送消息.一个死循环吧.

读取消息然后回调对应handler的handleMessage(Message msg) 方法.

上面的三个问题我们了解差不多,但是又有新的疑问,new Handler()发送消息就怎么就会发送到主线程的Looper对象里面的消息队列里面去呢??

好了,我们来看一下Handler的构造方法:

public Handler() {
this(null, false);
}
public Handler(Callback callback, boolean async) {
...
mLooper = Looper.myLooper();//
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}

通过构造函数知道,在构造handler对象时(无参数),首先会取得当前线程对应的looper对象.如果该线程没有设置过looper对象,那么就会报异常.一般在我们在程序中new Handler()都是在主线程中,所以这次获取的是主线程的looper对象,而主线程的looper在程序启动的时候就创建好了(activitythread的main()方法里面),所以平时我们这样创建都没有报错.

这样一来我们也知道了,如果在一个线程中使用handler,那么该线程中必须创建了Looper对象.有了Looper对象也就有了消息队列(它在looper的构造方法里面创建).

这样线程称呼为异步消息线程.

总结一下:异步消息处理线程启动后会进入一个无限的循环体之中,每循环一次,从其内部的消息队列中取出一个消息,然后回调相应的消息处理函数,执行完成一个消息后则继续循环。若消息队列为空,线程则会阻塞等待.

android应用程序的主线程就这样一个异步消息线程.

那么除主线程外,我们自定义线程可不可定义成异步消息线程呢?当然可以,android提供了一个HandlerThread这个类

frameworks/base/core/java/android/os/HandlerThread.java

写一个线程MyThread 继承HandlerThread, 看它的run()方法

public void run() {
mTid = Process.myTid();
Looper.prepare();
synchronized (this) {
mLooper = Looper.myLooper();
notifyAll();
}
Process.setThreadPriority(mPriority);
onLooperPrepared();
Looper.loop();
mTid = -1;
}

是不是有点熟悉,就是先创建MessageQueue消息队列和Looper对象,然后进入死循环了.那么现在就可以使用handler向该线程的消息队列发送消息了,这里得注意了handler怎么与这个线程的消息队列关联起来.handler另外的构造函数:

public Handler(Looper looper) {
this(looper, null, false);
}
没错,直接传入该线程的looper对象就可以该线程的looper对象怎么获取呢:

public Looper getLooper() {
....      return mLooper;
}

也就是MyThread对象调用getLooper()就可以了.

这样想要往这个线程发送消息,构建一个handler传入looper对象.

我们在项目用得最多的应该是往主线程发送更新UI的消息,因为更新UI必须要在主线程中,不然会报异常.



内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息