Handler消息机制
@(Android)
Handler消息机制是Android系统提供的一种用于解决线程间通信的方式。Handler主要作用,分发和处理消息,实现将一个任务切换到指定的线程中执行。
为什么使用Handler?
子线程是无法访问UI的。如果允许子线程访问UI且存在并发的情况,会使得控件处于混乱的状态。如果采用加锁来解决并发问题,又会使UI访问的效率降低。因此Android采用单线程模型操作UI,通过handler切换到UI线程,解决子线程无法访问UI的问题。
理解handler,Message,MessageQueue及Looper四者关系,是搞懂Android消息机制的关键。
Message
Message是消息机制中的媒介,包含了描述和任意数据对象,用于发送给handler。handler.postXXX(Runnable r)底层也是将Runnable封装成一个Message,这个Runnable设为Message的callback属性。
public final boolean postDelayed(Runnable r, long delayMillis)
{
return sendMessageDelayed(getPostMessage(r), delayMillis);
}
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}
MessageQueue
MessageQueue是一个单链表,用于存放所有handler发送过来的消息。enqueueMessage( )往队列中插入一条消息,next( )是一个无限循环的方法,用于移除消息,如果队列为空,则阻塞。
Looper
Looper是给线程轮询消息的。线程默认是没有Looper的,需要调用Looper.prepare()创建looper,使用Looper.loop()开启消息循环,将next()获取的消息通msg.target.dispatchMessage(msg)回调给handler。如果线程中没有创建Looper,是无法使用handler的。由于Android程序在其入口函数ActivityThread$main(),调用了 Looper.prepareMainLooper(),因此我们可以在主线程中直接使用Handler。
一个线程中只能有一个MessageQueue和Looper,采用ThreadLocal进行保存
Handler
Looper把消息回调到handler的dispatchMessage中进行消息处理:
- 若该消息有callback,即通过Post(Runnable)的方式投递消息,因为在投递runnable时,把runnable对象赋值给了message的callback。
- 若handler的mCallback不为空,则交由通过callback创建handler方式去处理。
- 否则,由最常见创建handler对象的方式,在重写handlerMessage中处理。
子线程中使用Handler
遵循三个步骤
- 创建Looper
- 调用Looper.loop()
- 创建Handler
public class ChildThreadHandlerActivity extends Activity {
private MyThread childThread;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_handler);
childThread = new MyThread();
childThread.start();
Handler childHandler = new Handler(childThread.childLooper){//这样之后,childHandler和childLooper就关联起来了。
public void handleMessage(Message msg) {
};
};
}
private class MyThread extends Thread{
public Looper childLooper;
@Override
public void run() {
Looper.prepare();//创建与当前线程相关的Looper
childLooper = Looper.myLooper();//获取当前线程的Looper对象
Looper.loop();//调用此方法,消息才会循环处理
}
}
}
创建子线程的handler, Handler childHandler = new Handler(childThread.childLooper)后,就可以使用这个childHandler发送消息到子线程中执行。上述代码中由于多线程,childThread.childLooper可能回报空指针错误。
Android提供HandlerThread,简化子线程使用Handler的代码。HandlerThread的run方法中创建Looper,并开启消息循环loop(),
@Override
public void run() {
mTid = Process.myTid();
Looper.prepare();
synchronized (this) {
mLooper = Looper.myLooper();
notifyAll();
}
Process.setThreadPriority(mPriority);
onLooperPrepared();
Looper.loop();
mTid = -1;
}
public Looper getLooper() {
if (!isAlive()) {
return null;
}
// If the thread has been started, wait until the looper has been created.
synchronized (this) {
while (isAlive() && mLooper == null) {
try {
wait();
} catch (InterruptedException e) {
}
}
}
return mLooper;
}
getLooper()中使用wait,以及run()中使用nitifyAll(),解决getLooper()可能为空的问题。
HandlerThread应用示例
public class HandlerThreadActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_handler);
TextView textView = (TextView) findViewById(R.id.tv);
textView.setText("HandlerThreadActivity.class");
HandlerThread handlerThread = new HandlerThread("HandlerThread");
handlerThread.start();
Handler mHandler = new Handler(handlerThread.getLooper()){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
Log.d("HandlerThreadActivity.class","uiThread2------"+Thread.currentThread());//子线程
}
};
Log.d("HandlerThreadActivity.class","uiThread1------"+Thread.currentThread());//主线程
mHandler.sendEmptyMessage(1);
}
}
子线程如何切换到UI线程
创建主线程的handler,并发送消息
new Handler(getMainLooper()).post(new Runnable() {
@Override
public void run() {
// 将在UI线程执行下句
Toast.makeText(getApplicationContext(), "Loaded Person from broadcast-receiver->intent-service: " + info, Toast.LENGTH_LONG).show();
}
});
Activity的runOnUiThread和View.postDelayed()内部也是使用handler切换到UI线程执行自己的Runnable。
public final void runOnUiThread(Runnable action) {
if (Thread.currentThread() != mUiThread) {
mHandler.post(action);
} else {
action.run();
}
}
public boolean postDelayed(Runnable action, long delayMillis) {
final AttachInfo attachInfo = mAttachInfo;
if (attachInfo != null) {
return attachInfo.mHandler.postDelayed(action, delayMillis);
}
// Assume that post will succeed later
ViewRootImpl.getRunQueue().postDelayed(action, delayMillis);
return true;
}
Handler要注意的坑
- 使用非静态内部类声明Handler,会造成内存泄漏,推荐静态内部类+弱引用。
- onDestory中移除队列中的消息,handler.removeCallbacksAndMessages(null)。
al-and-quick-reference