Handler建立的初衷是为了解决子线程更新UI的问题:当Android程序第一次启动时,会建立一条(仅一条)UI线程,用于处理与UI相关的事件(就是咱们的Activity);而为了保证线程安全性,规定只有UI线程能够修改Activiy中的组件。这就带来了一个问题,Andorid程序在很大程度上都依赖于UI组件(各线程的处理结果大多都以更新UI组件来呈现),Handler的消息机制就是用于将子线程的结果数据传递给UI线程进行更新执行,而UI线程能够放心地把那些耗时的操做放到子线程中,本身管好本身的UI就好了。固然,Handler没有规定只用于更新UI,你彻底能够当他是一个异步的消息处理(在一个线程中发送消息,在另外一个线程中处理接收到的消息),在使用时,你只要遵循它所制定的规则便可。若是有人问为何要用子线程处理耗时任务,那什么ANR网上有不少讲解。java
既然是消息机制,首先来了解下消息的结构,它由Handler对象进行发送和处理,包含标识信息和任意的数据对象:android
public final class Message implements Parcelable { //消息标识,用户自定义,用于收发双方肯定对方身份 public int what; //用于存放简单的整数数据,效率较高 public int arg1; //同上 public int arg2; //Bundle结构(封装的ArrayMap),用于存储数据信息 Bundle data; //控制消息发送、处理,后面详细分析 public void setData(Bundle data) { this.data = data; } public Bundle getData() { if (data == null) { data = new Bundle(); } return data; } //处理消息的对象,即Handler Handler target; //具体的消息处理,优先级高于Handler的handleMessage,后面详细分析 Runnable callback; //任意类型的对象数据,当使用Messenger进行进程间消息传递时,常常用来存放Parcelable(序列化对象) public Object obj; //基于Message的进程间通讯管理器,将Handler做为接收者(指定发送的消息由那个Handler处理) public Messenger replyTo; //标识消息是否在使用,用于消息池的回收再用控制 static final int FLAG_IN_USE = 1 << 0; //指向消息池中可用的消息 private static Message sPool; //替代构造函数,从消息池中获取可用的消息(避免直接构造时的空间申请),当消息池中没有可用时才调用构造 //一些重载函数(带Message、Handler、what等参数)再也不复述 public static Message obtain() { synchronized (sPoolSync) { if (sPool != null) { Message m = sPool; sPool = m.next; m.next = null; m.flags = 0; // clear in-use flag sPoolSize--; return m; } } return new Message(); } }
能够简单地进行归纳下:what标识消息;arg一、arg二、data和obj存储数据;target和callback控制消息的处理;obtain建立消息(固然经常使用的还有Handler.obtainMessage(),后面会提到);replyTo用于进程间通讯(不在本文探讨范围)。安全
要讲解Handler,就不能不说他的死党Looper。Looper能够说是Handler处理消息的控制者,而且拥有很是重要的属性——存储待处理消息的消息队列MessageQueue。每个Looper对象在构造之初便会初始化一个消息队列(一一对应),用于存放它所控制的那些Handler在其余线程所发送过来的消息。首先看一下Looper的属性和构造:多线程
public final class Looper { final MessageQueue mQueue; //消息队列 final Thread mThread; //当前线程标识 //构造函数,将当前线程构建为Looper线程(初始化线程对应的消息队列,固然还有初始化线程私有属性,后面为讲到) private Looper(boolean quitAllowed) { mQueue = new MessageQueue(quitAllowed); mThread = Thread.currentThread(); } }
不对啊,这是要搞事情啊,构造函数怎么是私有的呢?由于它不想让你用这个构造,这里涉及到多线程,Java对多线程的处理大多都给出了重载函数,一方面保证单线程使用的性能,另外一方面保证多线程使用时数据的准确性(同步与异步),因此这里也给出了相应的封装函数,看一下具体处理:app
public final class Looper { //ThreadLocal:线程私有属性,不受其余线程的影响,用于存储与同进程中其余线程不一样的那些私有数据(带有存储私有数据的hash map),这里主要用于构造控制(见prepare) static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>(); //Looper构造与初始化函数 public static void prepare() { prepare(true); } private static void prepare(boolean quitAllowed) { //构造Looper对象时初始化其私有属性,经过私有属性保证线程只建立一次(一个线程中prepare只被调用一次) if (sThreadLocal.get() != null) { throw new RuntimeException("Only one Looper may be created per thread"); } sThreadLocal.set(new Looper(quitAllowed)); } }
哟西,这样咱们在一个线程中调用Looper.prepare(),便可将当前线程构建成Looper线程,同时初始化了Looper线程的消息队列和私有属性。而后看一下Looper具体作了哪些工做(为了便于阅读,只列出重要的代码部分):异步
public static void loop() { //获取当前Looper线程的Looper对象,loop函数在prepare函数以后调用,二者工做在同一线程中 final Looper me = myLooper(); final MessageQueue queue = me.mQueue; for (;;) { //从消息队列中取出一条未处理的消息 Message msg = queue.next(); if (msg == null) { return; } try { //具体的消息处理过程,而dispatchMessage这个函数是Handler的函数,下面分析Handler时会详细讲述 //这里的处理也说明了一点:loop函数是在处理线程中调用的 msg.target.dispatchMessage(msg); } //回收处理完的Message资源(将Message的各项值还原成初始化值,而后丢到消息池中) msg.recycleUnchecked(); } }
上面还有一个问题没有解决,那就是msg.target这个对象是什么。毫无疑问,他是Handler的对象,由于dispatchMessage是Handler类的函数,而target在这里的做用就是将发送和处理进行关联(在发送线程中,将Handler做为Message的target属性与Message进行绑定,在处理线程中,经过Message的target对Message进行处理)。这样就经过属性target将发送消息和处理消息联系起来了,即哪一个Handler对象发送消息,就由哪一个Handler进行处理。async
消息的构造方法有不少,如Message的obtain,Handler的obtainMessage,固然也有人就喜欢直接使用Message的构造函数。Handler的obtainMessage和Message的包含Handler参数的obtain直接在构造时就肯定了他的target,有些则是等到最终的统一确认阶段——发送消息时进行target赋值(Handler的sendMessage),下面给出在构造时肯定target的状况,发送消息时的统一确认在讲述Handler时会提到:ide
public class Handler { public final Message obtainMessage() { return Message.obtain(this); } } public final class Message implements Parcelable { public static Message obtain(Handler h) { Message m = obtain(); m.target = h; return m; } }
讲到这,下面看一下Looper如何使用,提供一个最简单的例子:函数
public class TestLooper extends Thread { //声明Handler对象,供发送线程使用 public Handler mHandler; public Handler mHandlernew; @Override //必须在线程中,Looper是将寻常线程初始化为Looper线程,且不能在UI线程中 public void run() { //初始化,将TestLooper转化成Looper线程 Looper.prepare(); //定义Handler对象,即将Handler对象与Looper绑定,使得消息队列中的Message经过其target能够找到正确的Handler进行处理,能够定义多个不一样Handler类的对象。固然,若是你在一个Looper下定义同一个Handler类的多个对象,我也布吉岛结果是啥 Handler mHandler = new MyHandler(){ @Override public void handleMessage(Message msg) {...} }; Handler mHandlernew = new MyHandlernew(){......} //开始循环处理消息 Looper.loop(); } }
上面的TestLooper类中,咱们首先构造了一个Looper线程TestLooper;随后定义了两个Handler对象,并重写了他们的handleMessage函数(必须);最后调用loop开始等待、处理消息。在使用时,只需定义TestLooper的对象,调用其start方法便可启动线程,以后能够手动调用quit或发送信号量结束该Looper线程,到这里Looper的工做就完成了(UI线程中直接定义Handler对象便可)。在发送线程中,经过Handler对象的引用(Handler对象做为参数传递给发送线程)或将Handler做为Looper对象的属性(在发送线程中定义处理线程的对象,经过对象调用其属性Handler)等方法发送消息,在结束Handler讲解后会有使用的代码,这里给出简易的图示说明Looper的功能:oop
不能在UI线程中的缘由:UI线程在建立时就已经设定为是一个Looper线程,若是在UI线程中调用Looper.prepare(),就至关于屡次建立会抛出异常,下面简单的看下对UI线程在Looper方面的控制
//控制应用程序的主线程执行和调度 public final class ActivityThread { public static void main(String[] args) { Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain"); SamplingProfilerIntegration.start(); ........ Looper.prepareMainLooper(); ActivityThread thread = new ActivityThread(); thread.attach(false); if (sMainThreadHandler == null) { sMainThreadHandler = thread.getHandler(); } Looper.loop(); throw new RuntimeException("Main thread loop unexpectedly exited"); } } public final class Looper { //用于将当前线程建立为Looper线程,且标识为应用的main Looper,这个函数由Android环境所调用 public static void prepareMainLooper() { prepare(false); synchronized (Looper.class) { if (sMainLooper != null) { throw new IllegalStateException("The main Looper has already been prepared."); } sMainLooper = myLooper(); } } }
Looper还有一些获取当前Looper属性的函数,这里一并给出:
public final class Looper { //获取Looper对象对应的线程标识 public @NonNull Thread getThread() { return mThread; } //获取当前Looper线程的Looper对象 public static @Nullable Looper myLooper() { return sThreadLocal.get(); } //获取Looper对象对应的消息队列 public @NonNull MessageQueue getQueue() { return mQueue; } //结束此Looper public void quit() { mQueue.quit(false); } }
终于轮到Handler了,Handler提供了两个功能:1.将打有本身标识的消息(包括可运行过程)发送给另外一个线程 2.根据打有本身标识的消息在对方线程中执行操做。
能够打个不是很恰当的比喻,Handler比如是一个外卖餐点,他提供两个功能:1.点餐,不论是美团的仍是大众点评的,只要客户是经过我家点餐页面点的外卖(特定Handler对象发送消息),那都把这些消息发送给我(Handler绑定的MessageQueue);2.送餐(具体操做),这是餐点在线下(相对于美团这种发送线程,线下就至关因而处理线程,而餐点就是在该线程定义的Handler)执行的,固然有可能会有一些矫情的(我要身高一米八,皮肤白皙笑起来还很阳光的帅哥送,这就是Runnable,由发送的Message决定具体操做,固然执行者仍是餐点)
首先来看一下Handler的属性和构造函数:
public class Handler { final Looper mLooper; final MessageQueue ; final Callback mCallback; //不带Looper参数的构造最终走的函数,没有的参数用null、false代替 public Handler(Callback callback, boolean async) { //获取当前Looper线程的Looper对象。咱们在使用的时候,Handler的构造函数都是在消息处理线程中执行(定义Handler对象,将Handler对象与Looper线程、消息队列进行绑定),因此这里获取的是消息处理线程的Looper对象,而消息发送线程通常是经过参数传递得到Handler对象的引用,经过此引用来发送消息 mLooper = Looper.myLooper(); //当前线程不是Looper线程,抛出异常,即Handler必定要在Looper线程中定义 if (mLooper == null) { throw new RuntimeException( "Can't create handler inside thread that has not called Looper.prepare()"); } //关联消息队列 mQueue = mLooper.mQueue; //相似回调机制,在Activity中实现interface Callback的boolean handleMessage(Message msg)函数,利用此callback建立Handler,能够代替定义Handler对象时重写void handleMessage(Message msg),功能相同 mCallback = callback; } //带Looper参数的构造最终走的函数,没有的参数用null、false代替 public Handler(Looper looper, Callback callback, boolean async) { mLooper = looper; mQueue = looper.mQueue; mCallback = callback; mAsynchronous = async; } }
能够看到,Handler的构造作了两件事:1.绑定Looper(和定义Handler对象所在的Looper(处理消息的Looper)进行绑定,同时绑定Looper对应的消息队列),目的:绑定消息队列,这样无论Handler在其余哪一个线程中发送消息,都能保证正确的送达其对应的消息队列 2.若是须要,设置回调函数(代替handleMessage)
下面来看一下他的发送消息,有不少重载函数,sendMessage(Message msg)、sendEmptyMessage(int what)、sendMessageDelayed(Message msg, long delayMillis)、sendEmptyMessageAtTime(int what, long uptimeMillis)等等,他们最终都会调用sendMessageAtTime(Message msg, long uptimeMillis):
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); } private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) { //这就是target的统一赋值,将Handler做为Message的target属性,具体解析见Looper分析 msg.target = this; if (mAsynchronous) { msg.setAsynchronous(true); } //将消息插入到绑定的消息队列中,这样处理线程就能正确的接收他所绑定的Handler发送的消息 return queue.enqueueMessage(msg, uptimeMillis); } boolean enqueueMessage(Message msg, long when) { synchronized (this) { //标识消息正在使用,防止此时消息被再次入队或被回收 msg.markInUse(); msg.when = when; //消息队列的头结点 Message p = mMessages; boolean needWake; //队列为空,当前消息做为头结点 if (p == null || when == 0 || when < p.when) { msg.next = p; mMessages = msg; needWake = mBlocked; } //队列不为空,当前消息插入到队尾 else { needWake = mBlocked && p.target == null && msg.isAsynchronous(); Message prev; for (;;) { prev = p; p = p.next; if (p == null || when < p.when) { break; } if (needWake && p.isAsynchronous()) { needWake = false; } } msg.next = p; prev.next = msg; } if (needWake) { nativeWake(mPtr); } } return true; }
固然还有那个谁——Runnable,他也是以消息的形式进行发送的,固然,加了一层封装,并将他的需求设置到Message的callback(优先级大于handleMessage),这样就能控制Handler执行他指定的需求,具体执行稍后讲述,先看下他的发送,只列举post看一下他的封装,postAtTime(Runnable r, long uptimeMillis)、postDelayed(Runnable r, long delayMillis)等等同理:
public final boolean post(Runnable r) { return sendMessageDelayed(getPostMessage(r), 0); } private static Message getPostMessage(Runnable r) { Message m = Message.obtain(); //将Runnable对象设置为Message的callback属性,封装到一个空Message中 m.callback = r; return m; }
这样,不论是Message仍是Runnable,都只要走Message的统一发送接口,就能把咱们的需求发送处处理线程的消息队列中。至此,发送线程中的操做就完成了,那要怎么使用呢,举个最简单的例子(接着上面的TestLooper):
TestLooper mLooper = new TestLooper(); //启动TestLooper线程(启动Looper、监听MessageQueue) mLooper.start(); //经过Looper对象绑定的Handler属性发送消息 Message msg = mLooper.mHandler.obtainMessage(); msg.what = 1; mLooper.mHandler.sendMessage(msg); Runnable rmsg = new MyRunnable(); mLooper.mHandlernew.post(rmsg);
最后来看一下Handler的消息处理,也就是上面loop函数中的msg.target.dispatchMessage:
public void dispatchMessage(Message msg) { //对比getPostMessage可知,这个callback就是Runnable,handleCallback其实就是调用Runnable的run方法,可见Runnable的优先级是最高的,当有特殊要求须要处理线程执行时,可用这个 if (msg.callback != null) { handleCallback(msg); } else { if (mCallback != null) { //回调函数,执行interface Callback的boolean handleMessage(Message msg),优先级次之 if (mCallback.handleMessage(msg)) { return; } } //定义Handler对象时重写的handleMessage函数,优先级最低,能够把他当成时最大众的处理方式,若是没有特殊需求,处理他,有特殊需求,就用Runnable代替 handleMessage(msg); } }
到这里,Handler的发送、处理消息也就分析完了,下面看一下如何使用。
上面已经列举了一个TestLooper的例子,首先将一个线程类定义成一个Looper类(Looper类中定义了Handler对象),调用线程类的start函数使Looper线程开始工做,经过Looper线程对象的Handler属性发送消息。下面列举一个经过 消息机制更新UI的例子:
public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); TextView text = (TextView) findViewById(R.id.editText); //在UI线程中,不用再建立Looper,直接定义Handler final Handler myHandler = new Handler(){ @Override public void handleMessage(Message msg){ if(msg.what == 0x01){ Bundle paramBuldle = msg.getData(); text.setText(paramBuldle.getString("data")); } } }; //启动发送线程 MyThread mthread = new MyThread(myHandler); mthread.start(); } } public class MyThread extends Thread{ private Handler pHandler; public MyThread(Handler inputHandler){ //处理线程中Handler对象的引用 pHandler = inputHandler; } @Override public void run() { Message msg = myHandler.obtainMessage(); msg.what = 0x01; for(int i = 0; i < input.length; i++){ Bundle paramBuldle = new Bundle(); paramBuldle.putString("data",input[i]); msg.setData(paramBuldle); //经过Handler对象的引用发送消息,由参数传递 pHandler.sendMessage(msg); } } }
在调试smali时,常常会遇到消息机制,而消息机制的异步特性致使调试时没法关联(sendMessage和handleMessage),这时就要借助消息机制的特性。
1.调试时遇到sendMessage,没法肯定消息处理函数的状况,首先看看找对应的handleMessage:
invoke-virtual {v1, v0}, Landroid/os/Handler;->sendMessage(Landroid/os/Message;)Z
这时根据消息机制的特性——发送消息的Handler也即处理消息的Handler,而这里时Handler通用类型,那就要回溯去寻找v1的类型
iget-object v1, p0, Lyi/decoder_yi/Helper/ThreadLooper;->pHandler:Landroid/os/Handler;
而后查找Lyi/decoder_yi/Helper/ThreadLooper;->pHandler 这个对象在何处赋值,若是赋值的地方较多,就须要借助调试帮忙肯定,这里发现是在初始化中赋值,很显然是一个参数传递引用
.method public constructor <init>(Landroid/os/Handler;)V invoke-direct {p0}, Ljava/lang/Thread;-><init>()V iput-object p1, p0, Lyi/decoder_yi/Helper/ThreadLooper;->pHandler:Landroid/os/Handler;
接着就要查找这个初始化函数是在哪里被调用,又是用什么参数进行赋值的(肯定Handler对象),这时若是又有多个,那仍是须要借助调试,对仍是他(由于在一些接口、虚函数处,咱们没法用静态的方法肯定它具体会执行哪个,只有经过动态调试去肯定)
iget-object v3, p0, Lyi/decoder_yi/Activity/AESdecode;->mhandler:Landroid/os/Handler; invoke-direct {v2, v3}, Lyi/decoder_yi/Helper/ThreadLooper;-><init>(Landroid/os/Handler;)V
到这一步,咱们的Handler对象就能够肯定了,就是这个mhandler,接着咱们就要找到mhandler的具体类型,而handleMessage就在这个具体类型的smali文件中(这个具体类型其实就是Handler的一个子类)
invoke-direct {v2, p0}, Lyi/decoder_yi/Activity/AESdecode$1;-><init>(Lyi/decoder_yi/Activity/AESdecode;)V iput-object v2, p0, Lyi/decoder_yi/Activity/AESdecode;->mhandler:Landroid/os/Handler;
到这就有了,这个类就是 Lyi/decoder_yi/Activity/AESdecode$1,找到它对应的smali文件中的handleMessage,就是所要找的处理函数
2.若是是Runnable,要找处理函数就简单多了,由于post的参数就是Runnable对象,找到该对象的具体类型(红色),直接能够到对应smali文件中查找run方法,这就是处理函数
invoke-direct {v1, p0}, Lyi/decoder_yi/Activity/ongdecode$2$1;-><init>(Lyi/decoder_yi/Activity/ongdecode$2;)V invoke-virtual {v0, v1}, Landroid/os/Handler;->post(Ljava/lang/Runnable;)Z
3.回调函数handleMessage与第一种状况相似,它寻找的是回调函数所在的类类型(红色)
invoke-direct {v3, p0}, Lyi/decoder_yi/Activity/activity_desdecode$1;-><init>(Lyi/decoder_yi/Activity/activity_desdecode;)V invoke-direct {v2, v3}, Landroid/os/Handler;-><init>(Landroid/os/Handler$Callback;)V iput-object v2, p0, Lyi/decoder_yi/Activity/activity_desdecode;->mhandler:Landroid/os/Handler;
4.而若是是调试时找到了处理函数,而没法肯定消息是从哪发送过来的(消息的处理函数由looper控制,调试时是一个死循环,这样就没法跳出这个线程去查找发送信息的函数),这种你推比较难理解一点,还好咱们有上面的思路,能够逆着推导
找到handleMessage后,查看它所在的类,而后查找类的初始化函数调用(这个类就是Handler的子类,咱们的Handler对象的具体类型,而要执行处理函数,就要定义Handler对象,因此确定会有这步初始化工做(这个初始化<init>其实就是类的构造)),发现handleMessage在类 Lyi/decoder_yi/Activity/mappingdecode$1中,紧接着找 Lyi/decoder_yi/Activity/mappingdecode$1;-><init>的调用
invoke-direct {v2, p0}, Lyi/decoder_yi/Activity/mappingdecode$1;-><init>(Lyi/decoder_yi/Activity/mappingdecode;)V iput-object v2, p0, Lyi/decoder_yi/Activity/mappingdecode;->mhandler:Landroid/os/Handler;
这里v2就是构建的Handler对象,紧接着它被赋值给mappingdecode的属性mhandler,能够猜想,发送线程要么是经过mappingdecode的对象直接操做属性mhandler发送信息,要么是将mhandler做为参数传递给发送线程进行信息的发送,不管如何,他会经过mhandler来发送,因此紧跟mhandler的使用
iget-object v3, p0, Lyi/decoder_yi/Activity/mappingdecode;->mhandler:Landroid/os/Handler; invoke-direct {v2, v3}, Lyi/decoder_yi/Helper/ThreadLooper;-><init>(Landroid/os/Handler;)V
显然这里是经过传递引用的方式来使用这个Handler对象,一样能够猜想,在类ThreadLooper中有一个Handler类型的属性,它做为 Lyi/decoder_yi/Activity/mappingdecode;->mhandler这个对象的引用,在以后会用这个引用来发送信息
.method public constructor <init>(Landroid/os/Handler;)V invoke-direct {p0}, Ljava/lang/Thread;-><init>()V iput-object p1, p0, Lyi/decoder_yi/Helper/ThreadLooper;->pHandler:Landroid/os/Handler;
pHandler就是这个 引用,接着就跟踪这个pHandler,看它在哪里调用了消息发送函数
iget-object v1, p0, Lyi/decoder_yi/Helper/ThreadLooper;->pHandler:Landroid/os/Handler; invoke-virtual {v1, v0}, Landroid/os/Handler;->sendMessage(Landroid/os/Message;)Z
好吧,就是它了(红色)