博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Android源码解析之一 异步消息机制
阅读量:7052 次
发布时间:2019-06-28

本文共 18956 字,大约阅读时间需要 63 分钟。

Android源码解析之一 异步消息机制

1,handler的常规使用方式

/** * 测试Activity,主要用于测试异步消息(Handler)的使用方式 */public class MainActivity extends AppCompatActivity {    public static final String TAG = MainActivity.class.getSimpleName();    private TextView texttitle = null;    /**     * 在主线程中定义Handler,并实现对应的handleMessage方法     */    public static Handler mHandler = new Handler() {        @Override        public void handleMessage(Message msg) {            if (msg.what == 101) {                Log.i(TAG, "接收到handler消息...");            }        }    };    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        texttitle = (TextView) findViewById(R.id.texttitle);        texttitle.setOnClickListener(new View.OnClickListener() {            @Override            public void onClick(View v) {                new Thread() {                    @Override                    public void run() {                        // 在子线程中发送异步消息                        mHandler.sendEmptyMessage(101);                    }                }.start();            }        });    }}复制代码

根据上述代码可以看出,一般handler的使用方式都是在主线程中定义handler,然后在子线程中调用mHandler.sendEnptyMessage();方法,那么这里就有一个疑问了,我们可以在子线程中定义handler 吗?

2,如何在子线程中定义Handler ?

我们在子线程中定义Handler,看看测试结果:

/** * 定义texttitle的点击事件处理 */texttitle.setOnClickListener(new View.OnClickListener() {            @Override            public void onClick(View v) {                /**                 * 定义在子线程中执行handler的创建                 */                new Thread() {                    @Override                    public void run() {                        Handler mHandler = new Handler() {                            @Override                            public void handleMessage(Message msg) {                                if (msg.what == 101) {                                    Log.i(TAG, "在子线程中定义Handler,并接收到消息。。。");                                }                            }                        };                    }                }.start();            }        });复制代码

可以看出在子线程中定义的Handler对象报错了,难道Handler对象的定义或者出事中能在主线程中?其实不是这样,错误信息中提示的很明显了,在初始化Handler对象之前需要调用Looper.prepare()方法。

/** * 定义texttitle的点击事件处理 */texttitle.setOnClickListener(new View.OnClickListener() {            @Override            public void onClick(View v) {                new Thread() {                    @Override                    public void run() {                        Looper.prepare();                        Handler mHandler = new Handler() {                            @Override                            public void handleMessage(Message msg) {                                if (msg.what == 101) {                                    Log.i(TAG, "在子线程中定义Handler,并接收到消息。。。");                                }                            }                        };                    }                }.start();            }        });        复制代码

再次点击按钮运行此段代码,程序就不会报错了,那么这说明初始化Handler对象的时候我们是需要调用Looper.prepare()方法的,那么主线程中为什么可以直接初始化Handler呢?

再次查阅一下源码发现,APP初始化的时候会执行ActivityThread 的main方法:

public static void More ...main(String[] args) {        SamplingProfilerIntegration.start();       // CloseGuard defaults to true and can be quite spammy.  We       // disable it here, but selectively enable it later (via       // StrictMode) on debug builds, but using DropBox, not logs.       CloseGuard.setEnabled(false);       // 初始化应用中需要使用的系统路径       Environment.initForCurrentUser();       // Set the reporter for event logging in libcore       EventLogger.setReporter(new EventLoggingReporter());       //增加一个保存key的provider      Security.addProvider(new AndroidKeyStoreProvider());      // Make sure TrustedCertificateStore looks in the right place for CA certificates            //为应用设置当前用户的CA证书保存的位置        final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());        TrustedCertificateStore.setDefaultUserDirectory(configDir);       //设置进程的名称      Process.setArgV0("
"); Looper.prepareMainLooper(); //创建ActivityThread 对象 ActivityThread thread = new ActivityThread(); thread.attach(false); if (sMainThreadHandler == null) { sMainThreadHandler = thread.getHandler(); } if (false) { Looper.myLooper().setMessageLogging(new LogPrinter(Log.DEBUG, "ActivityThread")); } Looper.loop(); throw new RuntimeException("Main thread loop unexpectedly exited"); }复制代码

首先Looper.prepareMainLooper();是为主线程创建了Looper,然后thread.getHandler();是保存了主线程的Handler,最后Looper.loop();进入消息循环。

3, 查看Handler源码

① 查看Looper.prepare()方法

// sThreadLocal.get() will return null unless you've called prepare().    static final ThreadLocal
sThreadLocal = new ThreadLocal
();/** 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)); } 复制代码

可以看到Looper中有一个ThreadLocal成员变量,熟悉JDK的同学应该知道,当使用ThreadLocal 维护变量时,ThreadLocal为每个使用该成员的线程提供独立的变量副本,所以每一个线程都可以独立地改变自己的副本,而不会影响其他线程所对应的副本。

由此可以看出在每个线程中Looper.prepare() 能且只能调用一次。

继续看Looper对象的构造方法,可以看到在其构造方法中初始化了一个MessageQueue对象:

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

综上小结①:Looper.prepare()方法初始话了一个Looper对象并关联在一个MessageQueue对象,并且一个线程中只有一个Looper对象,只有一个MessageQueue对象。

② 查看Handler对象的构造方法

public Handler(Callback callback, boolean async) {        if (FIND_POTENTIAL_LEAKS) {            final Class
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 that has not called Looper.prepare()"); } mQueue = mLooper.mQueue; mCallback = callback; mAsynchronous = async; }复制代码

可以看出在Handler中的构造方法中,主要初始化了一下变量,并判断Handler对象的初始化不应在内部类,静态类,匿名类中,并且保存了当前线程中的Looper对象。

综上小结② :Looper.prepare()方法初始话了一个Looper对象并关联在一个MessageQueue对象,并且一个线程中只有一个Looper对象,只有一个MessageQueue对象。而Handler的构造方法则在Handler内部维护了当前线程的Looper对象

③查看handler.setMesssage(msg)方法 一般情况下,我们发送异步消息的时候会这样调用:

mHandler.sendMessage(new Message());

通过不断的跟进源代码,其最后会调用:

private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {        msg.target = this;        if (mAsynchronous) {            msg.setAsynchronous(true);        }        return queue.enqueueMessage(msg, uptimeMillis);    }    复制代码

原来msg.target就是Handler对象本身;而这里的queue对象就是我们的Handler内部维护的Looper对象关联的MessageQueue对象。查看messagequeue对象的enqueueMessage方法:

boolean enqueueMessage(Message msg, long when) {        if (msg.target == null) {            throw new IllegalArgumentException("Message must have a target.");        }        if (msg.isInUse()) {            throw new IllegalStateException(msg + " This message is already in use.");        }        synchronized (this) {            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) {                // New head, wake up the event queue if blocked.                msg.next = p;                mMessages = msg;                needWake = mBlocked;            } else {                // Inserted within the middle of the queue.  Usually we don't have to wake                // up the event queue unless there is a barrier at the head of the queue                // and the message is the earliest asynchronous message in the queue.                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; // invariant: p == prev.next                prev.next = msg;            }            // We can assume mPtr != 0 because mQuitting is false.            if (needWake) {                nativeWake(mPtr);            }        }        return true;    }    复制代码

可以看到这里MessageQueue并没有使用列表将所有的Message保存起来,而是使用Message.next保存下一个Message,从而按照时间将所有的Message排序。

④ 查看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.");        }        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();        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            Printer logging = me.mLogging;            if (logging != null) {                logging.println(">>>>> Dispatching to " + msg.target + " " +                        msg.callback + ": " + msg.what);            }            msg.target.dispatchMessage(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();        }    }    复制代码

可以看到Looper.loop()方法里起了一个死循环,不断的判断MessageQueue中的消息是否为空,如果为空则直接return掉,然后执行queue.next()方法:

Message next() {        // Return here if the message loop has already quit and been disposed.        // This can happen if the application tries to restart a looper after quit        // which is not supported.        final long ptr = mPtr;        if (ptr == 0) {            return null;        }        int pendingIdleHandlerCount = -1; // -1 only during first iteration        int nextPollTimeoutMillis = 0;        for (;;) {            if (nextPollTimeoutMillis != 0) {                Binder.flushPendingCommands();            }            nativePollOnce(ptr, nextPollTimeoutMillis);            synchronized (this) {                // Try to retrieve the next message.  Return if found.                final long now = SystemClock.uptimeMillis();                Message prevMsg = null;                Message msg = mMessages;                if (msg != null && msg.target == null) {                    // Stalled by a barrier.  Find the next asynchronous message in the queue.                    do {                        prevMsg = msg;                        msg = msg.next;                    } while (msg != null && !msg.isAsynchronous());                }                if (msg != null) {                    if (now < msg.when) {                        // Next message is not ready.  Set a timeout to wake up when it is ready.                        nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);                    } else {                        // Got a message.                        mBlocked = false;                        if (prevMsg != null) {                            prevMsg.next = msg.next;                        } else {                            mMessages = msg.next;                        }                        msg.next = null;                        if (DEBUG) Log.v(TAG, "Returning message: " + msg);                        msg.markInUse();                        return msg;                    }                } else {                    // No more messages.                    nextPollTimeoutMillis = -1;                }                // Process the quit message now that all pending messages have been handled.                if (mQuitting) {                    dispose();                    return null;                }                // If first time idle, then get the number of idlers to run.                // Idle handles only run if the queue is empty or if the first message                // in the queue (possibly a barrier) is due to be handled in the future.                if (pendingIdleHandlerCount < 0                        && (mMessages == null || now < mMessages.when)) {                    pendingIdleHandlerCount = mIdleHandlers.size();                }                if (pendingIdleHandlerCount <= 0) {                    // No idle handlers to run.  Loop and wait some more.                    mBlocked = true;                    continue;                }                if (mPendingIdleHandlers == null) {                    mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];                }                mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);            }            // Run the idle handlers.            // We only ever reach this code block during the first iteration.            for (int i = 0; i < pendingIdleHandlerCount; i++) {                final IdleHandler idler = mPendingIdleHandlers[i];                mPendingIdleHandlers[i] = null; // release the reference to the handler                boolean keep = false;                try {                    keep = idler.queueIdle();                } catch (Throwable t) {                    Log.wtf(TAG, "IdleHandler threw exception", t);                }                if (!keep) {                    synchronized (this) {                        mIdleHandlers.remove(idler);                    }                }            }            // Reset the idle handler count to 0 so we do not run them again.            pendingIdleHandlerCount = 0;            // While calling an idle handler, a new message could have been delivered            // so go back and look again for a pending message without waiting.            nextPollTimeoutMillis = 0;        }    }    复制代码

可以看到其大概的实现逻辑就是Message的出栈操作,里面可能对线程,并发控制做了一些限制等。获取到栈顶的Message对象之后开始执行:

msg.target.dispatchMessage(msg);

那么 msg.target 是什么呢? 痛过追踪可以知道就是我们定义的Handler对象,然后我们查看一下Handler类的dispatchMessage 方法:

/**     * Handle system messages here.     */    public void dispatchMessage(Message msg) {        if (msg.callback != null) {            handleCallback(msg);        } else {            if (mCallback != null) {                if (mCallback.handleMessage(msg)) {                    return;                }            }            handleMessage(msg);        }    }    复制代码

可以看到,如果我们设置了callback(Runnable对象)的话,则会直接调用handlerCallback方法:

private static void handleCallback(Message message) {        message.callback.run();    }    复制代码

即,如果我们在初始化Handler的时候设置callback (Runnable)对象,则直接调用run方法。 比如我们经常写的runOnUiThread 方法:

runOnUiThread(new Runnable() {            @Override            public void run() {            }        });        复制代码

看起内部实现:

public final void runOnUiThread(Runnable action) {        if (Thread.currentThread() != mUiThread) {            mHandler.post(action);        } else {            action.run();        }    }复制代码

而如果msg.callback为空的话,会直接调用我们的mCallback.handleMessage(msg),即handler的handlerMessage方法。由于Handler对象是在主线程中创建的,所以handler的handlerMessage方法的执行也会在主线程中。

总结

  • 主线程中定义Handler,直接执行:
Handler mHandler = new Handler() {        @Override        public void handleMessage(Message msg) {               super.handleMessage(msg);        }};复制代码

而如果想要在子线程中定义Hanlder,则标准写法为:

// 初始化该线程Looper,MessageQueue,执行且只能执行一次                Looper.prepare();                // 初始化Handler对象,内部关联Looper对象                Handler mHandler = new Handler() {                    @Override                    public void handleMessage(Message msg) {                        super.handleMessage(msg);                    }                };                // 启动消息队列出栈死循环                Looper.loop();                复制代码
  • 一个线程中只存在一个Looper对象,只存在一个MessageQueue对象,可以存在N个Handler对象,Handler对象内部关联了本线程中唯一的Looper对象,Looper对象内部关联着唯一的一个MessageQueue对象。

  • MessageQueue消息队列不是通过列表保存消息列表的,而且通过Message对象next属性关联下一个Message 从而实现列表的功能,同时所有的消息都是按时间来排序的。

  • Android中两个子线程相互交互同样可以通过Handler的异步消息机制实现,可以在线程A中定义Handler对象,而在线程B中获取handler的引用并调用sendMessage方法。

  • activity内部默认存在一个handler的成员变量,Android中的一些其他的异步消息机制实现方法:

Handler的post方法:

mHandler.post(new Runnable() {                    @Override                    public void run() {                    }                });                复制代码

查看其内部实现:

public final boolean post(Runnable r)    {       return  sendMessageDelayed(getPostMessage(r), 0);    }    复制代码

可以发现其内部调用就是sendMessage系列方法。

view的post方法:

public boolean post(Runnable action) {        final AttachInfo attachInfo = mAttachInfo;        if (attachInfo != null) {            return attachInfo.mHandler.post(action);        }        // Assume that post will succeed later        ViewRootImpl.getRunQueue().post(action);        return true;    }复制代码

可以发现其调用的就是activity中默认保存的handler对象的post方法。

activity的runOnUiThread方法:

public final void runOnUiThread(Runnable action) {        if (Thread.currentThread() != mUiThread) {            mHandler.post(action);        } else {            action.run();        }    }复制代码

判断当前线程是否是UI线程,如果不是,则调用handler的post方法,否则直接执行run方法。

转载于:https://juejin.im/post/5ab8bceff265da238e0dbccf

你可能感兴趣的文章
IOS公司开发者账号申请详细教程
查看>>
ubuntu12.04 安装配置 mysql
查看>>
我的友情链接
查看>>
关于两容器倒水问题的感悟(ACM)
查看>>
Scala Pattern Match之Regular Expressions
查看>>
大型网站技术架构(一)大型网站架构演化
查看>>
Java基础学习总结(16)——Java制作证书的工具keytool用法总结
查看>>
ORACLE 绑定变量用法总结
查看>>
ssh证书登录
查看>>
Swoole学习笔记(五):多协议多端口
查看>>
211学院的小胖子钟情好程序员
查看>>
表格操作
查看>>
用python读写excel文件
查看>>
【Enterprise Manager 12c】如何在EM 12c中配置Exadata Infiniband告警邮件
查看>>
盆盆的11年Microsoft MVP心路历程
查看>>
利用kickstart实现pxe自动安装
查看>>
推荐一个spring的demo网站
查看>>
如何利用互联网工具调研网站
查看>>
最新 Hadoop 视频分享
查看>>
3_Shell语言———输入输出重定向和管道概述
查看>>