Handler源码分析(从实践入手)

一:概述

关于Android异步处理:

常用异步处理

  • runOnUiThread
  • AsyncTask
  • Handler
  • HandlerThread
  • IntentService

这次blog主要分析Handler,关于其他的部分会在以后的blog中进行分析,Handler是异步消息处理机制的基础,并且其他异步消息处理也与Handler有着莫大的联系。(我的源码分析为参考相关资料,自己实际源码调试检测,从实际调试的过程中去深入理解,希望大家有实际调试经验或者技巧,可以共同分享,望共勉)网上关于源码分析的文章数不胜数,但是我没有发现过文章是从实际调试过程中去引导我们去学习源码(个人认为只读资料这种源码学习方式很被动),所以我就以个人源码调试的过程为记录,与大家共同学习源码。

 

二:分析

此处默认读者已经会Handler的基本使用方法,所以就不再于此赘述Handler的基本用法。但是接下来我会先给出Handler执行流程的执行流程图,让大家先对整体的过程中涉及到的对象以及相关方法有一定的了解:

考虑再三,还是决定附一段代码截图,因为此代码可以让大家更好的参照流程图理解(代码很简单,主要是看实现):

主要的实现就是这两个,主要知道我怎么做的就好,流程图中的内容会参考此部分。

先给大家看一张整体的流程图,从onClick触发器,到handleMessage执行,到最后的Toast得到处理的全过程。 

那么,接下来就看具体的调试的流程截图了:

上图就是onClick执行,所以接下来会涉及到消息的封装和发送。

如上图,Bundle就是在onClick中的实现,所以接下来看Messgae的截图:

如上图,这就是Message的实现过程,最后setData()包装了Bundle对象。

如上图,在onClick最后,我们向handler发送了消息,所以此处调用sendMessage(),还许哟啊注意enqueueMessage方法的调用。

上图调用的是MessageQueue的next方法,留下一个疑问,MessageQueue是什么时候启动的呢?怎么此处直接调用了其next()方法,难搞哦,继续流程图:

这就涉及到了Handler的dispatchMessgae方法,和实现Handler并重写其handleMessage()方法实现自定义逻辑(我在这里用了一个Toast的显示作为handleMessage的处理,如上图的红框,我们就可以知道handleMessage方法得到执行)

但是可能都会有一个疑问,为什么没有Looper相关的东西,Handler不是还有其他三个兄弟吗?Message, MessageQueue, Looper。我们为什么看流程图的时候没有出现Looper,请看下图:

你可能会好奇,怎么Looper实在ActivityThread之后就执行loop方法了,还有MessageQueue也被涉及了,不过此处只需要知道Looper不是没有启动,只是在程序启动时已经得到执行了(相信分析过Looper相关内容的同志,可以知道此处的逻辑了),Looper的作用在此流程图中没有很好体现,在分析部分会做说明。知道了整体的执行流程,接下来我们先从概念入手,先看以下Handler比较概念性的东西。

Handler

首先,对于异步消息的四部分先做简要说明:

Message:是在线程之间传递的消息,它可以携带少量的信息,用于在不同线程之间交换数据

Handler:主要用于发送和处理消息,常用方法sendMessage()和post()

MessageQueue:消息队列,主要用于存放所有通过Handler发送的消息,这部分消息会一直存放在消息队列中,等待被处理,每个线程只能有一个此对象

Looper:Looper相当于MessageQueue的管理者,调用其loop()方法后,就会进入到无限循环中,只要有消息存在,就会将它转发到Handler。同样,每个线程一直会有一个Looper对象。

下面就有一个简单的示例进入分析:


//这里只有主要用到的代码,但是希望大家对基本使用已经熟练
private Handler mhandler = new  Handler(){
                
                @Override
                public void handleMessage(Message msg) {
                        ...// 执行的UI操作
                    }
            };

 
    ......

    Message msg = Message.obtain();
    msg.what = 1;
    msg.obj = "string";
  
    ......

   mHandler.sendMessage(msg);

