Android组件_Handler Looper Message理解
一、Handler机制概述
Handler机制是Android中一种消息处理机制。
主要组成或重要概念:
- Message,线程间通讯的数据单元。
- Message Queue,消息队列,用来存放Handler发布的消息,按照FIFO执行。
- Handler是Message的主要处理者,负责将Message添加到消息队列意见对消息队列中的Message进行处理。
- Looper循环器,循环去除Message Queue里面的Message,并交付给相应的Handler进行处理。
- Thread UI thread通常就是main thread,Android启动程序时会替它建立一个Message Queue。
每一个线程里可以含有一个Looper对象以及MessageQueue数据结构。 - ThreadLocal 他的作用是帮助Handler获得当前线程的Looper(多个线程可能有多个Looper)
二、使用场景
我们常常用Handler来更新UI,但是不是说Handler就是把用来更新UI的,耗时的I/O操作,读取文件,访问网络等等都是可以在Handler里面操作的。
子线程间通讯,可以在一个子线程中去创建一个Handler,然后使用这个handler实例在任何其他线程中发送消息,最终处理消息的代码都会在你创建Handler实例的线程中运行。
延时任务等
三、使用方法
3.1. 子线程线程里更新UI
public class MainActivity extends AppCompatActivity {
//主线程中的handler
Handler mHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
//获得刚才发送的Message对象,然后在这里进行UI操作
Log.e(TAG,"------------> msg.what = " + msg.what);
...
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//在子线程中发送更新消息给主线程的handler去更新UI
new Thread(new Runnable() {
@Override
public void run() {
Message m = mHandler.obtainMessage();
...
mHandler.sendMessage(m);
}
}).start();
}
}
3.2 子线程间通信
final Handler[] h = new Handler[1];
new Thread(new Runnable() {
@Override
public void run() {
Looper.prepare();//创建子线程的looper,子线程使用handler消息传递,这一步必须要有,因为默认的子线程是没有Looper对象的
h[0] = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
Log.e(TAG,"------------> msg.what = " + msg.what);
}
};
Looper.loop();//消息池消息循环处理
}
}, "work1").start();
new Thread(new Runnable() {
@Override
public void run() {
Message msg = h[0].obtainMessage();
//msg.sendToTarget();
h[0].sendEmptyMessage(0);//在work2子线程中使用持有work1子线程looper的handler发送消息至work1中去做消息处理
}
}, "work2").start();
3.3 HandlerThread使用
{
MyHandlerThread mHandlerThread = new MyHandlerThread("work");
Handler mHandler = new Handler(mHandlerThread.getLooper()){//将该mHandler与HandlerThread对象的Looper关联起来
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
}
};
}
MyHandlerThread mHandlerThread = new MyHandlerThread("work");
Handler mHandler = new Handler(mHandlerThread.getLooper()){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
}
};
}
3.4 Handler CallBack参数使用
在构造Handler对象的时候可以穿入CallBack参数,如下
//主线程:
Handler handler = new Handler(new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
return false;
}
});
//其他线程中:
handler.sendMessageXXX(msg);
3.5 Handler post方法使用
//主线程中创建mPostHandler
Handler mPostHandler = new Handler();
//子线程中使用post方法
new Thread(new Runnable() {
@Override
public void run() {
/* Message m = mHandler.obtainMessage();
mHandler.sendMessage(m);*/
mPostHandler.post(new Runnable() {
@Override
public void run() {
//更新UI ,注意这里虽然操作写在子线程中,事实运行时是在主线程中,原因分析在4.2.5中
}
});
}
}).start();
3.6 Handler 延时任务/循环定时操作
延时任务
new Handler().postDelayed(new Runnable(){
public void run() {
//show dialog
}
}, 5000); //延时5秒后执行runnable run方法
循环定时操作:
1,首先创建一个Handler对象
Handler handler=new Handler();
2,然后创建一个Runnable对
Runnable runnable=new Runnable(){
@Override
public void run() {
// TODO Auto-generated method stub
//要做的事情,这里再次调用此Runnable对象,以实现每两秒实现一次的定时器操作
handler.postDelayed(this, 2000);
}
};
3,使用PostDelayed方法,两秒后调用此Runnable对象
handler.postDelayed(runnable, 2000);
4,如果想要关闭此定时器,可以这样操作
handler.removeCallbacks(runnable);
四、原理分析
Handler机制的铁三角-Handler、Looper和MessageQueue,另外还有下面进行浅入分析。
整体架构 :
4.1 主线程Handler对象与主线程Looper对象关联
对3.1中的例子进行浅入的分析。
4.1.1 Handler对象初始化获取主线程的Looper对象
1.主线程Handler对象初始化:
Handler mHandler = new Handler(){...}
默认的构造函数会将该Handler对象与当前线程的Looper对象关联。下面我们看下构造函数是如何关联上当前线程的looper的。
Handler的默认构造函数:
frameworks/base/core/java/android/os/Handler.java
public Handler() {
this(callback:null, async:false);
}
2.如果该线程没有looper对象,该handler将抛异常:
public Handler(Callback callback, boolean async) {
...
mLooper = Looper.myLooper();//
if (mLooper == null) {//如果当前线程没有Looper对象将抛出异常
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
3.sThreadLocal对象中保存着当前线程的looper对象,Looper.myLooper()即用来获取当前线程的Looper对象:
frameworks/base/core/java/android/os/Looper.java
public static @Nullable Looper myLooper() {
return sThreadLocal.get();//这里获取当前线程的Looper对象
}
4.1.2 主线程初始化时创建Looper对象及MessageQueue对象
1.对于UI线程即我们所说的Main线程及ActivityThread,在ActivityThread创建的时候,main函数中创建了Looper对象,并通过Looper.loop()是主线程进入消息循环中:
frameworks/base/core/java/android/app/ActivityThread.java
public static void main(String[] args) {
...
Looper.prepareMainLooper(); //创建主线程Looper对象
ActivityThread thread = new ActivityThread();
thread.attach(false);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
...
Looper.loop();//不断循环处理MessageQueue中的消息
throw new RuntimeException("Main thread loop unexpectedly exited");
}
2.Looper.prepareMainLooper()创建主线程的Looper以及MessageQueue,并通过Looper.loop()来开启主线程的消息循环:
frameworks/base/core/java/android/os/Looper.java
public static void prepareMainLooper() {
prepare(false);//创建Looper对象,并且将新建的looper对象与当前线程关联
synchronized (Looper.class) {
if (sMainLooper != null) { //可以看出主线程应该只有一个Looper对象
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();//返回当前线程的Looper对象
}
}
3.Looper.prepare()函数为当前线程(此处为主线程,也可以是其他线程)创建Looper对象,并将该对象设置至线程的sThreadLocal变量中:
private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) { // 说明prepare不能调用两次,否则会抛出异常,保证一个线程只有一个Looper实例
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper(quitAllowed));//sThreadLocal是一个ThreadLocal对象,可以在一个线程中存储变量。
}
4.Looper.myLooper()返回当前线程的Looper对象,这里也就验证了myLooper()的确是从sThreadLocal变量中获取了当前线程的Looper对象。
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
概述一下就是,主线程在初始化时就生成了含有一个不可退出的MessageQueue的Looper对象,并将该Looper对象保存在主线程的sThreadLocal变量中;
当在主线程中创建Handler对象时,会将该Handler对象与当前线程的sThreadLocal变量中保存的主线程Looper对象关联起来。
因此,在上面3.1的例子中,主线程new一个Handler对象时会将该Handler对象与主线程中的Looper对象关联,执行Looper.loop()不断循环处理消息;
4.2 Handler、Looper、Message及HandlerThread浅析
4.2.1 Handler
1.可以使用的方法
post(Runnable)
postAtTime(Runnable,long)
postDelayed(Runnable long)
sendEmptyMessage(int)
sendMessage(Message)
sendMessageDelayed(Message,long)
sendMessageAtTime(Message,long)
2.以上方法都最终都将调用sendMessageAtTime(),将消息加入msg的目标Handler对象关联的Looper持有的消息队列中,之后就是排队消息队列循环处理消息了:
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
MessageQueue queue = mQueue;
if (queue == null) {
RuntimeException e = new RuntimeException(
this + " sendMessageAtTime() called with no mQueue");
Log.w("Looper", e.getMessage(), e);
return false;
}
return enqueueMessage(queue, msg, uptimeMillis); //将该消息加入该消息的目标handler关联的looper中的消息队列中
}
4.2.2 Looper:
1.Looper构造函数,看到Looper的构造函数里新建了一个MessageQueue对象:
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);//创建消息队列
mThread = Thread.currentThread();
}
2.Looper.loop(),循环处理消息队列中消息的函数:
/**
* Run the message queue in this thread. Be sure to call
* {@link #quit()} to end the loop.
*/
public static void loop() {
...
// 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 (;;) {
Message msg = queue.next(); // might block,若无消息则会阻塞在这里。
...
try {
msg.target.dispatchMessage(msg); //调用发该message的handler的dispatchMessage方法;
} finally {
}
...
msg.recycleUnchecked();//处理完的msg可以进行回收,实现的地方清除了该msg对象的内容,并将其保留在消息池中,以供循环使用
}
}
3.在dispatchMessage中进行消息处理,post方法中的runnable就是这里的msg.callback
frameworks/base/core/java/android/os/Handler.java
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg); //执行post方法中的runnable方法
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) { //如果创建handler时有传入callback的话
return;
}
}
handleMessage(msg);//创建handler时需复写方法handleMessage(),处理消息
}
}
3.1首先会判断msg.callback存不存在,msg.callback是Runnable类型,如果msg.callback存在,那么说明该Message是通过执行Handler的postXXX系列方法将Message放入到消息队列中的,这种情况下会执行handleCallback(msg), handleCallback源码如下:
private static void handleCallback(Message message) {
message.callback.run();
}
这样我们我们就清楚地看到我们执行了msg.callback的run方法,也就是执行了postXXX所传递的Runnable对象的run方法。
3.2.如果我们不是通过postXXX系列方法将Message放入到消息队列中的,那么msg.callback就是null,代码继续往下执行,接着我们会判断Handler的成员字段mCallback存不存在。mCallback是Hanlder.Callback类型的,我们在上面提到过,在Handler的构造函数中我们可以传递Hanlder.Callback类型的对象,该对象需要实现handleMessage方法,如果我们在构造函数中传递了该Callback对象,那么我们就会让Callback的handleMessage方法来处理Message。
3.3.如果我们在构造函数中没有传入Callback类型的对象,那么mCallback就为null,那么我们会调用Handler自身的hanldeMessage方法,该方法默认是个空方法,我们需要自己是重写实现该方法。
综上,我们可以看到Handler提供了三种途径处理Message,而且处理有前后优先级之分:首先尝试让postXXX中传递的Runnable执行,其次尝试让Handler构造函数中传入的Callback的handleMessage方法处理,最后才是让Handler自身的handleMessage方法处理Message。
4.2.3 MessageQueue
1 MessageQueue的next()方法
在消息队列中不断取出消息,next方法是个无限循环的方法,如果有消息返回这条消息并从链表中移除,而没有消息则一直阻塞在这里。
Message next() {
for (;;) {
synchronized (this) {
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
if (msg != null && msg.target == null) {
// Stalled by a barrier. Find the next asynchronous message in the queue.
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}
if (msg != null) {
...
// Got a message.
mBlocked = false;
if (prevMsg != null) {
prevMsg.next = msg.next;
} else {
mMessages = msg.next;
}
msg.next = null;
msg.markInUse();
return msg;
}
}
}
}
4.2.4 HandlerThread
1.自带Looper的Thread:
frameworks/base/core/java/android/os/HandlerThread.java
public class HandlerThread extends Thread {
@Override
public void run() {
...
Looper.prepare();
synchronized (this) {
mLooper = Looper.myLooper();
notifyAll();
}
onLooperPrepared();
Looper.loop();
...
}
}
我们看到HandlerThread继承自Thread,并且在run函数中使用Looper.prepare()为使用线程创建了一个Looper对象,并且把该对象放到了该线程范围内的变量中(sThreadLocal);在Looper对象的构造过程中,初始化了一个MessageQueue,作为该Looper对象成员变量;loop()开启了,不断的循环从MessageQueue中取消息处理了,当没有消息的时候会阻塞,有消息的到来的时候会唤醒。
因此也可以使用HandlerThread,这样可以不用像普通线程那样需要Looper.prepare()和Looper.loop(),因为HandlerThread为我们做好了准备工作;
2.另外可以通过HandlerThread的getLooper方法获的该HandlerThread的Looper对象:
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;
}
handleMessage
4.2.5 Handler中post方法
3.5中的例子中子线程中post中的run方法运行在主线程中,原因分析如下:
1.传入Runnable对象
public final boolean post(Runnable r)
{
return sendMessageDelayed(getPostMessage(r), 0);
}
2.这里将Runnable封装到一个Message对象中
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r; //将runnable传入message对象中的callback变量中
return m;
}
3.之后与普通的handler sendMessage方法流程一致,调用用sendMessageAtTime,将该消息加入该消息的目标handler,及mPostHandler关联的looper中的消息队列中,如何处理在4.2.1 中有提到过。
public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
也就是说,虽然runnable复写方法在子线程中,但是同样是封装成message对象传入主线程的looper持有的MessageQueue对象中,在主线程中对封装了runnable变量的message进行消息执行处理,因此post中的run方法是运行在主线程中的。
五、注意事项
- Handler在处理消息需要严格区分是否是在UI线程中,Handler一般用在非UI线程中来传递消息,在非UI线程中使用Handler来发送消息,消息
处理会被严格执行,但如果在UI线程中使用Handler来发送消息,相同的消息在内部会被合并,且执行时序也得不到保证 - 因为HandlerThread拥有自己的消息队列,它不会干扰或阻塞UI线程,比较合适处理那些需要花费时间偏长的任务。我们只需要把任务发送给HandlerThread,然后就只需要等待任务执行结束的时候通知返回到主线程就好了。
- android中handler使用应该注意的由handler引起的OOM内存泄漏) http://blog.csdn.net/javazejian/article/details/50839443
- 在普通线程中使用机制时要记得先Looper.prepare(); 最后还要Looper.loop();