PS:因为感冒缘由,本篇写的有点没有主干,你们凑合看吧。。异步
学习内容:ide
1.MessageQueue,Looper,MessageQueue的做用.oop
2.子线程向主线程中发送消息post
3.主线程向子线程中发送消息学习
异步消息处理机制是Android提供给咱们异步更新UI的一种很好的方式,线程之间以Handler做为桥梁,使得Message能够在线程间进行传递,从而实现异步的一种方式。ui
1.MessageQueuethis
MessageQueue顾名思义,指的就是消息队列,说这个以前咱们首先须要知道什么是Message,好比说咱们在UI界面时点击一个按钮,或者是接收到了一条广播,其实都算是一条Message,这些事件都被封装成一条Message被添加到了MessageQueue队列当中,由于咱们知道一个线程在一段时间只能对一种操做进行相关的处理,所以这些消息的处理就要有前后顺序,所以采用MessageQueue来管理,也就是消息队列。消息队列其实就是一堆须要处理的Message而造成的传送带。一旦有消息发送进来,那么直接执行enqueueMessage()方法。也就是将消息压入到队列当中,一旦线程空闲下来,那么直接从MessageQueue中取出消息,使得消息出队。spa
2.Looper线程
Looper的主要做用是与当前线程造成一种绑定的关系,同时建立一个MessageQueue,这样保证一个线程只能持有一个Looper和MessageQueue同时Looper使得MessageQueue循环起来,就比如水车和水同样,MessageQueue比如水车,当他有了Looper的时候,那么水车会随着水去转动也就是说Looper为MessageQueue提供了活力,使其循环起来,循环的动力每每就少不了Thread。通常而言子线程通常是没有MessageQueue,所以为了使线程和消息队列可以关联那么就须要有Looper来完成了,所以咱们能够这样去实现3d
class ChildRunnable implements Runnable{ @Override public void run() { Looper.prepare(); handler = new Handler(){ @Override public void handleMessage(Message msg) { Log.e("TAG",msg.obj+""); } }; Log.e("TAG_2",Looper.myLooper().toString()); Looper.loop(); } }
这里调用了Looper的prepare()和loop()方法。
public static final void prepare() { if (sThreadLocal.get() != null) { throw new RuntimeException("Only one Looper may be created per thread"); } sThreadLocal.set(new Looper(true)); }
方法比较的简单,咱们能够看到prepare()方法首先判断了sThreadLocal中持有的线程引用是否为空,若是不为空,那么直接就会抛异常,这也就说明了Looper.prepare()在一个线程中只容许调用一次,这样也一样为一个线程对应一个Looper作了保障。当Looper.prepare()执行完毕以后Looper才能够执行loop()方法。
public static void loop() { /** * 获取当前线程绑定的Looper */ final Looper me = myLooper(); if (me == null) { throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread."); } /** * 当前线程的MessageQueue */ 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(); /** * 死循环,循环从MessageQueue取出消息. */ for (;;) { /** * 从Queue中取出一条消息 */ Message msg = queue.next(); // might block if (msg == null) { // No message indicates that the message queue is quitting. return; } // This must be in a local variable, in case a UI event sets the logger Printer logging = me.mLogging; if (logging != null) { logging.println(">>>>> Dispatching to " + msg.target + " " + msg.callback + ": " + msg.what); } /** * 将消息分发出去 */ msg.target.dispatchMessage(msg); if (logging != null) { logging.println("<<<<< Finished to " + msg.target + " " + msg.callback); } // Make sure that during the course of dispatching the // identity of the thread wasn't corrupted. final long newIdent = Binder.clearCallingIdentity(); if (ident != newIdent) { Log.wtf(TAG, "Thread identity changed from 0x" + Long.toHexString(ident) + " to 0x" + Long.toHexString(newIdent) + " while dispatching to " + msg.target.getClass().getName() + " " + msg.callback + " what=" + msg.what); } /** * 将消息回收 */ msg.recycle(); } }
这里咱们能够看到loop()是经过几个流程使得MessageQueue循环起来的,首先经过静态方法获取当前线程对应的Looper,而后获取由Looper建立的MessageQueue,而后进入死循环,阻塞式的从MessageQueue中取出消息,获取到消息以后将消息分发出去,不然进入等待状态,等待新的消息到来。这就是Looper使MessageQueue循环起来的方式。
那么咱们如今MessageQueue消息队列已经存在了,Looper以线程为动力使得MessageQueue循环了起来,那么消息的发送和处理由谁来完成呢显而易见这就是Handler的做用了.
3.Handler
Handler的出现使得消息能够被发送和接收,Handler的构造方法有多个。
public Handler() { } public Handler(Handler.Callback callback) { } public Handler(Looper looper) { } public Handler(Looper looper, Handler.Callback callback) { }
这是Handler的四个构造方法,1和2都没有传递Looper,那么Handler经过调用Looper.myLooper()方法将Handler与Looper和MessageQueue造成绑定关系,而3和4直接传递Looper,那么Handler将直接将传递进来的Looper对象进行保存,直接和传递的Looper以及相关的MessageQueue造成绑定关系。同时这四个构造方法还有不一样点,就是是否传递Callback回调接口对应的参数。
public interface Callback { public boolean handleMessage(Message msg); }
实现Callback接口是实现处理Message的一种方式,而另外一种方式则不传递Callback回调接口,而是直接实现Handler中handleMessage方法,也就是说咱们能够经过这两种方式实现Handler对消息的处理。这样三者就造成了绑定关系,而后咱们来看看Handler的sendMessage等方法.其实不管是调用了哪一种方法,sendMessage(),sendMessageDelayed()等等,最后都会调用sendMessageAtTime()方法。这里须要先说一下post方法和sendMessage方法。通常咱们使用Handler发送消息的时候也会这样去写。
handler.post(new Runnable() { @Override public void run() { /** * 相关操做 * */ } });
这样发送消息也是能够的,post执行过程是这样的。
public final boolean post(Runnable r) { return sendMessageDelayed(getPostMessage(r), 0); } private static Message getPostMessage(Runnable r) { Message m = Message.obtain(); m.callback = r; return m; }
post方法也是发送了一条消息,runnable则做为callback参数做为回调,用于后续处理消息。这里也调用了sendMessageDelayed()方法,最后仍是会调用sendMessageAtTime()方法,所以能够看出,不管是send仍是post,最后都会调用sendMessageAtTime()方法。
public boolean sendMessageAtTime(Message msg, long uptimeMillis) { MessageQueue queue = mQueue; return enqueueMessage(queue, msg, uptimeMillis); }
sendMessageAtTime()是最终调用的方法,他经过调用enqueueMessage将消息加入到消息队列当中。
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) { //注意下面这行代码 msg.target = this; if (mAsynchronous) { msg.setAsynchronous(true); } //注意下面这行代码 return queue.enqueueMessage(msg, uptimeMillis); }
enqueueMessage方法比较的简单,这里讲msg.target = this,将msg的target赋值为当前的handler,而后调用queue.enqueueMessage()方法将消息加入到消息队列当中。那么SendMessage以后,Looper就经过loop()方法不断的从MessageQueue取出消息,而后经过dispatchMessage()方法将消息分发给Handler,交给Handler去处理相关的Message.
public void dispatchMessage(Message msg) { if (msg.callback != null) { handleCallback(msg); } else { if (mCallback != null) { if (mCallback.handleMessage(msg)) { return; } } handleMessage(msg); } }
这样咱们就能够直接去处理相关的消息了,这个方法作了一些判断,也就是判断是否传递了callback属性,若是msg.callback不为空,那么则表示是post发送过来的消息,在处理的时候就须要调用handleCallback(msg)方法,而后咱们会继续判断Handler的Callback是否为空,也就是咱们在new Handler(Handler.Callback)方法去建立Handler的时候的判断,若是不为空,那么执行咱们Callback中实现的handleMessage()方法,若是为空,那么就直接执行Handler中的handleMessage()方法。整体的流程就是这样。在其余的牛人博客中,看到了一幅特别形象的流程图。在这里贴出来。
在现实生活的生产生活中,存在着各类各样的传送带,传送带上面洒满了各类货物,传送带在发动机滚轮的带动下一直在向前滚动,不断有新的货物放置在传送带的一端,货物在传送带的带动下送到另外一端进行收集处理。
咱们能够把传送带上的货物看作是一个个的Message,而承载这些货物的传送带就是装载Message的消息队列MessageQueue。传送带是靠发送机滚轮带动起来转动的,咱们能够把发送机滚轮看作是Looper,而发动机的转动是须要电源的,咱们能够把电源看作是线程Thread,全部的消息循环的一切操做都是基于某个线程的。一切准备就绪,咱们只须要按下电源开关发动机就会转动起来,这个开关就是Looper的loop方法,当咱们按下开关的时候,咱们就至关于执行了Looper的loop方法,此时Looper就会驱动着消息队列循环起来。
那Hanlder在传送带模型中至关于什么呢?咱们能够将Handler看作是放入货物以及取走货物的管道:货物从一端顺着管道划入传送带,货物又从另外一端顺着管道划出传送带。咱们在传送带的一端放入货物的操做就至关于咱们调用了Handler的sendMessageXXX、sendEmptyMessageXXX或postXXX方法,这就把Message对象放入到了消息队列MessageQueue中了。当货物从传送带的另外一端顺着管道划出时,咱们就至关于调用了Hanlder的dispatchMessage方法,在该方法中咱们完成对Message的处理。
2.子线程向主线程发送消息
子线程向主线程发送消息,平时仍是很经常使用的,好比说子线程完成了一个耗时的操做,须要主线程去更新UI,那么这个时候就须要子线程向主线程中发送相关的消息。用起来也比较的简单。
private Handler handler = new Handler(){ //主线程作处理. @Override public void handleMessage(Message msg) { String data = (String) msg.obj; textView.setText("从子线程发送过来的消息"+data); childRunnable = null; } }; class ChildRunnable implements Runnable { //子线程发送相关消息 @Override public void run() { Message message = Message.obtain(); message.obj = "ChildToMain"; handler.sendMessage(message); } }
3.主线程将消息发送给子线程
咱们常常将消息从子线程发送给主线程,其实主线程也是能够发消息给子线程的。在默认状况下子线程是没有Looper和MessageQueue的,所以咱们须要为子线程建立一个Looper而后与子线程的Handler造成绑定关系。
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_handler_main_to_child); Log.e("TAG_0",Looper.getMainLooper().toString()); button = (Button) findViewById(R.id.button); button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { Message message = Message.obtain(); message.obj = "MainToChild"; handler.sendMessage(message); } }); new Thread(new ChildRunnable()).start(); } class ChildRunnable implements Runnable{ @Override public void run() { Looper.prepare(); handler = new Handler(){ @Override public void handleMessage(Message msg) { Log.e("TAG",msg.obj+""); } }; Log.e("TAG_2",Looper.myLooper().toString()); Looper.loop(); } }
这里设置了一个按钮的监听事件来控制主线程向子线程发送消息,或者咱们先让主线程休眠,先让子线程初始化完毕以后,主线程再向子线程发送消息,若是直接发送的话,颇有可能在Handler尚未初始化完毕后就致使消息发送,这样就会出现Handler为null从而致使NullPointerExpection发生。所以在这里进行了简单的控制,这里咱们能够看到,若是子线程想拥有本身的Looper和MessageQueue首先须要执行Looper.prepare()和Looper.loop()方法。才能为子线程的Handler绑定上Looper和MessageQueue。有人可能会问道,为何主线程直接发送消息就能够,而不须要调用这两个方法,这是由于Activity在onCreate的时候已经执行了这些方法,所以咱们能够直接发消息给主线程。
所以当咱们在使用Handler Message Looper实现异步消息机制的时候,若是想实现多级通讯,那么就须要弄明白当前的Looper MessageQueue绑定的是哪一个Handler。Looper,MessageQueue在一个线程中只有惟一一个,可是Handler能够是多个的,咱们只须要控制这个绑定关系是实现多级通讯的关键。
最后贴一个简单的源代码:Demo下载