Android杂谈(25)Handler机制梳理

转载请注意:http://blog.csdn.net/wjzj000/article/details/75088423

本菜开源的一个自己写的Demo,希望能给Androider们有所帮助,水平有限,见谅见谅…
https://github.com/zhiaixinyang/PersonalCollect (拆解GitHub上的优秀框架于一体,全部拆离不含任何额外的库导入)
https://github.com/zhiaixinyang/MyFirstApp(Retrofit+RxJava+MVP)


写在前面

今天突然莫名其妙的想起了Handler,然后自己凭借着记忆去梳理了一下Handler机制…然后整个人记忆就凌乱了,那感觉就像:先帝创业未半,而中道崩殂,今天下三分,益州疲弊,此诚危急存亡之秋也。然滚滚长江东逝水,浪花淘尽英雄,是非成败转头空,二龙戏珠决雌雄,东风不与周郎便,生子当如孙仲谋….记忆已经乱到波多野结衣战胜了七个小矮人赢取了日向雏田出任了海贼王…
所以今天特定来用文字去梳理一下。


开始

我们正常在子线程获取数据,想要更新UI的时候,我们就会用到Handler,正常我们会这么写:

    private Handler handler=new Handler(){
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            if (msg.what==1){
                textView.setText(msg.arg1+"");
            }
        }
    };

    //省略部分代码
    new Thread(new Runnable() {
        @Override
        public void run() {
            Message message=new Message();
            message.what=1;
            message.arg1=123;
            handler.sendMessage(message);
        }
     }).start();

OK,在这里我们可以注意到,我们直接使用了Handler这个类,以及Message。仅仅是只有这俩个类在工作么?显然不可能呐,势必要有第三者插足,所以Handler机制就变复杂了!可是这能怎么办呢?我也很绝望,但是只能选择原谅它。


我们都知道,整个Handler机制涉及到Handler、Looper、MessageQueue、Message甚至ThreadLocal、ActivityThread等等…所以整体的剪不断理还乱的关系略显有些复杂,因此我们一点点来,从使用开始:

我们使用时会直接或间接的new一个Handler,重写里边的handleMessage(Message msg)方法。这里我们知道,我们要在主线程(我们需要在主线程更新UI,不在主线程也一样,只要能获得到Looper对象)运行的组件中new,否则抛出Can't create handler inside thread that has not called Looper.prepare()异常。如果我们查看源码我们会发现:

    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。这里写到:Handler应该为static,否则有可能出现内存泄漏。
                //这里是因为内部类会持有外部类的引用,如果Handler没有运行完毕,而Activity理杀死,那么这时候便会因为Handler一直持有引用而导致GC不会回收这块内存,即会造成泄漏。
                Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
                    klass.getCanonicalName());
            }
        }

        //这里出现了Looper这个类,并通过myLooper()来获取一个Looper。
        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.myLooper()获取不到mLooper对象就会抛出异常。(如果我们没有手动的创建Looper,默认只有主线程之中才存在Looper)所以我们接下来就让我们走进Looper中。

//这个方法很简单,就是通过ThreadLocad.get()来获取Looper对象。那么这个Looper对象就什么时候被创建并传到ThreadLocal里的呢?
public static Looper myLooper() {
    //get的原理是通过当前线程进行查找,以获取对应的<T>对象,这里的泛型是Looper
    return sThreadLocal.get();
}

//prepare方法就是创建Looper并传入ThreadLocal中。
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));
}
//new一个Looper的同时会创建MessageQueue,以及获取当前线程
private Looper(boolean quitAllowed) {
    mQueue = new MessageQueue(quitAllowed);
    mThread = Thread.currentThread();
}

这里我们可以看到一些端倪,Looper之中持有一个MessageQueue,而我们的Handler出new出来之后也有和一个Looper对象建立关系,而这个Looper对象还得通过Looper.myLooper()->sThreadLocal.get()获取。因此我们需要调用prepare()来完成对Looper的初始化。
那么这样,我们的Handler就和Looper以及MessageQueue建立了关系。
但是prepare()是哪里被调用的呢?…ActivityThread。关于这个类是干嘛的,我相信各位看官多多少少也略有耳闻,毕竟走南闯北这么多年,道上有头有脸的“朋友”还是略懂略懂。这里就不对ActivityThread展开了,因为一旦展开根本停不下来,所以就简单的看一下其中和Looper有关的地方:

Looper.prepareMainLooper();
Looper.loop();

这里是ActivityThread中main方法里的Looper相关内容,我们可以发现,这里使用了prepareMainLooper(),其实内部也调用了prepare(),不过进行了额外的赋值:

    public static void prepareMainLooper() {
        //通过此保证,当前线程只有一个Looper对象,而当前线程是主线程。因此保证了,主线程只有一个Looper。
        prepare(false);
        synchronized (Looper.class) {
            if (sMainLooper != null) {
                throw new IllegalStateException("The main Looper has already been prepared.");
            }
            //调用myLooper,从ThreadLocal中获取Looper
            sMainLooper = myLooper();
        }
    }

    public static @Nullable Looper myLooper() {
        return sThreadLocal.get();
    }

那么此时很明显,我们的初始化了Looper并调用了loop()方法,而loop()就是一个循环:


    public static void loop() {
        final Looper me = myLooper();   
        final MessageQueue queue = me.mQueue;
        //省略部分代码
        for (;;) {
            Message msg = queue.next(); 
            msg.target.dispatchMessage(msg);
           //省略部分代码
        }
    }

我们可以看到,我们loop()是一个死循环,不断的从MessageQueue之中获取Message对象,然后通过Message.target.dispatchMessage(msg),将自己发送出去,而这里的target其实就是Handler对象,并且是我们最开始时new的那个Handler。因此我们的最后一步就是去看看Message做了什么处理。在开始一层一层的剥开Message时,我们先沐浴焚香,抚琴赏菊一发…不不不,是梳理一下一上的内容:

我们的ActivityThread在自己的main方法之中,初始化了Looper,并且调用loop方法,进行了循环拿到MessageQueue之中的Message,通过Message之中持有的Handler,将自己发送出去。发送出去,之后怎么办?继续往下看:

    //调用这个方法的本质,还是调用handleMessage()也就是我们new Handler是重写的那个方法
    public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }

至于我们的Handler是怎么和Message绑定在一起的,这要追溯到sendMessage这个方法:

    //sendMessage()方法一层层调用至enqueueMessage(),这里我们可以看到msg.target=this,这样就完成了Handler与Message的绑定。
    private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        msg.target = this;
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }

OK,到这里我们这条Handler机制的线就可以基本走通。
首先,我们会在主线程之中new一个Handler,为什么要在主线程?因为我们要在handleMessage方法之中更新UI,(我们可以在子线程通过Looper.prepare以及Looper.loop方法进行Toast,但是不能更新UI,因为Toast本质不是在更新UI)因此我们需要在主线程new Handler()。
new出来之后,我们就和main()方法中初始化的Looper扯上了关系。当我们在后台线程中使用sendMessage发送Message的时候,我们的Looper就会从MessageQueue之中出去Message,通过msg.target的dispatchMessage方法,调用handleMessage完成在主线程更新UI。


尾声

最后希望各位看官可以star我的GitHub,三叩九拜,满地打滚求star:
https://github.com/zhiaixinyang/PersonalCollect
https://github.com/zhiaixinyang/MyFirstApp

猜你喜欢

转载自blog.csdn.net/wjzj000/article/details/75088423