本文主要是介绍Android组件Handler Looper Message理解,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
Android组件_Handler Looper Message理解
一、Handler机制概述
Handler机制是Android中一种消息处理机制。
主要组成或重要概念:
1. Message,线程间通讯的数据单元。
2. Message Queue,消息队列,用来存放Handler发布的消息,按照FIFO执行。
3. Handler是Message的主要处理者,负责将Message添加到消息队列意见对消息队列中的Message进行处理。
4. Looper循环器,循环去除Message Queue里面的Message,并交付给相应的Handler进行处理。
5. Thread UI thread通常就是main thread,Android启动程序时会替它建立一个Message Queue。
每一个线程里可以含有一个Looper对象以及MessageQueue数据结构。
6. ThreadLocal 他的作用是帮助Handler获得当前线程的Looper(多个线程可能有多个Looper)
二、使用场景
我们常常用Handler来更新UI,但是不是说Handler就是把用来更新UI的,耗时的I/O操作,读取文件,访问网络等等都是可以在Handler里面操作的。
子线程间通讯,可以在一个子线程中去创建一个Handler,然后使用这个handler实例在任何其他线程中发送消息,最终处理消息的代码都会在你创建Handler实例的线程中运行。
延时任务等
三、使用方法
3.1. 子线程线程里更新UI
public class MainActivity extends AppCompatActivity {//主线程中的handlerHandler mHandler = new Handler(){@Overridepublic void handleMessage(Message msg) {super.handleMessage(msg);//获得刚才发送的Message对象,然后在这里进行UI操作Log.e(TAG,"------------> msg.what = " + msg.what);...}};@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);//在子线程中发送更新消息给主线程的handler去更新UInew Thread(new Runnable() {@Overridepublic void run() {Message m = mHandler.obtainMessage();...mHandler.sendMessage(m);}}).start();}
}
3.2 子线程间通信
final Handler[] h = new Handler[1];new Thread(new Runnable() {@Overridepublic void run() {Looper.prepare();//创建子线程的looper,子线程使用handler消息传递,这一步必须要有,因为默认的子线程是没有Looper对象的h[0] = new Handler() {@Overridepublic void handleMessage(Message msg) {super.handleMessage(msg);Log.e(TAG,"------------> msg.what = " + msg.what);}};Looper.loop();//消息池消息循环处理}}, "work1").start();new Thread(new Runnable() {@Overridepublic 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关联起来@Overridepublic void handleMessage(Message msg) {super.handleMessage(msg);}};}MyHandlerThread mHandlerThread = new MyHandlerThread("work");Handler mHandler = new Handler(mHandlerThread.getLooper()){@Overridepublic void handleMessage(Message msg) {super.handleMessage(msg);}};}
3.4 Handler CallBack参数使用
在构造Handler对象的时候可以穿入CallBack参数,如下
//主线程:Handler handler = new Handler(new Handler.Callback() {@Overridepublic boolean handleMessage(Message msg) {return false;}});
//其他线程中:
handler.sendMessageXXX(msg);
3.5 Handler post方法使用
//主线程中创建mPostHandlerHandler mPostHandler = new Handler();//子线程中使用post方法new Thread(new Runnable() {@Overridepublic void run() {/* Message m = mHandler.obtainMessage();mHandler.sendMessage(m);*/mPostHandler.post(new Runnable() {@Overridepublic 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.javapublic 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.javapublic 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.javapublic 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.javapublic 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.javapublic 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 {
@Overridepublic 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();
六、案例分析
以后补上
这篇关于Android组件Handler Looper Message理解的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!