详解Android Handler机制和原理

描述

Handler的使用

在Android开发中,Handler机制是一个很重要的知识点,主要用于消息通信。

Handler使用的三大步骤:

1、Loop.prepare()。

2、new一个Handler对象,并重写handleMessage方法。

3、Loop.loop()。

先运行实例代码观察现象,再深入分析内部原理。

public class LooperThread extends Thread{
    private static final String TAG = LooperThread.class.getSimpleName();
    private Handler handler;

    @Override
    public void run() {
        Looper.prepare();
        handler = new Handler(Looper.myLooper(), new Handler.Callback() {
            @Override
            public boolean handleMessage(@NonNull Message msg) {
                Log.d(TAG, "what: " + msg.what + ", msg: " + msg.obj.toString());
                return true;
            }
        });
        Looper.loop();
    }

    public void sendMessage(int what, Object obj){
        Message msg = handler.obtainMessage(what, obj);
        handler.sendMessage(msg);
    }
}

 

 

public class FirstActivity extends AppCompatActivity {
    private static final String TAG = FirstActivity.class.getSimpleName();

    private LooperThread looperThread;

    @Override
    protected void onCreate(Bundle savedInstanceState) {

        looperThread = new LooperThread();
        looperThread.start();
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        looperThread.sendMessage(1, "Hello android!");
    }

 

编译运行程序,输出如下:

 

2021-10-06 23:15:24.323 20107-20107/com.example.activitytest D/FirstActivity: Task id is 73
2021-10-06 23:15:25.328 20107-20124/com.example.activitytest D/LooperThread: what: 1, msg: Hello android!
2021-10-06 23:15:25.394 20107-20132/com.example.activitytest I/OpenGLRenderer: Initialized EGL, version 1.4
2021-10-06 23:15:25.394 20107-20132/com.example.activitytest D/OpenGLRenderer: Swap behavior 1

 

Loop.prepare方法内部实现原理

了解某个方法具体做了什么,最好的方法就是追踪下去看源码。我们跟随IDE一步一步查看Loop.prepare到底做了什么。

/** Initialize the current thread as a looper.
      * This gives you a chance to create handlers that then reference
      * this looper, before actually starting the loop. Be sure to call
      * {@link #loop()} after calling this method, and end it by calling
      * {@link #quit()}.
      */
    public static void prepare() {
        prepare(true);
    }

    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));
    }

 

sThreadLocal是一个ThreadLocal类型变量,且ThreadLocal是一个模板类。Loop.prepare最终创建一个新的Looper对象,且对象实例被变量sThreadLocal引用。继续追踪下去,查看Looper构造方法做了什么操作。

 

private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);
        mThread = Thread.currentThread();
    }
......
MessageQueue(boolean quitAllowed) {
    mQuitAllowed = quitAllowed;
    mPtr = nativeInit();
}
到这里我们已经很清楚,Looper构造方法主要是创建一个MessageQueue,且MessageQueue构造方法调用native方法获取底层queue的指针,mQuitAllowed值为true表示允许退出loop,false表示无法退出loop。结合前面Looper.prepare方法内部代码,表示我们创建的Looper允许退出loop。 new一个Handler对象实例,到底做了什么?
/**
     * Use the provided {@link Looper} instead of the default one and take a callback
     * interface in which to handle messages.
     *
     * @param looper The looper, must not be null.
     * @param callback The callback interface in which to handle messages, or null.
     */
    public Handler(@NonNull Looper looper, @Nullable Callback callback) {
        this(looper, callback, false);
    }
......
    /**
     * Use the provided {@link Looper} instead of the default one and take a callback
     * interface in which to handle messages. Also set whether the handler
     * should be asynchronous.
     *
     * Handlers are synchronous by default unless this constructor is used to make
     * one that is strictly asynchronous.
     *
     * Asynchronous messages represent interrupts or events that do not require global ordering
     * with respect to synchronous messages. Asynchronous messages are not subject to
     * the synchronization barriers introduced by conditions such as display vsync.
     *
     * @param looper The looper, must not be null.
     * @param callback The callback interface in which to handle messages, or null.
     * @param async If true, the handler calls {@link Message#setAsynchronous(boolean)} for
     * each {@link Message} that is sent to it or {@link Runnable} that is posted to it.
     *
     * @hide
     */
    @UnsupportedAppUsage
    public Handler(@NonNull Looper looper, @Nullable Callback callback, boolean async) {
        mLooper = looper;
        mQueue = looper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }

 

Handler还有其他构造方法,这里我们调用其中一种构造方法创建一个Handler对象实例。该构造方法要求传入一个Looper对象实例和CallBack对象实例。回顾一下最开始的例子代码,我们传入的形参,一个是由Looper.myLooper方法获取的Looper对象实例,另外一个则是Callback匿名类。我们先看看Looper.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();
    }
这里获取到的就是前面Looper.prepare方法新创建的Looper对象实例,所以Looper.prepare方法必须在创建Handler对象实例之前调用。再回到Handler构造方法里,有几个地方很关键: 1、Handler内部保存了Looper对象引用。 2、Handler内部保存了Looper内部的MessageQueue对象引用。 3、Handler内部保存了Callback对象引用。 4、mAsyncchronous值为true表示handleMessage方法异步执行,false表示同步执行。

 

Looper.loop方法内部实现原理

 

/**
     * Run the message queue in this thread. Be sure to call
     * {@link #quit()} to end the loop.
     */
    public static void loop() {
        final Looper me = myLooper();
        if (me == null) {
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
        }
        if (me.mInLoop) {
            Slog.w(TAG, "Loop again would have the queued messages be executed"
                    + " before this one completed.");
        }

        me.mInLoop = true;
        final MessageQueue queue = me.mQueue;

        // Make sure the identity of this thread is that of the local process,
        // and keep track of what that identity token actually is.
        Binder.clearCallingIdentity();
        final long ident = Binder.clearCallingIdentity();

        // Allow overriding a threshold with a system prop. e.g.
        // adb shell 'setprop log.looper.1000.main.slow 1 && stop && start'
        final int thresholdOverride =
                SystemProperties.getInt("log.looper."
                        + Process.myUid() + "."
                        + Thread.currentThread().getName()
                        + ".slow", 0);

        boolean slowDeliveryDetected = false;

        for (;;) {
            Message msg = queue.next(); // might block
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                return;
            }

            // This must be in a local variable, in case a UI event sets the logger
            final Printer logging = me.mLogging;
            if (logging != null) {
                logging.println(">>>>> Dispatching to " + msg.target + " " +
                        msg.callback + ": " + msg.what);
            }
            // Make sure the observer won't change while processing a transaction.
            final Observer observer = sObserver;

            final long traceTag = me.mTraceTag;
            long slowDispatchThresholdMs = me.mSlowDispatchThresholdMs;
            long slowDeliveryThresholdMs = me.mSlowDeliveryThresholdMs;
            if (thresholdOverride > 0) {
                slowDispatchThresholdMs = thresholdOverride;
                slowDeliveryThresholdMs = thresholdOverride;
            }
            final boolean logSlowDelivery = (slowDeliveryThresholdMs > 0) && (msg.when > 0);
            final boolean logSlowDispatch = (slowDispatchThresholdMs > 0);

            final boolean needStartTime = logSlowDelivery || logSlowDispatch;
            final boolean needEndTime = logSlowDispatch;

            if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
                Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
            }

            final long dispatchStart = needStartTime ? SystemClock.uptimeMillis() : 0;
            final long dispatchEnd;
            Object token = null;
            if (observer != null) {
                token = observer.messageDispatchStarting();
            }
            long origWorkSource = ThreadLocalWorkSource.setUid(msg.workSourceUid);
            try {
                msg.target.dispatchMessage(msg);
                if (observer != null) {
                    observer.messageDispatched(token, msg);
                }
                dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
            } catch (Exception exception) {
                if (observer != null) {
                    observer.dispatchingThrewException(token, msg, exception);
                }
                throw exception;
            } finally {
                ThreadLocalWorkSource.restore(origWorkSource);
                if (traceTag != 0) {
                    Trace.traceEnd(traceTag);
                }
            }
            if (logSlowDelivery) {
                if (slowDeliveryDetected) {
                    if ((dispatchStart - msg.when) <= 10) {
                        Slog.w(TAG, "Drained");
                        slowDeliveryDetected = false;
                    }
                } else {
                    if (showSlowLog(slowDeliveryThresholdMs, msg.when, dispatchStart, "delivery",
                            msg)) {
                        // Once we write a slow delivery log, suppress until the queue drains.
                        slowDeliveryDetected = true;
                    }
                }
            }
            if (logSlowDispatch) {
                showSlowLog(slowDispatchThresholdMs, dispatchStart, dispatchEnd, "dispatch", msg);
            }

            if (logging != null) {
                logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
            }

            // Make sure that during the course of dispatching the
            // identity of the thread wasn't corrupted.
            final long newIdent = Binder.clearCallingIdentity();
            if (ident != newIdent) {
                Log.wtf(TAG, "Thread identity changed from 0x"
                        + Long.toHexString(ident) + " to 0x"
                        + Long.toHexString(newIdent) + " while dispatching to "
                        + msg.target.getClass().getName() + " "
                        + msg.callback + " what=" + msg.what);
            }

            msg.recycleUnchecked();
        }
    }

 

代码较长,我们只取关键代码阅读。通过myLooper获取新创建的Looper对象实例,进而获取Looper内部的MessageQueue对象实例。然后进入死循环中不断调用MessageQueue类的next方法获取MessageQueue里的message,然后调用dispatchMessage进行消息分发,最后由handleMessage进行消息处理。到这里Looper、MessageQueue和Handler之间的关系就建立起来了。介于篇幅,发送消息和消息处理原理,下篇文章详细分析。

  审核编辑:汤梓红

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分