在正式开始分析之前,我想以Message的源码先作为热身,这是整个Handler分析中的消息载体,而且会是不是的就用到它,所以就以它作为铺垫:

    /*package*/ int flags;

    /*package*/ long when;

    /*package*/ Bundle data;

    /*package*/ Handler target;

    /*package*/ Runnable callback;

    // sometimes we store linked lists of these things
    /*package*/ Message next;


    /** @hide */
    public static final Object sPoolSync = new Object();
    private static Message sPool;
    private static int sPoolSize = 0;

    private static final int MAX_POOL_SIZE = 50;

    private static boolean gCheckRecycle = true;

    ......

    public static Message obtain() {
        synchronized (sPoolSync) {
            if (sPool != null) {
                Message m = sPool;
                sPool = m.next;
                m.next = null;
                m.flags = 0; // clear in-use flag
                sPoolSize--;
                return m;
            }
        }
        return new Message();
    }

    public static Message obtain(Message orig) {
        Message m = obtain();
        m.what = orig.what;
        m.arg1 = orig.arg1;
        m.arg2 = orig.arg2;
        m.obj = orig.obj;
        m.replyTo = orig.replyTo;
        m.sendingUid = orig.sendingUid;
        m.workSourceUid = orig.workSourceUid;
        if (orig.data != null) {
            m.data = new Bundle(orig.data);
        }
        m.target = orig.target;
        m.callback = orig.callback;

        return m;
    }

    public static Message obtain(Handler h) {
        Message m = obtain();
        m.target = h;

        return m;
    }

    public static Message obtain(Handler h, Runnable callback) {
        Message m = obtain();
        m.target = h;
        m.callback = callback;

        return m;
    }

    public static Message obtain(Handler h, int what) {
        Message m = obtain();
        m.target = h;
        m.what = what;

        return m;
    }

    public static Message obtain(Handler h, int what, Object obj) {
        Message m = obtain();
        m.target = h;
        m.what = what;
        m.obj = obj;

        return m;
    }

    public static Message obtain(Handler h, int what, int arg1, int arg2) {
        Message m = obtain();
        m.target = h;
        m.what = what;
        m.arg1 = arg1;
        m.arg2 = arg2;

        return m;
    }

   
    public static Message obtain(Handler h, int what,
            int arg1, int arg2, Object obj) {
        Message m = obtain();
        m.target = h;
        m.what = what;
        m.arg1 = arg1;
        m.arg2 = arg2;
        m.obj = obj;

        return m;
    }

从以上Message的源码中,我们看见了频繁使用的参数,当然还有一个next引用,相信大家已经明了它的作用,对于target指向的也是一个Handler对象,其作用会在以后的源码分析中看到。继续看,Message内部维护了一个消息池sPoolSync,使用obtaion()则直接从池内获取,由此我们知道尽量可以使用obtain()方法创建Message对象,避免new重新分配内存。然后,若池内无消息对象可复用,则会调用new关键字创建,Message的分析就简单的到这。

然后,我们可以看到一个内名内部类方式创建的Handler类对象,所以就看一下Handler的几个构造方法:

public Handler() {
        this(null, false);
    }

   
    public Handler(Callback callback) {
        this(callback, false);
    }

    
    public Handler(Looper looper) {
        this(looper, null, false);
    }

    public Handler(boolean async) {
        this(null, async);
    }

    public Handler(Looper looper, Callback callback) {
        this(looper, callback, false);
    }

    //请读者主要注意此构造方法    
    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());
            }
        }
        
        //关联当前线程的Looper
        mLooper = Looper.myLooper();
        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread " + Thread.currentThread()
                        + " that has not called Looper.prepare()");
        }

        //关联当前线程的MessageQueue
        mQueue = mLooper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }

    
    public Handler(Looper looper, Callback callback, boolean async) {
        mLooper = looper;
        mQueue = looper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }

从以上源码可以找到我们调用的构造方法最后的执行者是Handler(Callback callback, boolean async),它的内部自动关联了当前线程的Looper对象和MessageQueue对象,所以实现了绑定实现创建Handler对象的操作线程。但是Looper和MessageQueue是在构造方法里就关联的,那么他们又是在何时创建的呢?刚好也对应了我们流程图中的问题,我们发现在最后一张流程图实在ActivityThrad.main执行后就有了Looper和MessageQueue的影子,所以我们就先来看ActivityThread.main()的源码:


    // 在Android应用进程启动时,会默认创建1个主线程(ActivityThread,也叫UI线程)
    // 创建时,会自动调用ActivityThread的1个静态的main()方法 = 应用程序的入口
        public static void main(String[] args) {
            
            ... ...

            Looper.prepareMainLooper(); 
            /* 作用:为 主线程(UI线程) 创建1个循环器对象(Looper),同时也生成了1个消息队列对象(MessageQueue),该方法在主线程(UI线程)创建时自动调用*/        

            ActivityThread thread = new ActivityThread(); 
   
            Looper.loop(); 
        }

