Handler机制

我觉得我有必要讲一下Handler机制这个问题,因为经常面试的同学都知道Handler机制是面试官最喜欢问的问题。刚毕业那两年就经常栽倒在这个问题,所以有必要对Handler机制有深刻的了解。下面我将详细讲解Handler机制。

我们都知道Android的第一个程序是init进程,这个程序是一个用c写的应用程序,它通过rc脚本启动java的第一个程序Zygote(ZygoteInit.java)进程。

Zygote进程又名孵化器,它创建着Android系统的大部分服务和一些重要进程,其中ActivityThread类就是在这个进程实例化并运行的。

下面我们将讲一下ActivityThread.java的main函数:

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

        // Find the value for {@link #PROC_START_SEQ_IDENT} if provided on the command line.
        // It will be in the format "seq=114"
        long startSeq = 0;
        if (args != null) {
            for (int i = args.length - 1; i >= 0; --i) {
                if (args[i] != null && args[i].startsWith(PROC_START_SEQ_IDENT)) {
                    startSeq = Long.parseLong(
                            args[i].substring(PROC_START_SEQ_IDENT.length()));
                }
            }
        }
        ActivityThread thread = new ActivityThread();
        thread.attach(false, startSeq);

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

我们可以看到上面的代码中有一行

Looper.prepareMainLooper();

想要知道这行代码是什么意思,就只能先看看这个方法的内容

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

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

从上面prepareMainLooper分析,先执行了prepare(false)方法,从这个方法上

看就是Looper如何已经存在那么就抛出异常终止运行。而sThreadLocal.get()

方法是怎么实现的呢?那就让我我们来看看,ThreadLocal是什么

public T get() {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null) {
            ThreadLocalMap.Entry e = map.getEntry(this);
            if (e != null) {
                @SuppressWarnings("unchecked")
                T result = (T)e.value;
                return result;
            }
        }
        return setInitialValue();
}

 ThreadLocalMap getMap(Thread t) {
        return t.threadLocals;
    }
 

上面的代码就是ThreadLocal<T>类中的get、getMap方法,从代码上分析意

思是:获取当前线程的threadLocals属性,这个属性是ThreadLocalMap类

的对象,下面看看这个对象是什么

这个对象也就是包含了一个Entry[]数组,这个Entry的父类是弱引用类

 

获取到e对象后,就返回它的value值,也就是上面说的Looper()对象,

经过上面的分析我们得出总结:

从当前线程中的数组获取Looper对象,这个下标是sThreadLocal对象

的哈希码。然后因为sThreadLocal是一个静态属性,所以下标是固定的。

既然下标是固定的,那么Looper保存到数组里,就表明数组里只有

一个Looper对象,最后的结论是一个线程只有一个Looper对象。

第一阶段将完了,开始下一个阶段

 

其他的我们就不讲了,因为我们只讲跟Handler机制有关的,跟Handler

机制有关的还有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();

        // Allow overriding a threshold with a system prop. e.g.
        // adb shell 'setprop log.looper.1000.main.slow 1 && stop && start'
        final int thresholdOverride =
                SystemProperties.getInt("log.looper."
                        + Process.myUid() + "."
                        + Thread.currentThread().getName()
                        + ".slow", 0);

        boolean slowDeliveryDetected = false;

        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
            final Printer logging = me.mLogging;
            if (logging != null) {
                logging.println(">>>>> Dispatching to " + msg.target + " " +
                        msg.callback + ": " + msg.what);
            }

            final long traceTag = me.mTraceTag;
            long slowDispatchThresholdMs = me.mSlowDispatchThresholdMs;
            long slowDeliveryThresholdMs = me.mSlowDeliveryThresholdMs;
            if (thresholdOverride > 0) {
                slowDispatchThresholdMs = thresholdOverride;
                slowDeliveryThresholdMs = thresholdOverride;
            }
            final boolean logSlowDelivery = (slowDeliveryThresholdMs > 0) && (msg.when > 0);
            final boolean logSlowDispatch = (slowDispatchThresholdMs > 0);

            final boolean needStartTime = logSlowDelivery || logSlowDispatch;
            final boolean needEndTime = logSlowDispatch;

            if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
                Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
            }

            final long dispatchStart = needStartTime ? SystemClock.uptimeMillis() : 0;
            final long dispatchEnd;
            try {
                msg.target.dispatchMessage(msg);
                dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
            } finally {
                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)) {
                        // Once we write a slow delivery log, suppress until the queue drains.
                        slowDeliveryDetected = true;
                    }
                }
            }
            if (logSlowDispatch) {
                showSlowLog(slowDispatchThresholdMs, dispatchStart, dispatchEnd, "dispatch", 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有一个MessageQueue,我们先来看看

MessageQueue.next()方法,这个方法返回了一个Message对象,在这个

方法中主要设计的是message.next和message.target,我们来看看这个

类Message。

 

上面的图片是Message的部分代码,target是Handler对象,next表示

下个Message对象,表明了MessageQueue是一个链表结构。

根据我们实际的应用,发送message时是用Handler.sendMessage

发送消息的。

这样我们就发现了一个问题,如果发送的Message都包含Handler那么就很容易造成内存

泄露(在Handler长时间存在的情况下)。

所以Android为避免出现这个问题,设计了一个obtain()方法:

 

上面的代码是Message.java的一部分,从代码上看出,sPool是一个静态

的Message变量,而obtain()方法就是首先从Message池里获取message

对象,没有的话就新建,其实message使用完毕后又会保存到message池中,

MAX_POOL_SIZE=50表示,message池大小是50。

下面我们回到最开始的地方

 

从上面的代码上分析,for是一个一个死循环,queue.next()方法会

获取Message对象来执行,没有对象就会阻塞。下一步就是执行message

消息,msg.target.dispatchMessage(msg);

queue.next()的部分代码如下:

 

nativePollOnce()方法是一个阻塞方法,意思是有message添加到

队列里就就会发送释放消息,这个方法才会解除阻塞。

Looper.loop()方法的最后一步:回收

面我们将讲解,上面没说完的message回收到Message池的内容

 

上面的代码是Message.recycleUnchecked()回收方法,重置message

对象后就保存到message池中,上限是MAX_POOL_SIZE=50.

第二阶段我们讲完了,下面开始第三阶段

上面我们讲到一个阻塞方法nativePollOnce(),那我们将详细说明解除

阻塞的内容。

说到解除阻塞的内容,我们就需要讲一下发送消息。我们都知道发送消息

是通过Handler.sendMessage()方法发送,那我就列举Handler的核心

代码:

 

上面的代码是Message.recycleUnchecked()回收方法,重置message

对象后就保存到message池中,上限是MAX_POOL_SIZE=50.

第二阶段我们讲完了,下面开始第三阶段

上面我们讲到一个阻塞方法nativePollOnce(),那我们将详细说明解除

阻塞的内容。

说到解除阻塞的内容,我们就需要讲一下发送消息。我们都知道发送消息

是通过Handler.sendMessage()方法发送,那我就列举Handler的核心

代码:

 

从上面的代码分析出:发送消息,最终还是要执行MessageQueue中

的enqueueMessage()方法。

 

上面就是MessageQueue.enqueueMessage方法,这个方法的意思是

把message保存到链表里,并执行nativeWake解除阻塞。

到这里Handler机制我们算是讲完了,主要是总结了一下内容:

  1. 一个线程只有一个Looper

  2. message消息的执行,和阻塞

  3. message消息的发送,和解除阻塞

  4. message的回收

关注微信公众号,即可得知Android相关知识

  • 33
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值