欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

安卓handler消息机制源码解析

程序员文章站 2022-07-14 15:10:18
...

    安卓应用启动时,默认创建UI线程(主线程),默认应用所有操作都放在主线程中执行,但为了保证系统的流畅性,通常把耗时的操作放入子线程中执行,例如网络访问、IO操作,如果在主线程中执行耗时长的操作很有可能出现ANR错误。本文就安卓中的handler消息机制进行源码上的简单解析。

    handler消息机制涉及到四个元素,handler、Message、looper、MessageQueue。简言之,handler发送Message到MessageQueue中,looper不断从MessageQueue提取消息到对应的handleMessage代码处理。那么他们内部之间协调是怎么代码实现的?

    从handler构造函数开始看起。

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.w(TAG, "The following Handler class should be static or leaks might occur: " +
                    klass.getCanonicalName());
            }
        }

        mLooper = Looper.myLooper();//获取looper对象
        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread that has not called Looper.prepare()");
        }
        mQueue = mLooper.mQueue;//获取MeaasgeQueue
        mCallback = callback;
        mAsynchronous = async;
    }

    可以看到在handler构造方法中调用Looper.myLooper()方法获取到了looper对象与之关联,而获取MessageQueue对象则是通过mLooper.Queue方法获得对象关联。请看looper源码:

/**
     * 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
    }

    handler从myLooper方法中获得looper对象,从myLooper方法中可以看到是从sThreadLocal(ThreadLocal用于不同线程间互不干扰地存储并提供数据)中获得looper对象。那么线程和looper又是怎样绑定?

/**
     * Initialize the current thread as a looper, marking it as an
     * application's main looper. The main looper for your application
     * is created by the Android environment, so you should never need
     * to call this function yourself.  See also: {@link #prepare()}
     */
    public static void prepareMainLooper() {
        prepare(false);
        synchronized (Looper.class) {
            if (sMainLooper != null) {
                throw new IllegalStateException("The main Looper has already been prepared.");
            }
            sMainLooper = myLooper();
        }
    }
    上述源码在UI线程创建时也会被调用,调用如下:

// Make sure TrustedCertificateStore looks in the right place for CA certificates
        final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());
        TrustedCertificateStore.setDefaultUserDirectory(configDir);

        Process.setArgV0("<pre-initialized>");

        Looper.prepareMainLooper();//创建主线程的looper

        ActivityThread thread = new ActivityThread();
        thread.attach(false);

    在执行prepareMainLooper方法首先会执行prepare(false)方法,看其源码:

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
    }

    此时可以发现:looper与当前的线程关联,而handler又与looper关联。前文中可以看到handler获取MessageQueue对象时是从looper中获取的,那么MessageQueue对象何时与looper关联?

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

    从looper构造方法中可以看到,looper构造时创建了一个MessageQueue对象,所以MessageQueue对象是存在于looper中的。

    此时,可以总结关系:handler与looper关联,MessageQueue创建于looper中,looper与线程关联,handler与MessageQueue通过looper关联,handler与线程关联。此时可以解释为什么handler需要创建在UI线程中?因为handler需要与UI线程关联,这样handleMessage方法才能执行在UI线程中,UI更新才能线程安全。至此内部关联关系已了解。那么消息机制又是如何运转的?

public static void main(String[] args) {
        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");
        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());

        // Make sure TrustedCertificateStore looks in the right place for CA certificates
        final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());
        TrustedCertificateStore.setDefaultUserDirectory(configDir);

        Process.setArgV0("<pre-initialized>");

        Looper.prepareMainLooper();//创建looper

        ActivityThread thread = new ActivityThread();
        thread.attach(false);

        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.loop();//执行消息循环

        throw new RuntimeException("Main thread loop unexpectedly exited");
    }

    上述为UI现场main方法可以看到在创建looper后调用Looper.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(); // 从消息队列中获取消息
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                return;
            }

  	   //省略部分源码
            try {
                msg.target.dispatchMessage(msg);//分发消息
            } finally {
                if (traceTag != 0) {
                    Trace.traceEnd(traceTag);
                }
            }

            if (logging != null) {
                logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
            }            
           //省略部分源码
 msg.recycleUnchecked();//message复用 } }

    从上述代码可以看出从looper中获取到消息队列后建立死循环不断从队列中读取messsge通过dispatchMeaasge方法分发处理,处理完成后通过recycleUnchecked方法回收message对象。其中对于消息处理的关键代码是:msg.target.dispatchMessage(msg);//分发消息。可以看一下message源码:

Handler target;
    
Runnable callback;

Message next;

    message对象内部创建了一个handler类型的target对象(为什么创建handler稍后解释),再调用其的dispatchMessage方法。

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

    从dispatchMessage源码中可以看到message自带的runnable类型的callback若不为空则调用handleCallback方法,如下:

private static void handleCallback(Message message) {
        message.callback.run();//直接在子线程中执行
    }

    何时runnable不为空?当我们使用handler去post一个runnable时,例如:

handler.post(new Runnable() {
            @Override
            public void run() {
                
            }
        });

    看一下一步一步实现的源码:

public final boolean post(Runnable r)
    {
       return  sendMessageDelayed(getPostMessage(r), 0);
    }
private static Message getPostMessage(Runnable r) {//包装为message对象
        Message m = Message.obtain();
        m.callback = r;
        return m;
    }
public final boolean sendMessageDelayed(Message msg, long delayMillis)
    {
        if (delayMillis < 0) {
            delayMillis = 0;
        }
        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
    }
public boolean sendMessageAtTime(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);
    }
 private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        msg.target = this;//message中的handler与当前handler关联
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);//加入消息队列
    }

    从上述代码中可以看到post一个runnable对象的时候也会将其包装为一个message对象并加入到消息队列中。而之前遇到的在message中创建handler对象在最后一个函数中与当前的handler关联,所以message在被handler发送出去后最终绕了一圈还是与当前handler关联处理。

    而如果是普通非runnable的message的话则直接调用:

if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);

    mCallback是一个回传接口,handleMessage是其中的方法:

public interface Callback {
        public boolean handleMessage(Message msg);
    }

    我们在创建handler时重写了的handleMessage中的代码此时被执行。举例如下:

public void handleMessage(Message msg) {
                switch (msg.what) {
                    case SHOW_PROGRESS:
                        showProgressBar();
                        break;
                    case HIDE_PROGRESS:
                        hideProgressBar();
                        break;
                    case UPDATE_DIALOG:
                        customDialogAssociateDetail.initView();//更新dialog
                    default:
                        break;
                }
                super.handleMessage(msg);
            }

    至此,源码解析完成。总结,handler不断产生message添加到messageQueue中,looper不断从messageQueue中取出message给handler dispatch分发后进行处理。

    特记下,以备后日回顾。