zoukankan      html  css  js  c++  java
  • 01、Android进阶Handler原理解析

    Handler原理

    Handler是支撑整个Android系统运行的基础,本质上Android系统都是由事件驱动的。而处理事件的核心就在于Handler。

    关于Handler、Looper、MessageQueue和Message的概述如下:

    Handler 消息机制中作为一个对外暴露的工具,其内部包含了一个 Looper 。负责Message的发送及处理。
    Looper 作为消息循环的核心,其内部包含了一个消息队列 MessageQueue,用于记录所有待处理的消息。(线程切换在这里完成)
    MessageQueue 则作为一个消息队列,则包含了一系列链接在一起的Message(内部是单链表)
    Message 则是消息体,内部又包含了一个目标处理器target,这个target正是最终处理它的Handler

    Handler的使用

    Handler的使用非常简单,一般分为在主线程创建Handler和在子线程创建Handler两种方式。
    一般是在主线程中实现一个Handler,然后在子线程中使用它。

    public class MainActivity extends AppCompatActivity {
        private Handler handler = new MyHandler();
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            new Thread(){
                @Override
                public void run() {
                    handler.sendEmptyMessageDelayed(1, 1000);
                }
            };
        }
        // 自定义一个Handler
        class MyHandler extends Handler {
            @Override
            public void handleMessage(@NonNull Message msg) {
                Log.i("MainActivity", "主线程:handleMessage:" + msg.what);
            }
        }
    }
    

    或者有时候需要在子线程中创建运行在主线程中的Handler

    public class MainActivity extends AppCompatActivity {
        private Handler handler = null;
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            new Thread(){
                @Override
                public void run() {
                    Looper.prepare();
                    handler = new MyHandler();
                    handler.sendEmptyMessageDelayed(1, 1000);
                    Looper.loop();
                }
            };
        }
        // 自定义一个Handler
        class MyHandler extends Handler {
            @Override
            public void handleMessage(@NonNull Message msg) {
                Log.i("MainActivity", "主线程:handleMessage:" + msg.what);
            }
        }
    }
    

    Handler

    Handler可以用来发送消息,我们这里从sendMessage()方法开始,源码如下所示:

    public final boolean sendMessage(@NonNull Message msg) {
        return sendMessageDelayed(msg, 0);
    }
    

    调用了sendMessageDelayed方法:

    public final boolean sendMessageDelayed(@NonNull Message msg, long delayMillis) {
        if (delayMillis < 0) {
            delayMillis = 0;
        }
        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
    }
    

    继而调用sendMessagAtTime方法:

    public boolean sendMessageAtTime(@NonNull 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);
    }
    

    在这里赋值MessageQueue并且将它作为参数传递给enqueueMessage()方法,其方法的调用顺序如下:

    可以看到无论如何,最后都会走到enqueueMessage()方法中。接下来看看enqueueMessage()方法:

    private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg, long uptimeMillis) {
        msg.target = this;
        msg.workSourceUid = ThreadLocalWorkSource.getUid();
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }
    

    enqueueMessage()方法一共做了两件事情,一个是给Message赋值,一个是调用传进来的这个MessageQueue的enqueueMessage()方法。

    target表示的就是当前的Handler,也就是说每个发出去的Message都持有把它发出去的Handler的引用。

    MessageQueue

    Handler这个mQueue就是上文我们提到过的MessageQueue对象,它是一个单链表(一种链式存取的数据结构)。

    我们在sendMessageAtTime()方法中对MessageQueue进行赋值,并且当做参数传递给Handler的enqueueMessage()方法。

    public boolean sendMessageAtTime(@NonNull Message msg, long uptimeMillis) {
        // 对MessageQueue进行赋值,mQueue来源于构造方法
        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);
    }
    

    其实MessageQueue是在构造方法中进行赋值的,接下来我们看看构造方法的源码:

    public Handler(@Nullable 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 " + Thread.currentThread()
                + " that has not called Looper.prepare()");
        }
        // 对MessageQueue进行赋值,来自于Looper
        mQueue = mLooper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }
    

    这里我们可以看到,MessageQueue的实例创建应该在Looper中,这里先不研究Looper,继续探索MessageQueue。

    最后发送消息都调用的是MessageQueue的queue.enqueueMessage(msg, uptimeMillis)方法。

    boolean enqueueMessage(Message msg, long when) {
        if (msg.target == null) {
            throw new IllegalArgumentException("Message must have a target.");
        }
        synchronized (this) {
            if (msg.isInUse()) {
                throw new IllegalStateException(msg + " This message is already in use.");
            }
            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; 
                prev.next = msg;
            }
            if (needWake) {
                nativeWake(mPtr);
            }
        }
        return true;
    }
    

    主要分为三个步骤,可以参看代码标注:

    mMessages 是队列的第一消息,获取到它判断消息队列是不是空的,是则将当前的消息放到队列头部。
    如果当前消息不需要延时,或当前消息的执行时间比头部消息早,也是放到队列头部。
    如果不是以上情况,说明当前队列不为空,并且队列的头部消息执行时间比当前消息早,需要将它插入到队列的中间位置。

    我们也许会产生疑问,如何判断这个位置呢?其实依旧是通过消息被执行的时间。

    通过遍历整个队列,当队列中的某个消息的执行时间比当前消息晚时,将消息插到这个消息的前面。

    Looper

    我们再来看看Handler的构造方法,如下所示:

    public Handler(@Nullable 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());
            }
        }
        // 赋值Looper的实例
        mLooper = Looper.myLooper();
        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread " + Thread.currentThread()
                + " that has not called Looper.prepare()");
        }
        // 对MessageQueue进行赋值,来自于Looper
        mQueue = mLooper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }
    

    可以看到在这里通过mLooper = Looper.myLooper()代码赋值了Looper的实例,它作为消息循环的核心,不断的从MessageQueue中取出消息进行分发。

    我们再来看看Lopper的myLooper()方法,源码如下:

    /**
    * Return the Looper object associated with the current thread.  Returns
    * null if the calling thread is not associated with a Looper.
    */
    public static @Nullable Looper myLooper() {
        return sThreadLocal.get();
    }
    

    可以看到这个sThreadLocal是一个ThreadLocal类,并且它的泛型是Looper对象。

    ThreadLocal提供了线程的局部变量,每个线程都可以通过set()和get()来对这个局部变量进行操作,但不会和其他线程的局部变量进行冲突,实现了线程的数据隔离。

    我们看到源码中有个注释:除非您已调用prepare(),否则sThreadLocal.get()将返回null。接下来看看prepare()方法:

    public static void prepare() {
        prepare(true);
    }
    private static void prepare(boolean quitAllowed) {
        // 如果sThreadLocal有值,就抛异常,没有值才会塞进去一个值。
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        sThreadLocal.set(new Looper(quitAllowed));
    }
    

    prepare方法必须调用但也只能调用一次,不调用没有值,抛异常,调用多次也还抛异常。

    在构造Handler之前,必须调用Looper的prepare()方法创建Looper。

    接下来看看sThreadLocal.set(new Looper(quitAllowed))方法的源码:

    public void set(T value) {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
    }
    

    set方法首先获取到了当前的线程,然后获取一个map。这个map是以键值对形式存储内容的。

    如果不为空就塞进去值,如果获取的map为空,就创建一个map。这里面的key是当前的线程,这里面的value就是Looper。(线程绑定)

    接下来我们去看看Lopper的构造方法,源码如下所示:

    private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);
        mThread = Thread.currentThread();
    }
    

    可以看到在Looper的构造方法中创建了MessageQueue对象。
    由于prepare()方法只能调用一次,所以只会创建一个Looper对象,所以:

    一个线程中只会创建一个Looper对象,而一个Looper对象也只会创建一个MessageQueue对象。

    最后,主线程创建Handler没有调用prepare()方法,那么Looper是从哪里来的?

    其实主线程的Looper是在ActivityThread类中main()方法中创建的,接下来我们看看这个main()方法:

    public static void main(String[] args) {
        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");
        Process.setArgV0("<pre-initialized>");
        // 就是这里调用了prepare方法
        Looper.prepareMainLooper();
        ActivityThread thread = new ActivityThread();
        thread.attach(false, startSeq);
        if (sMainThreadHandler == null) {
            sMainThreadHandler = thread.getHandler();
        }
        if (false) {
            Looper.myLooper().setMessageLogging(new LogPrinter(Log.DEBUG, "ActivityThread"));
        }
        // End of event ActivityThreadMain.
        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
        // 就是这里给主线程创建了Looper
        Looper.loop();
        throw new RuntimeException("Main thread loop unexpectedly exited");
    }
    

    接下来我们看看Looper的prepareMainLooper方法:

    public static void prepareMainLooper() {
        //设置不允许退出的Looper
        prepare(false);
        synchronized (Looper.class) {
            if (sMainLooper != null) {
                throw new IllegalStateException("The main Looper has already been prepared.");
            }
            sMainLooper = myLooper();
        }
    }
    

    prepare(false)创建了Looper并且把它和当前线程一起塞进map中的。
    Looper要分发消息并不知道具体发送消息的时间,只能开启死循环不断从MessageQueue中获取消息,所以Looper.loop()方法就是开启了死循环:

    public static void loop() {
        // 拿到当前线程的Looper
        final Looper me = myLooper();
        if (me == null) {
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
        }
        
        // ......
        for (;;) {
            Message msg = queue.next(); // might block
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                return;
            }
            // ......
            msg.recycleUnchecked();
        }
    }
    

    在循环中Looper不停的取出消息,拿到Message对象以后,会去调用Message的target字段的dispatchMessage方法,其中target就是发送消息的Handler。

    可以再来看看dispatchMessage方法,源码如下所示:

    public void dispatchMessage(@NonNull Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }
    

    可以看到在回调中调用了handleMessage(msg)方法。

    通过Handler发送消息时,消息会回到Handler初始化的线程,而不一定是主线程,所以Handler其实是借助共享变量来进行线程切换的。

    Looper和ANR

    其实整个Android就是在一个Looper的loop循环的,整个Android的一切都是以Handler机制进行的,即只要有代码执行都是通过Handler来执行的,而所谓ANR便是Looper.loop()没有得到及时处理,一旦没有消息,Linux的epoll机制则会通过管道写文件描述符的方式来对主线程进行唤醒与沉睡,Android里调用了linux层的代码实现在适当时会睡眠主线程

  • 相关阅读:
    Codeforces 672D
    Codeforces 672C
    Codeforces 673D
    Codeforces 1181D
    Codeforces 1181C
    Codeforces 1011F
    Codeforces 1011E
    2020.1.3计导全面复习
    csp模拟题-201903
    CCF模拟题-201909
  • 原文地址:https://www.cnblogs.com/pengjingya/p/14939030.html
Copyright © 2011-2022 走看看