Android Handler原理解析

Handler主要用于跨线程通信。

一般会在子线程总去执行一些耗时操作,然后使用Handler去通知主线程去做一些更新UI的操作。

弄清楚Handler消息机制需要弄清楚四个非常重要的类:Handler/MessageQueue/Message/Looper。

Handler:消息处理者,负责向消息池中发送消息 (Handler.enqueueMessage) 和处理消息 (Handler.handleMessage) 。
Message:消息,链表结构(最大缓存容量为50),按时间顺序排列。
MessageQueue:消息队列,主要功能是向消息池投递信息 (MessageQueue.enqueueMessage) 和取走消息池的信息 (MessageQueue.next) 。
Looper:消息泵,不断循环执行 (Looper.loop) ,按分发机制将消息分发给目标处理者。

它们之间的关系:
Handler持有looper和MessageQueue对象;Looper持有MessageQueue对象;MessageQueue持有待处理的Message;Message持有Handler对象

消息发送和处理的整个过程:
1.Handler 发送消息,调用MessageQueue的enqueueMessage方法把消息添加到消息队列;2.每个线程里面有个Looper对象,Looper会不停的轮循消息队列的消息,获取到要处理的消息后,会调用msg.target.dispatchMessage(msg),就会回调到Handler的handleMessage方法。

Handler发送消息的几种方式:
handler.sendMessage()、handler.sendMessageAtTime()、handler.sendMessageDelayed()
查看源码可以看到sendMessage和sendMessageDelayed里面调用的都是sendMessageAtTime,只是参数不同。
注意:sendMessageAtTime的参数是:SystemClock.uptimeMillis() + delayMillis,不是System.currentTimeMillis()+delayMillis,SystemClock.uptimeMillis()表示系统开机到当前的时间总数,System.currentTimeMillis()获取的是系统的时间。

	/**
	 @param uptimeMillis 消息具体的发送时间
	*/
    public boolean sendMessageAtTime(@NonNull 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);
    }

sendMessageAtTime调用的enqueueMessage,把消息添加到消息队列

    private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
            long uptimeMillis) {
        msg.target = this;
        msg.workSourceUid = ThreadLocalWorkSource.getUid();

        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }

下面是MessageQueue中enqueueMessage方法,看注释部分

   boolean enqueueMessage(Message msg, long when) {
        if (msg.target == null) {//msg 绑定的Handler为空
            throw new IllegalArgumentException("Message must have a target.");
        }
        if (msg.isInUse()) {//msg 在使用中,不允许重复添加
            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;//mMessages是当前MessageQueue队首
            boolean needWake;
            //空消息队列、发送时间为0或者小于第一条消息,则把该消息放在队首
            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;
                    //如果当前消息为null,或者当前消息时间大于新消息的时间
                    if (p == null || when < p.when) {
                        break;
                    }
                    // 因为消息队列之前还剩余有消息,所以这里不用调用nativeWake
                    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,以触发nativePollOnce函数结束等待
                nativeWake(mPtr);
            }
        }
        return true;
    }

下面我们再来了解一下,Looper是怎么样来轮循消息的,看注释部分

    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
            //只有当消息队列退出(调用quit方法)时,msg对象才会为空,这时就会退出轮循
            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);
            }

            final long slowDispatchThresholdMs = me.mSlowDispatchThresholdMs;

            final long traceTag = me.mTraceTag;
            if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
                Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
            }
            final long start = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
            final long end;
            try {
            	//处理消息,就是调用Handler的dispatchMessage方法,dispatchMessage里面就会调用我们创建Handler时重写的handleMessage方法,但也不一定调用,可以自己去看下这个方法的源码
                msg.target.dispatchMessage(msg);
                end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
            } finally {
                if (traceTag != 0) {
                    Trace.traceEnd(traceTag);
                }
            }
            if (slowDispatchThresholdMs > 0) {
                final long time = end - start;
                if (time > slowDispatchThresholdMs) {
                    Slog.w(TAG, "Dispatch took " + time + "ms on "
                            + Thread.currentThread().getName() + ", h=" +
                            msg.target + " cb=" + msg.callback + " msg=" + msg.what);
                }
            }

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

上面涉及到MessageQueue的一个重要的next方法,我们看下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;//是nativeInit()方法返回的一个值,是native层对象指针,为 0 时表示MessageQueue退出
        if (ptr == 0) {
            return null;
        }

        int pendingIdleHandlerCount = -1; // -1 only during first iteration
        int nextPollTimeoutMillis = 0;//阻塞时长
        //死循环
        for (;;) {
            if (nextPollTimeoutMillis != 0) {
                Binder.flushPendingCommands();
            }
            //阻塞线程,第一次nextPollTimeoutMillis为0,不会阻塞
            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.
                        //直接取出,并把mMessages指向下一条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.
                    //队列中没有消息,会一直阻塞,直到新有消息进来,可以看enqueueMessage方法,上面有说明
                    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;
        }
    }

上面是整个消息的发送和处理的过程,下面我们我再了解下Looper是怎么创建的,这个有助于理解,是怎么做到在子线程发送消息,在主线程处理消息的

    public static void main(String[] args) {
        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");

        // 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();

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

下面我们看下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();
        }
    }

	/**
	@param quitAllowed 是否允许退出,主线程是不允许退出,所以prepareMainLooper会传一个false
	*/
 	private static void prepare(boolean quitAllowed) {
 		//一个线程只允许有一个Looper,否则会抛异常
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        //创建一个Looper对象,保存到sThreadLocal中
        //ThreadLocal相当于一个容器,存储着每个线程的数据,且所有线程都共享这一个ThreadLocal变量,但每个线程访问该对象时会获得不同的数据,而且就算修改这些数据也不会影响到其他线程,即所有线程互不干扰,详细信息自行查找。
        sThreadLocal.set(new Looper(quitAllowed));
    }
    /**
    Looper的构造方法中保存了Looper创建的线程
    */
	private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);
        mThread = Thread.currentThread();
    }

根据上面表述,我们知道了主线程自动调用了prepareMainLooper方法来创建Looper,所以如果我们要在子线程使用Looper就需要我们手动创建。

问题点:
Handler使用不当会造成内存泄漏,原因是新开启的线程是会持有Handler引用的,如果在Activity等中创建Handler,并且是非静态内部类的形式,Handler会持有activity的引用,如果消息没处理完,就有可能造成内存泄漏。

解决办法:
(1). 使用静态内部类+弱引用的方式:

private Handler sHandler = new TestHandler(this);

static class TestHandler extends Handler {
    private WeakReference<Activity> mActivity;
    TestHandler(Activity activity) {
        mActivity = new WeakReference<>(activity);
    }

    @Override
    public void handleMessage(Message msg) {
        super.handleMessage(msg);
        Activity activity = mActivity.get();
        if (activity != null) {
            //TODO:
        }
    }
}

(2). 在外部类对象被销毁时,将MessageQueue中的消息清空。例如,在Activity的onDestroy时将消息清空。

@Override
protected void onDestroy() {
    handler.removeCallbacksAndMessages(null);
    super.onDestroy();
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值