我们看到此处执行了Looper的一个prepareMainLooper(),看其名字我们大概能猜出其作用,为了验证,再看下面源码:


        //Looper的prepareMainLooper()
        public static void prepareMainLooper() {
        prepare(false);
        synchronized (Looper.class) {
            if (sMainLooper != null) {
                throw new IllegalStateException("The main Looper has already been prepared.");
            }
            sMainLooper = myLooper();
        }
    }

        //prepareMainLooper()中调用prepare()
        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.set(new Looper(quitAllowed))调用,关联了一个MessageQueue对象
        private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);
        mThread = Thread.currentThread();
    }


当创建主线程时,会自动调用ActivityThread的静态main();而main()内则会调用Looper.prepareMainLooper()为主线程生成1个Looper对象,然后通过ThreadLocal来保存这个Looper(ThreadLocal是一个线程级的单例),同时Looper在创建的时候创建了一个MessageQueue对象,通过Looper中的一个final成员变量保存起来,这样就保证了一个线程中只能有一个MessageQueue。到了这里,我们就能知道Looper和MessageQueue的创建时机了。因为Looper是MQ的管理者,那么我们就先来看被管理者的实现,然后再看管理者管理的实现。

所以呢,接下来分析MessageQueue:

public final class MessageQueue {

    ......

    Message mMessages;
    private final ArrayList<IdleHandler> mIdleHandlers = new ArrayList<IdleHandler>();
    private SparseArray<FileDescriptorRecord> mFileDescriptorRecords;
    private IdleHandler[] mPendingIdleHandlers;
    private boolean mQuitting;

   ......
    MessageQueue(boolean quitAllowed) {
        mQuitAllowed = quitAllowed;
        mPtr = nativeInit();
    }

   .......
  
    Message next() {
        
        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;
        }
    }

    void quit(boolean safe) {
        if (!mQuitAllowed) {
            throw new IllegalStateException("Main thread not allowed to quit.");
        }

        synchronized (this) {
            if (mQuitting) {
                return;
            }
            mQuitting = true;

            if (safe) {
                removeAllFutureMessagesLocked();
            } else {
                removeAllMessagesLocked();
            }

            // We can assume mPtr != 0 because mQuitting was previously false.
            nativeWake(mPtr);
        }
    }


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

    ......
}

这里我们主要看他的next(),enqueueMessage()方法(quit()方法就是经过判断各个参数,然后安全的退出MQ),每次使用Handler发送一个Message的时候,最终会先调用MessageQueue的enqueueMessage方法将Message方法放入到MessageQueue里面(我们在流程图中也看到当调用sendMessage后,就会调用enqueueMessage方法,忘记的可以回看以下流程图)。对比分析,它的执行过程由以上源码可知:

  1.  进入此方法首先就会进行合法性的判断(target,isInUse,mQuitting),然后进行第二步操作。
  2. 若队列为空 / 当前处理的时间点为0(when的数值表示Message将要执行的时间点)/ 当前Message需要处理的时间点先于队列中的首节点,则将Message放入队列首部,否则进行第3步。
  3.  遍历队列中Message,找到when比当前Message的when大的Message,将Message插入到该Message之前,如果没找到则将Message插入到队列最后。
  4. 判断是否需要唤醒(needwake),一般是当前队列为空的情况下,next()会进入睡眠,需要enqueue唤醒next()。
  5. 执行完后,会释放持有的MessageQueue.this的锁。

关于next()方法,这里首先做铺垫,因为在Looper的分析中也会用到此方法。next()方法的执行过程如下:

  1.  初始化,如果mPtr为null,则直接返回null,设置nextPollTimeoutMillis为0。
  2. 调用nativePollOnce, nativePollOnce有两个参数,第一个为mPtr表示native层MessageQueue的指针,nextPollTimeoutMillis表示超时返回时间,调用这个nativePollOnce会等待wake,如果超过nextPollTimeoutMillis时间,则不管有没有被唤醒都会返回。(具体的native层代码需要有能力的大佬去分析)
  3. 获取队列的头Message(msg),如果头Message的target为null,则查找一个异步Message来进行下一步处理。当队列中添加了同步Barrier的时候target会为null。
  4. 判断上一步获取的msg是否为null,为null说明当前队列中没有msg,设置等待时间nextPollTimeoutMillis为-1(实际上是等待enqueueMessage的nativeWake来唤醒,执行此步骤)如果非null,则执行下一步。
  5. 判断msg的when是否比now大,如果小,则将msg从队列中移除,并且返回msg,结束。如果大则设置等待时间nextPollTimeoutMillis为(int) Math.min(msg.when - now, Integer.MAX_VALUE),执行时间与当前时间的差与MAX_VALUE的较小值。
  6.  判断是否MessageQueue是否已经取消,如果取消的话则返回null,否则执行下一步 。
  7.  运行idle Handle,idle表示当前有空闲时间的时候执行,而运行到这一步的时候,表示消息队列处理已经是出于空闲时间了。如果没有idle,则执行步骤2,如果有则执行idleHandler的queueIdle方法,执行完毕后,回到步骤2.

分析完MessageQueue,我们就该分析管理MQ的Looper了(知道了MQ,可以更容易分析Looper),在上面分析ActivityThread的时候,我们就知道Looper和MQ的创建,以及Looper在其构造方法中关联MQ的过程,最后在main()中执行了一个Looper.loop()方法,所以我们应该看loop方法到底做了什么,其源码如下:

......

public static void loop() {
        final Looper me = myLooper();

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

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

            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)) {
                        slowDeliveryDetected = true;
                    }
                }
            }
            

            msg.recycleUnchecked();
        }
    }

现在开始分析,当此方法获取到Looper和消息队列对象,然后通过无限for循环,从消息队列中取出消息(next()方法),并通过

msg.target.dispatchMessage(msg);

将消息派发到对应的目标Handler(通过把消息派发给target(target实际是一个handler对象)),最后完成消息资源的释放。所以接下来一个重要的方法,dispatchMessage(msg):

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

观察源码,如果Callback为空,则执行的是handleMessage(msg),若不为空,则调用handleCallback(msg);

此处进行说明:

  • msg.callback属性不为空,则代表使用了post(Runnable r)发送消息,则直接回调Runnable对象里复写的run()
  • msg.callback属性为空,则代表使用了sendMessage(Message msg)发送消息,则回调复写的handleMessage(msg)

最后通过自定义handleMessage()方法,完成对消息的处理。

所以读者对于post和sendMessage的使用有了更深的认识。(post()方法内部与sendMessage()原理雷同)

现在我们知道了MQ, Looper,Message,Handler的handleMessage也有我们自己重写,那么还剩下什么呢?没错,就是发送消息了。所以,现在分析sendMessage(msg)方法,他在工作线程(自己创建的子线程)发送消息到消息队列:

 

public final boolean sendMessage(Message msg)
    {
        return sendMessageDelayed(msg, 0);
    }

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;
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }

boolean enqueueMessage(Message msg, long when) {

                ......

                synchronized (this) {

                    msg.markInUse();
                    msg.when = when;
                    Message p = mMessages;
                    boolean needWake;

                        if (p == null || when == 0 || when < p.when) {
                            msg.next = p;
                            mMessages = msg;
                            needWake = mBlocked;
                        } else {
                            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; 
                            prev.next = msg;
                        }

                        if (needWake) {
                            nativeWake(mPtr);
                        }
                    }
                    return true;
            }

相信大家可以从以上的源码中看出,经过层层调用,最后交给了enqueueMessage()方法执行(此是MessageQueue的方法)。【注意:在enqueueMessage方法调用的:

msg.target = this;

在此之前分析的Looper的loop()消息循坏时,从消息队列中取出每个消息的target,并去执行

msg.target.dispatchMessage(msg)

最重要的关于MessageQueue的方法enqueueMessage,内部用一个队列来保存消息。之后,随着Looper对象的无限循环,不断从消息队列中取出Handler发送的消息,并发布到相应的Handler,最终回调Handler.handMessage()处理消息。

三:总结与归纳

虽然直到了基础的源码,也基本了解了Handler的执行过程。但是我们对于Handler的常见问题就会作答了吗?以下是我在平时遇到的问题,与大家分享(关于Handler还有很多很多的问题,请大家自行百度):

  • 为什么不能在子线程中更新UI?

首先,对于这个问题,我们必须要知道子线程中是可以更新UI的

测试代码:

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main_activity);
    tv_thread = findViewById(R.id.tv_thread);
    new Thread(new Runnable() {
    @Override
        tv_thread.setText("子线程加载");
    }).start();
}

这段代码读者可以自行测试,同时,在测试时让其休眠一段时间,再观察结果,我们就可以知道异同(读者请自行测试)。

当应用程序启动时,mThread(MainThread / UIThread)就被初始化了,当我们访问UI的时候,ViewRoot会检查是哪个线程在访问UI,若不是主线程,则会抛出异常;但为什么在onCreate()方法中就可以访问呢?那是ViewRootImpl的创建在onResume()方法回调之后。所以在onCreate()方法中进行访问时,它还没来的及创建。

那问题来了,为什么UI更新一定要在UI线程里实现呢?

目的在于提高移动端更新UI的效率和和安全性。

原因是:

Android的UI访问是没有加锁的,多个线程可以同时访问更新操作同一个UI控件。也就是说访问UI的时候,android系统当中的控件都不是线程安全的,这将导致在多线程模式下,当多个线程共同访问更新操作同一个UI控件时容易发生不可控的错误,而这是致命的。所以Android中规定只能在UI线程中访问UI,这相当于从另一个角度给Android的UI访问加上一个伪锁。

  • 在子线程中创建Handler报错是为什么?

这个问题在于直接在子线程中创建了Handler对象,而没有在子线程中创建一个Looper对象。这句话已经给出了答案,

原因如下:

handler构造函数中 需要持有一个looper的对象,如果没有,提示这个错误;
looper的主要作用是与当前线程绑定,保证一个线程只会有一个Looper实例,同时一个Looper实例也只有一个MessageQueue。然后looper的loop()方法就是不断从MessageQueue中取出消息,交给handler去发送消息。

解决方法:

只需要调用prepare()方法,新建looper对象

当然,这只是这个问题的浅显回答,关于更深入的理解,我们还得多理解源码。

  • 如何在子线程创建Looper?

上一题中已经有答案,那就是调用Looper的prepare()方法

  • Handler.post的逻辑在哪个线程执行的,是由Looper所在线程还是Handler所在线程决定的?

从源码分析中,我们可以知道是由Looper所在的线程决定(在Looper.loop()方法中,从MsgQueue中拿出msg,并且执行其逻辑)

  • Looper和Handler一定要处于一个线程吗?子线程中可以用MainLooper去创建Handler吗?

  • 可以,子线程中Handler handler = new Handler(Looper.getMainLooper());,此时两者就不在一个线程中。

  • Handler的post方法发送的是同步消息吗?可以发送异步消息吗?

用户层发送的都是同步消息,不能发送异步消息;异步消息只能由系统发送。

  • 主线程如何向子线程发送消息?

自己创建继承Thread的子线程,内部实现一个handler,并将Looper启动起来(子线程默认不会启动Looper),然后主线程调用子线程的Handler对象发送消息。

具体的代码请读者自行实现。

  • Looper.loop()是如何阻塞的?MessageQueue.next()是如何阻塞的?

请读者自行看源码。

  • Handler的内存泄漏问题?

内存泄漏:

Java使用有向图机制,通过GC自动检查内存中的对象,如果GC发现一个或一组对象为不可到达状态,则将该对象从内存中回收。(一个对象不被任何引用所指向,则该对象会在被GC发现的时候被回收);同样,如果一组对象中只包含互相的引用,而没有来自它们外部的引用,仍然属于不可到达,同样会被GC回收。
Handler mHandler = new Handler() {
    @Override
    public void handleMessage(Message msg) {
        //逻辑代码
        ......
    }
}

如上代码所示,当使用内部类(包括匿名类)来创建Handler的时候,Handler对象会隐式地持有一个外部类对象(通常是Activity)的引用,而Handler通常会伴随着一个耗时的后台线程(eg:从网络下载图片)任务,这个后台线程在任务执行完毕之后,通过消息机制通知Handler,然后Handler把图片更新到界面。此时,如果用户在网络请求过程中关闭了Activity,正常情况下,Activity不再被使用,它就有可能在GC检查时被回收掉,但由于这时线程尚未执行完,而该线程持有Handler的引用,这个Handler又持有Activity的引用,就导致该Activity无法被回收(内存泄露),直到网络请求结束。

内存泄漏后果:

VM占用内存过高,导致了OOM,程序报错。

解决方法;

  1. 在关闭Activity的时候停掉后台线程
  2. 使用相应的Handler的removeCallbacks()方法,把消息对象从消息队列移除(此方法适用于Handler被delay的Message持有引用)
  • 为什么Android程序中的Looper.loop()不会造成ANR异常?

关于这个老生常谈的问题,此处我也因水平有限,只能给出浅显的回答。大家请自行深入学习理解,共勉。

首先:关于引发ANR异常的原因:

  1. 当前事件没有机会得到处理(MainThread正在处理前一个事件,没有及时完成或looper被某种原因阻塞)
  2. 当前事件正在处理,但没有及时完成

Android是由事件驱动的,Looper.loop()不断接受事件,处理事件,每一个点击触摸 / Activity的lifecycle 欧式在loop()的控制之下,如果它停止,则应用也会随着停止。所以换句话说,是某一个消息 / 对消息的处理 阻塞了loop(),而不是loop()阻塞它。(我们的代码实际会在这个循环里去执行)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值