1. Handler之前篇
1.1 ANR(Application Not Response)
在介绍为什么使用Handler之前,先来介绍一下ANR(Application Not Response)
1. ANR 发生的时机: 当用户进行的点击操作的时候,如果这个时候,应用程序在执行非常耗时的操作,导致,
系统和应用程序无法在继续处理用户的操作,
那么这个时候 系统会检测 操作的时间,如果超过5s,那么显示应用程序无响应。
2. 如何避免出现ANR的问题:
2.1. 控件事件的操作,如果需要耗时处理,那么放到子线程中完成;
3. 子线程中,获取的数据,如何显示到UI 控件上?
对于子线程操作/更新UI,必然出现异常,可以通过子线程,向创建UI控件的线程,发送消息,来进行处理;
4.创建UI控件的线程,被称作为 “UI线程”,执行其他操作的线程叫做子线程。
1.2 为什么要使用Handler
在ActivityUI中,需要很短的时间进行界面的绘制,Android是在每16ms进行一次界面的绘制。但是有时候界面会一直出现未响应,这是因为在UI线程中执行了耗时的操作,我们可以通过主线程开启一个线程,执行耗时操作,之后,子线程将得到的结果再
通过Handler机制,将消息发送给主线程,今儿在handMessage中执行UI的更新。Handler机制成为了线程之间进行通信的桥梁。
2. Handler的机制
在一个Activity启动时,首先执行的方法是ActivityThread中的main()方法,在main()方法中,会执行两个方法
Looper.prepareMainLooper(); 和Looper.loop();Looper.prepareMainLooper();方法会调用Looper中的prepare()方法;建立looper,下面是源码:
public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
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));
}
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
这样我们就可以得知,在Activity一启动,就会自动创建一个looper和一个messageQueue
在这之后,调用Looper.loop();一直等待messageQueue中的消息的来临,
(这里大家可以自己探究一下MessageQueue中的阻塞原理…….)
- 之后,我们会在Activity中new Handler(),在new Handler的同时,我们可以看一下他的源码:
Handler的构造方法:
public Handler(Callback callback, boolean async) {
if (FIND_POTENTIAL_LEAKS) {
final Class<? extends Handler> klass = getClass();
if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
(klass.getModifiers() & Modifier.STATIC) == 0) {
Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
klass.getCanonicalName());
}
}
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
Handler内部会自动调用Looper.myLooper();获取当前线程的lopper,
并且mLooper.mQueue;还会拿到messageQueue;
之后,我们可以说是handler和本线程中的looper mesageQueue绑定了.
3.之后,我们就可以新建Message了, 之后 可以添加相应的属性,但是在sendMessage之后,将消息发送到哪了哪?
我们最终会调到这个方法:enqueueMessage(),怎样将message加入到队列中
final boolean enqueueMessage(Message msg, long when) {
if (msg.when != 0) {
throw new AndroidRuntimeException(msg + " This message is already in use.");
}
if (msg.target == null && !mQuitAllowed) {
throw new RuntimeException("Main thread not allowed to quit");
}
synchronized (this) {
if (mQuiting) {
RuntimeException e = new RuntimeException(msg.target + " sending message to a Handler on a dead thread");
Log.w("MessageQueue", e.getMessage(), e);
return false;
} else if (msg.target == null) {
mQuiting = true;
}
msg.when = when;
Message p = mMessages;
if (p == null || when == 0 || when < p.when) {
msg.next = p;
mMessages = msg;
this.notify();
} else {
Message prev = null;
while (p != null && p.when <= when) {
prev = p;
p = p.next;
}
msg.next = prev.next;
prev.next = msg;
this.notify();
}
}
return true;
}
3.1 sendMessageAtTime()方法接收两个参数,其中msg参数就是我们发送的Message对象,
而uptimeMillis参数则表示发送消息的时间,它的值等于自系统开机到当前时间的毫秒数再加上延迟时间,
如果没有延迟时间,延迟时间默认就为0.
3.2 从enqueueMessage方法我们可以看出,所谓的入队其实就是将所有的消息按时间来进行排序,
这个时间当然就是我们刚才介绍的uptimeMillis参数。
3.3 这样message就加入到messageQueue当中了.
4.之后我们回到Looper.loop()当中, 他会一直执行 Message msg = queue.next(); ,调用messageQueue的
next()方法,等待消息,如果没有就阻塞.拿到消息之后,会调到dispatchMessage, 调用相应的方法去处理消息.
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
如果我们的Handler中没有一个callback, 会调用到我们重写的handleMessage方法处理我们的消息.
到这里Handler的消息处理机制,我们就了解的差不多了.
有关细致的地方:
http://www.nowcoder.com/discuss/1965
3. Handler的使用
3.1 主线程往子线程发送消息
主线程往子线程发送消息,四步走
/* 在子线程中建立loop,如果没有,会出现这样的异常
*提示的错误信息为 Can't create handler inside thread that
* has not called Looper.prepare() 。
*/
1.Looper.prepare();
2.新建一个Handler,并重写handleMessage()
3. // 把子线程中的handler对象,交给主线程 (发送消息的Handler和接受消息的Handler必须是同一个)
mMainHandler = handler;
4.Looper.loop();
应用场景:
聊天软件,子线程联网,界面部分发送消息给子线程。
注意:
1.Looper.myLooper().quit(); 代表当前线程中的Looper就退出了;looper的loop() 方法,结束;
这是一张关系图:
3.2 子线程通过Handler像主线程发送消息。
子线程往主线程中发送消息,这是我们通常见到的现象,子线程进行耗时的操作,
将得到的结果返回给主线程,主线程再在handleMessage()中处理.
通常的做法:
1.主线程中开启一个线程:new Thread();
2.在子线程中创建消息,并sendMessage();
3.主线程在handleMessage()中处理从MessageQueue中拿到的message进行处理.
这是一张他们的关系图
4.Handler和AsyncTask的区别
- Handler与线程配合,应用程序可以同时开启多个线程,一般的情况下,
系统可以支持很多线程的运行;对于异步任务而言,同一时刻,能够同时执行的异步任务数量很少; - 异步任务是一种能够在后台执行,短时间的后台任务的API;线程,可以在后台执行,长时间耗时操作的API;
- 代码编写:异步任务将线程与Handler进行封装,简化代码的开发;线程方式就需要编写Handler和发送消息。