转载请注明文章出处LooperJing!
Handler这个系列总共有5篇,前面三篇博客,我们从源码的层面将Handler消息机制梳理了一遍,现在回头再看当时作为小白使用Hander所遇到的一些问题,显然,这些问题基本都可以做一个解释了。
Android源码解析Handler系列第(一)篇 --- Message全局池
Android源码解析Handler系列第(二)篇 --- ThreadLocal详解
Android源码解析Handler系列第(三)篇 --- 深入了解Android的消息机制
-1、为什么不能在子线程线程中更新UI?
这个答案可以在ViewRootImpl中找到,ViewRootImpl对UI的操作做了验证,如果不是主线程,就会抛出CalledFromWrongThreadException。
void checkThread() {
if (mThread != Thread.currentThread()) {
throw new CalledFromWrongThreadException(
"Only the original thread that created a view hierarchy can touch its views.");
}
}
进一步思考,为什么ViewRootImpl要做checkThread的操作呢,因为多线程并发访问UI是不安全的,但是如果用同步锁不仅会降低性能,也会使逻辑变得复杂,所以就采用单线程模型,这活交给了主线程来干!
-2 、下面这段代码为什么会报错?
new Thread(new Runnable() {
@Override
public void run() {
Handler handler=new Handler();
}
}).start();
创建Handler的时候,就会调用Looper.myLooper()去获取Looper对象。myLooper方法中会通过sThreadLocal.get()获取返回,没有获取到就抛出RuntimeException。
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,主线程在ActivityThread的main方法中默认给我们创建了Looper。对于子线程需要我们自己创建。解决方式如下:
new Thread(new Runnable() {
@Override
public void run() {
Looper.prepare();
Handler handler=new Handler();
}
}).start();
这样子线程就有了自己的Looper,也有了自己的MessageQueue。但是这样还是有问题的,当你用handler发送一个消息,并不能收到消息的回调,因为你没有启动消息循环,只有调用Looper.loop()之后,Looper才能不断的从消息队列中取出消息交给Handler分发处理。即最终的解决方案是这样的。
new Thread(new Runnable() {
@Override
public void run() {
Looper.prepare();
Handler handler=new Handler();
Looper.loop()
}
}).start();
-3、使用Handler为什么会容易内存泄露?
下面是一段“常规”写法
Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
mImageView.setImageBitmap(mBitmap);
}
}
因为内部类会有一个指向外部类的引用(这个Handler又持有Activity的引用,就导致该Activity无法被回收)。垃圾回收机制中约定,当内存中的一个对象的引用计数为0时,将会被回收Handler作为Android上的异步消息处理机制(好吧,我大多用来进行worker thread与UI线程同步),它的工作是需要Looper和MessageQueue配合的。简单的说,要维护一个循环体(Looper)处理消息队列(MessageQueue)。每循环一次就从MessageQueue中取出一个Message,然后回调相应的消息处理函数。如果,循环体中有消息未处理(Message排队中),那么Handler会一直存在,那么Handler的外部类(通常是Activity)的引用计数一直不会是0,所以那个外部类就不能被垃圾回收。很多人会遇到activity的onDestroy方法一直不执行就是这个原因。
解决方案
static class MyHandler extends Handler {
WeakReference<Activity > mActivityReference;
MyHandler(Activity activity) {
mActivityReference= new WeakReference<Activity>(activity);
}
@Override
public void handleMessage(Message msg) {
final Activity activity = mActivityReference.get();
if (activity != null) {
mImageView.setImageBitmap(mBitmap);
}
}
}
Please accept mybest wishes for your happiness and success
网友评论