Android版本:8.1
- 我们知道MainActivity里面是不能做耗时操作的,几秒钟就会导致ANR,Application Not Responding应用无响应报错。
- 于是我们会把耗时操作代码放在异步任务里AsyncTask,比如用异步任务从网上下载一个图片。
- 下载完了我们就需要显示图片到Imageview上,而如果我们直接用imageview去更新图片,就会报错,提示你非主线程无法直接操作UI
CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
- 这时我们通常就会调用Handler对象,去发送一个Message,在Handler的handleMessage方法里,就能直接去更新Imageview了。
当初我在学习的时候就一下子就冒出了一些问题,
- 为什么UI线程会ANR?
- 为什么子线程不能访问主线程的UI?
- AsyncTask是怎么使用的?
- Handler是怎么使用的?
。。。
到了现在,当初的答案也都知道了,这里就写写关于Handler我知道的这一部分。
AsyncTask异步任务在使用的时候创建了一个新的Thread子线程,而主线程的view在初始化的时候都含有一个主线程对象,当然是ViewrootImpl那里,直接操作view的时候,都会先去检查是否是创建的时候的同一个线程,很明显,子线程不是那个创建的时候的线程,于是就会抛异常。在使用Handler的时候,发送Message,实现了跨线程通信,Message传到了主线程,再操作view更新,就不会异常了。
因为耗时,选择异步任务,因为子线程不能操作主线程UI,选择了Handler。
Hander在使用的时候
直接Handler handler = new Handler()就可以了,或者通常还会写一个匿名内部类方式。
private final Handler mHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
}
}
然后看看Handler源码,才知道handleMessage是handler的内部接口的方法
public interface Callback {
public boolean handleMessage(Message msg);
}
在Handler的里面有几个成员变量
final Looper mLooper;
final MessageQueue mQueue;
final Callback mCallback;
IMessenger mMessenger;
Handler把利用Messenger把Message发送到MessageQueue,然后Looper不断从队列里取出Message,这就是Handler处理信息的流程。
Handler的构造
public Handler() {
this(null, false);
}
public Handler(Callback callback) {
this(callback, false);
}
public Handler(Looper looper) {
this(looper, null, false);
}
public Handler(Looper looper, Callback callback) {
this(looper, callback, false);
}
public Handler(boolean async) {
this(null, async);
}
public Handler(Looper looper, Callback callback, boolean async) {
mLooper = looper;
mQueue = looper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
public Handler(Callback callback, boolean async) {
...
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;
}
在构造的时候如果传Looper对象,需要在Handler初始化之前调用Looper.prepare(),不然会异常。
其余的一般会使用无参或者带回调这两种方法。
于是可以理解前面的的匿名内部类写法了。
private final Handler mHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
}
}
---
private final Handler mHandler = new Handler(new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
return false;
}
});
Handler的Message构造方式
public final Message obtainMessage()
{
return Message.obtain(this);
}
public final Message obtainMessage(int what)
{
return Message.obtain(this, what);
}
public final Message obtainMessage(int what, Object obj)
{
return Message.obtain(this, what, obj);
}
public final Message obtainMessage(int what, int arg1, int arg2)
{
return Message.obtain(this, what, arg1, arg2);
}
public final Message obtainMessage(int what, int arg1, int arg2, Object obj)
{
return Message.obtain(this, what, arg1, arg2, obj);
}
看看Message.obtain
public static Message obtain(Handler h, int what,
int arg1, int arg2, Object obj) {
Message m = obtain();
m.target = h;
m.what = what;
m.arg1 = arg1;
m.arg2 = arg2;
m.obj = obj;
return m;
}
---
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();
}
这就是别人常说的消息池,先从消息池里获取已经存在的对象,如果没有才创建,降低内存泄漏,重复利用。
所以我们平时在sendmessage的时候,应该不要直接new Message, 而是用Message.obtain 或者Handler.obtainMessage
Handler的sendMessage
public final boolean sendMessage(Message msg)
{
return sendMessageDelayed(msg, 0);
}
public final boolean sendEmptyMessage(int what)
{
return sendEmptyMessageDelayed(what, 0);
}
public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
Message msg = Message.obtain();
msg.what = what;
return sendMessageDelayed(msg, delayMillis);
}
发送message可以用的方法还有
sendEmptyMessageAtTime(int what, long uptimeMillis)
sendMessageDelayed(Message msg, long delayMillis)
sendMessageAtTime(Message msg, long uptimeMillis)
sendMessageAtFrontOfQueue(Message msg)
最后我们看看它到底怎么发送Message的
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) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
这里的mQueue就是在构造Handler的时候的Looper的MessageQueue,
boolean enqueueMessage(Message msg, long when) {
//代码省略
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;
//代码省略
}
当message被发送给MessageQueue的时候,会有个无限循环查找当前队列的message,如果有就把msg传给prev,然后Looper里面,还在循环cha
public static void loop() {
final Looper me = myLooper();
final MessageQueue queue = me.mQueue;
Binder.clearCallingIdentity();
final long ident = Binder.clearCallingIdentity();
for (;;) {
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
try {
msg.target.dispatchMessage(msg);
end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
msg.recycleUnchecked();
}
}
Looper无限从MessageQueue 里面next()获取下一个msg.
然后调用msg.target.dispatchMessage(msg);
msg的target就是Handler,
public static Message obtain(Handler h, int what,
int arg1, int arg2, Object obj) {
Message m = obtain();
m.target = h; <---
m.what = what;
m.arg1 = arg1;
m.arg2 = arg2;
m.obj = obj;
return m;
}
于是回去看Handler的dispatchMessage
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
先检查msg是否含有回调,如果有就处理回调内容
如果没有则会检测当前Handler是否有回调对象,如果有就处理回调内容
如果没有回调,则直接处理msg
至此msg从handler发送出去,又回到handler来处理,好像没什么特别的。
其实这里忽略了一个点,msg是从子线程发送的,发出去之后它走到了主线程里,因为Looper是主线程里的Looper
在framework里,ActivityThread的Main方法里,Looper.prepareMainLooper();就是在Activity里构造了Looper对象,所以这里Looper是主线程的,那msg是什么时候从子线程传到主线程里的呢?
答案在MessageQueue的enqueueMessage里,做了一次传递
boolean enqueueMessage(Message msg, long when) {
//代码省略
synchronized (this) {
if (mQuitting) {
IllegalStateException e = new IllegalStateException(
msg.target + " sending message to a Handler on a dead thread");
Log.w(TAG, e.getMessage(), e);
msg.recycle();
return false;
}
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; // invariant: p == prev.next
prev.next = msg;
}
// We can assume mPtr != 0 because mQuitting is false.
if (needWake) {
nativeWake(mPtr);
}
}
//代码省略
}
所以,Message对象是跨过了线程,达到了主线程,所以提示信息可以更新,只需要根据msg的信息来更新不同的view就可以了。
然后关于Message对象,可以看到
public final class Message implements Parcelable {
Message实现了序列化接口Parcelable,这个标志表示这个对象可以实现跨进程传输,在不同进程传输的时候其实不是直接把对象给赋值过去,而是copy过去,把对象的所以属性都复制给一个新的另一个进程的对象,这就是实现序列化的意义。既符合不同进程对象不可直接传输,又实现了赋值对象间接的实现传输。
如此一来,发现其实Handler也很简单。