相信Handler对于做为Android开发者的小伙伴们来讲并不陌生,某些阶段的面试中Handler甚至是必考题之一。那么Handler到底有什么用呢?为何要用Handler呢?java
咱们都知道在Android中分有UI线程和非UI线程,其中有一条规定就是只能在UI线程操做UI组件,这是为了防止多个线程并发的操做一个UI组件带来的问题。同时咱们也知道不能在UI线程中作耗时操做,那么这个时候就带来了问题,若是咱们要在一个耗时操做结束后再去操做某个UI组件,那要怎么作呢?好比咱们须要下载一份文件,而且在文件下载完成以后须要让TextView显示下载完成来提醒用户。面试
这个时候就轮到咱们的Handler出场了:缓存
public class MainActivity extends AppCompatActivity {
TextView tv;
private static final int MSG_DOWNLOAD_FINISH=10;
//建立Handler,同时会发现as会提示不建议咱们这么写,这个稍后再说
private Handler handler=new Handler(){
@Override
public void handleMessage(Message msg) {
switch (msg.what){
case MSG_DOWNLOAD_FINISH:
tv.setText("下载成功!");
break;
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tv=findViewById(R.id.tv);
tv.setText("downloading ...");
new Thread(new Runnable() {
@Override
public void run() {
try {
//这里模拟一个耗时操做,能够看到这里是在非UI线程运行的
Thread.sleep(3000);
Message message = handler.obtainMessage(MSG_DOWNLOAD_FINISH);
message.sendToTarget();
//上面部分也能够这么写,效果是同样的
// Message message=new Message();
// message.what=MSG_DOWNLOAD_FINISH;
// handler.sendMessage(message);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
}
}
复制代码
运行上面的代码咱们能够发如今子线程sleep 3s后,成功的修改了tv的文字为 下载成功。咱们成功的知足了Android对UI线程的两条规定,咱们凭借Handler成功的在子线程作耗时操做而且在完成以后更新UI界面。数据结构
到此咱们否则发现Handler能够作线程直接的通讯使用,经过Handler发送的一个Message,UI线程就能收到子线程要传达的消息,那么Handler为何能作到线程间的通讯呢?并发
咱们先来对Handler的工做流程有一个总体的了解less
假设有AB两个线程。在A线程建立一个Handler对象h,而且把h发送给B线程。此时A线程中经过Looper.loop()达到死循环不停的从A线程的MessageQueue中尝试获取一个Message,若是获取到Message就会经过message.target获取到A线程中建立的h,并调用h.dispatchMessage()方法(注意这一部分都是在A线程中进行的)。这时B线程获取到h对象和经过h.sendMessage(msg)将一个msg插入到了A线程的MessageQueue中,这样子一个流程就完成了。async
再看源码以前咱们先说下Handler的总体结构,话很少说看图。 ide
能够看到主要涉及到三个类函数
值得注意的是线程不是一开始就拥有和本身绑定的Looper,MessageQueue的,在使用Handler以前须要咱们去调用Looper.prepare()方法来初始化当前线程的Looper,MessageQueue对象。咱们能够看下这个方法作了什么事情:oop
public static void prepare() {
prepare(true);
}
private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper(quitAllowed));
}
复制代码
能够看到Looper.prepare方法最终会构建一个Looper对象并放入ThreadLocal中。关于ThreadLocal小伙伴们能够简单理解为不一样线程从同一个ThreadLocal中得到的对象是不一样的,是独属于本身线程的。
经过上述的代码咱们也可以发现同一个线程只能拥有一个Looper对象。 看到这里的小伙伴可能会奇怪了,在咱们上面的简单使用中咱们并无调用Looper.prepare(),为何还能使用Handler呢?
这是应为Android系统在启动当前APP建立出UI线程后就会去执行Looper.prepareMainLooper()方法,系统已经帮咱们初始化过了UI线程的Looper,因此咱们能够直接使用Handler对象了。
而说了Looper.prepare()就不得不提下Looper.loop()方法了。事实上咱们要想在子线程成功的new 出Handler而且顺利使用的话,必需要再调用下Looper.loop()方法。 loop方法是一个是创建一个死循环,不停的尝试从当前的MessageQueue中获取一个Message。
public static void loop() {
//myLooper()就是经过ThreadLocal获取当前线程的Looper实例
final Looper me = myLooper();
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
final MessageQueue queue = me.mQueue;
//...省略部分代码
for (;;) {
/** * 经过MessageQueue获取一个msg。这里小伙伴会问了不是说好了是循环的吗? * 可是这里若是queue.next()返回为空不就return掉了吗? * 这里大可放心,queue.next()内部又是一个死循环,只有当如下状况是才会返回空 * 1.调用MessageQueue的quite方法 * 2.Application某些状况下尝试去重启looper(这部分存疑) * 博主只发现这两个状况,有其余状况欢迎指出 */
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
//....
try {
//msg.target就是发送该msg的Handler对象
/** * 调用Handler的dispatchMessage分为三种状况 * 1.当msg含有Callback时候会调用msg的callback * 2.当msg不含有Callback可是Handler有设置Callback时候会调用Handler的callback。若是Handler的callback返回为true的话就不会在执行handleMessage方法 * 3.不知足以上二者时会调用Handler的handleMessage方法,也就是咱们例子中实现的方法 */
msg.target.dispatchMessage(msg);
} finally {
//...
}
//...
//这里可见Message在使用以后会被清空数据并缓存
msg.recycleUnchecked();
}
}
复制代码
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
private static void handleCallback(Message message) {
//这里的callback是一个Runnable。
//调用handler.post(new Runnable())方法就是生成一个带callback的msg并投入到MessageQueue中
message.callback.run();
}
复制代码
看完了取出Message并处理的操做,咱们看看发送Message部分的逻辑。 sendMessage(msg) ->sendMessageDelayed(msg, 0) ->sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis) ->enqueueMessage(queue, msg, uptimeMillis) ->queue.enqueueMessage(msg, uptimeMillis) 能够看到上面这一串调用链以后最终会调用MessageQueue的enqueueMessage方法。而这上面这一串调主要作了一些常规的检测操做,同时把当前的Handler赋值给msg.target。这部分很少说,咱们重点看入队操做
boolean enqueueMessage(Message msg, long when) {
//常规性检测,注意下msg.isInUse判断,表明一个msg只能入队一次
if (msg.target == null) {
throw new IllegalArgumentException("Message must have a target.");
}
if (msg.isInUse()) {
throw new IllegalStateException(msg + " This message is already in use.");
}
synchronized (this) {
//若是调用过stop,此时判断就会为true
if (mQuitting) {
msg.recycle();
return false;
}
msg.markInUse();
msg.when = when;
Message p = mMessages;
boolean needWake;
//接下来这部分就是关于单链表的操做,相信没什么好说的(看不懂的小伙伴要补习下数据结构知识哦)
if (p == null || when == 0 || when < p.when) {
// New head, wake up the event queue if blocked.
msg.next = p;
mMessages = msg;
needWake = mBlocked;
} else {
// Inserted within the middle of the queue. Usually we don't have to wake
// up the event queue unless there is a barrier at the head of the queue
// and the message is the earliest asynchronous message in the queue.
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; // invariant: p == prev.next
prev.next = msg;
}
// We can assume mPtr != 0 because mQuitting is false.
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}
复制代码
看到这里,小伙伴们应该能了解Handler的整套工做流程了。关于Handler中的线程切换,若是有点迷糊的话能够这么想 无论Handler被传递了什么线程,不论是在什么线程发送的消息。最终对该消息的处理都是在最初建立Handler的线程上。
由于Android系统在启动APP的时候已经调用过Looper.prepareMainLooper();和Looper.loop()了 ActivityThread.java的main方法
public static void main(String[] args){
...
Looper.prepareMainLooper();
//初始化Looper
...
ActivityThread thread = new ActivityThread();
//实例化一个ActivityThread
thread.attach(false);
//创建Binder通道
...
Looper.loop();
//主线程进入无限循环状态,等待接收消息
}
复制代码
这部分参考知乎上的一个答案 Android中为何主线程不会由于Looper.loop()里的死循环卡死?
这个问题我在最初的例子中写到了,as不建议咱们这么写Handler,这是应为非静态内部类会持有外部内的引用。那么Handler将会持有Activity的引用,咱们知道handler是会被msg.target持有的,而msg又在MessageQueue队列中,那么当消息队列中拥有未消费的Message时,会致使Activity即便finish了也没法被GC回收,最终致使内存泄漏。为了不这个问题咱们能够将Handler写成外部内或者静态的内部类,而且传递的Activity引用能够用WeakReference弱引用来持有,同时能够在Activity的onDestory中使用Handler.removeCallbacksAndMessages(null);来清空消息队列
因为Handler还有一部分涉及到native层面,而对这一层面博主并不了解,因此没有能提到这部分的东西,但愿之后能有时间补充这部分的内容。以上内容如有错误之处欢迎你们指出,你们一块儿进步。