安卓handler消息机制源码解析

    安卓应用启动时,默认创建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分发后进行处理。

    特记下,以备后日回顾。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值