您的位置:首页 > 其它

Handler机制解析

2015-08-21 12:24 232 查看
1.Handler是Android中用来在线程中传递消息的工具,它提供了一种异步的回调机制,使在完成了一个相对耗时的操作后作出相应和通知。

2.Handler的使用,为了能让handler在线程间传递消息,还需要使用到Looper,messageQueue,message。

   Looper是为了指定的单一线程创建一个消息循环,与线程一一对应,在UI线程中会自动建立一个Looper,

   而在子线程中则需要手动创建或者使用主线程的Looper。

   在Looper与线程进行关联时会同时产生一个messageQueue消息队列,用来存放handler所发送的message,遵循先进先出原则。

   message包含必要的描述和属性数据,并且此对象可以被发送给Handler处理,属性字段:arg1、arg2、what、obj、replyTo等。

   what是用来保存消息标示的;obj是Object类型的任意对象;replyTo是消息管理器,会关联到一个handler,handler就是处理其中的消息。

   通常Message可以直接new出来的,但推荐调用handler中的obtainMessage方法来直接获得Message对象。

   (从系统线程池中直接取出,可以避免message的创建和销毁,从而节省资源。)

   在主线程(UI)中使用handler很简单,只需要创建一个handler对象,并实现他的handleMessage方法,在该方法中对接收到的消息作出相应处理。

3.handler发送消息的机制。在使用handler.sendEmptyMessage(0);发送一个消息对象后,message对象会被放入一个messageQueue队列中,

   而该队列属于某个Lopper对象,每个Looper对象通过ThreadLocal.set(new Looper())跟一个Thread进行绑定,Looper对象所属的线程在Looper.Loop

   方法中循环执行从messageQueue消息队列中读取message对象并把message对象交由handler处理,调用handler的dispatchMessage方法。

4.子线程中的handler,当在新建的子线程中建立handler时,程序会报RuntimeException异常,产生这个异常的原因是因为子线程没有建立Looper,

   为什么主线程中不会报错 呢?来看一下ActivityThread的源码:

public static final void main(String[] args) {
SamplingProfilerIntegration.start();

Process.setArgV0("<pre-initialized>");

Looper.prepareMainLooper();

ActivityThread thread = new ActivityThread();
thread.attach(false);

Looper.loop();

if (Process.supportsProcesses()) {
throw new RuntimeException("Main thread loop unexpectedly exited");
}

thread.detach();
String name = (thread.mInitialApplication != null)
? thread.mInitialApplication.getPackageName()
: "<unknown>";
Slog.i(TAG, "Main thread of " + name + " is now exiting");
}
可以看到,在main函数中它已经做了这个事情了,调用 Looper.prepareMainLooper(); Looper.loop();在prepareMainLooper方法中建立了一个looper对象,并且与当前的进程进行绑定,在Looper.loop方法中,线程建立消息循环机制,循环从MessageQueue获取Message对象,调用  msg.target.dispatchMessage(msg);进行处理msg.target在myThreadHandler.sendEmptyMessage(0)设置进去的,因为一个Thead中可以建立多个Hander,通过msg.target保证MessageQueue中的每个msg交由发送message的handler进行处理,然而Handler是怎样与Looper建立联系呢,打开Handler构造函数中会发现这样一段代码:

mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
当新建Handler对象时需要设置mLooper成员,Looper.myLooper是从当前线程中获取绑定的Looper对象:

public static final Looper myLooper() {
return (Looper)sThreadLocal.get();
}
若Looper对象没有创建,就会抛出"Can't create handler inside thread that has not called Looper.prepare()"这样一个异常。

所以想要在子线程中创建一个Handler对象就需要以下这种方式:

class MyThread extends Thread {
public void run() {
// 其它线程中新建一个handler
Log.i(TAG, MessageFormat.format("Thread run...", Thread.currentThread().getName()));
// 创建该线程的Looper对象,用于接收消息,在非主线程中是没有looper的所以在创建handler前一定要使用prepare()创建一个Looper
Looper.prepare();
newThreadHandler = new Handler() {
public void handleMessage(Message msg) {
Log.d(Constant.TAG, MessageFormat.format(
"newThreadHandler run...", Thread.currentThread().getName()));
}
};
Looper.myLooper().loop();// 建立一个消息循环,该线程不会退出
}
}


在其它线程中Handler使用主线程的Looper,前面我说了在新线程中要新建一个Handler需要调用Looper.prepare();

也有另一种方法就是使用主线程中的Looper,那就不必新建Looper对象了:

getMainLoopHandler =new Handler(Looper.getMainLooper()){
public void handleMessage(android.os.Message msg) {
Log.i(TAG, MessageFormat.format("handleMessage run...", Thread
.currentThread().getName()));
}
//该handleMessage方法将在UI线程中执行
};
这时候注意不要在handleMessage做太多的操作,因为它在主线程中执行,会影响主线程执行ui更新操作。

使用Message.callback回调

public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
从dispatchMessage定义可以看出,如果Message对象自带callback对象,handler不会执行handleMessage方法而是执行message.callback中定义的run方法,当然callback还是在handler关联的looper所绑定的线程中执行的。实际上Handler.post(Runnable r)方法就是把r添加到一个msg.callback的,也就是说,下面两种写法,没有什么区别:

1.使用Message.callback

Message msg = Message.obtain(newThreadHandler,new Runnable() {
@Override
public void run() {
Log.i(TAG, MessageFormat.format("newThreadHandler.Message.callback.run",
Thread.currentThread().getName()));
}
});
newThreadHandler.sendMessage(msg);

2.使用Handler.post

newThreadHandler.post(new Runnable() {
@Override
public void run() {
Log.i(TAG, MessageFormat.format("newThreadHandler.Message.callback.run",
Thread.currentThread().getName()));
}
});
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: