详解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 732021-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.42021-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之间的关系就建立起来了。介于篇幅,发送消息和消息处理原理,下篇文章详细分析。


!销售/收购/维修 HP83711B HP 83711B A
2011年通信展今日开幕
RC桥式振荡器,RC桥式振荡器工作原理是什么?
微雪电子IC120-0324-009 PLCC32测试座简介
汇川技术预计上半年实现营收超25.96 亿
详解Android Handler机制和原理
Choosing the Right RS-232 Tran
Versal系列芯片三个产品的基础知识
普通车床的组成和传动系统
可穿戴技术在医疗健康领域中的应用趋势
OpenHarmony视频录制流程介绍
S7-200 PLC的数据区
北工大校友Cheng Zhang获SIGGRAPH最佳博士论文奖!
Club Vivado 的用户们, 你在哪里?
华为nova8系列、畅享20SE部分参数曝光
动态充电:电动汽车在行驶中充电
科达猎鹰人像大数据平台 以熟稔身法游刃在技战前沿
迎接“无尾”时代 无线供电高层研讨会即将召开
光控开关与光电开关的区别
浅谈高压SiC MOSFET发展历程与研究现状