美文网首页Android源码解析HandlerAndroid开发
Android源码解析Handler系列第(四)篇 --- 打破

Android源码解析Handler系列第(四)篇 --- 打破

作者: LooperJing | 来源:发表于2016-12-05 18:24 被阅读365次

    转载请注明文章出处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

    参考链接:http://blog.csdn.net/lincyang/article/details/46875157

    相关文章

      网友评论

        本文标题:Android源码解析Handler系列第(四)篇 --- 打破

        本文链接:https://www.haomeiwen.com/subject/zfokmttx.html