Android Handler机制,一个小例子助你了解+为什么Loop里的死循环为什么没有阻塞主线程?

本文内容:

Handler机制是怎么工作的?
Loop里的死循环为什么没有阻塞线程?

     我们知道,更新ui等一些耗时操作都不能放在主线程去执行,最好在子线程中执行,那我们执行好的任务又怎么通知主线程去更新数据呢?答案就是Handler机制

Handler机制里又几个重要的东西:

  • Hanlder :用来处理消息的类
  • Looper :消息循环器,不断从MessageQueue取出消息,分发给处理该消息的Handler
  • Message :消息对象
  • MessageQueue :消息队列
  • ThreadLocal : 用来维护一个线程对应一个Looper,也就是我们创建Looper时ThreadLocal会给我们一个 Looper,防止不同线程之间混乱。

    整个handler主要运行在子线程相对的主线程中,子线程通过sendMessage吧Message发送到handler,并设置和这个handler绑定在一起,handler把消息放进从looper拿到的MessageQueue,looper会不断循环去取到MessageQueue里的Message,然后又分发到Message绑定的handler进行处理。整个过程就是个回调的过程

我们从源码分析:
    首先去看ActivityThread这个类的main方法,也就是程序入口:(这个类看起来很像个子线程,但并不是,它没有继承Thread)

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("");
        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();准备一个Looper

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

sMainLooper = myLooper();

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

从sThreadLocal中获取Looper()

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

这段代码实现了一个线程只能获取对应looper,不能获取他人的。

然后回去

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

获取handler,这个handler处理的消息几乎是整个app工作流程
开启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();

        // 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);
            }
            // Make sure the observer won't change while processing a transaction.
            final Observer observer = sObserver;

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

直接给你来个死循环,既能不断去消息分发,又能保证程序一直存活,那为什么不会阻塞整个app运行呢?

Loop里的死循环为什么没有阻塞线程?

我们看到24行那Message msg = queue.next(); // might block有一段注释:有可能阻塞,也就是当队列为空时会阻塞,直到下一个消息进来,这就是linux的epoll//pipe机制,从这个next()方法进去会发现有个nativePollOnce()本地方法,这就是阻塞的地方,可想而知底层代码肯定有睡眠和唤醒机制。还记得
thread.attach(false, startSeq);这个方法吗,它绑定一个binder线程,这个binder线程又会一直与bingder驱动进行沟通。我们知道binder驱动就是用于进程间通信的,关键就在这了,android系统进程会通过binder与我们的app进程通信!当我们点击屏幕,就会通过binder传到app的handler消息机制往队列塞消息。这就使app又动起来了

一个自定义handler机制的例子:
Message:

class Message {
    var target:Handler?=null
    var what: Int = 0
    var obj : Any? = null
}

MessageQueue:(用到了生产者消费者模式)

class MessageQueue {
    var putIndex:Int = 0
    var takeIndex:Int = 0
    var count:Int = 0
    private var lock: Lock? = null
    private var notEmpty: Condition? = null
    private var notFull: Condition? = null
    var items:Array<Message?>
    init {
        items = Array(20, {null})
        this.lock = ReentrantLock()
        this.notEmpty = (lock as ReentrantLock).newCondition()
        this.notFull = (lock as ReentrantLock).newCondition()
    }
    fun enqueueMessage(msg:Message) {
        try {
            lock?.lock()
            while (count == items.size){
                notFull?.await()
            }
            items?.set(putIndex, msg)
            putIndex = if (++putIndex == items?.size) 0 else putIndex
            count++
            notEmpty?.signalAll()
        }finally {
            lock?.unlock()
        }
    }
    fun next():Message?{
        var msg: Message? = null
        try {
            lock?.lock()
            while (count==0){
                notEmpty?.await()
            }
            items.let {
                msg = it?.get(takeIndex)
                it?.set(takeIndex, null)
                takeIndex = if (++takeIndex == items?.size) 0 else takeIndex
                count--
                notFull?.signalAll()
            }
        }finally {
            lock?.unlock()
        }
        return  msg;
    }
}

Looper:

class Looper {
    var mQueue:MessageQueue? = null
    init {
        this.mQueue=MessageQueue()
    }
    companion object{

        val sThreadLocal = ThreadLocal<Looper>()
        fun prepare(){
            if (sThreadLocal.get()!=null){
                throw RuntimeException("error")
            }
            sThreadLocal.set(Looper())
        }
        fun myLooper(): Looper? {
            return sThreadLocal.get()
        }
        fun loop(){
            val me = myLooper()
            if(me == null){
                throw RuntimeException("error1")
            }
            val queue = me.mQueue
            while (true){
                val msg = queue?.next()
                if(msg ==  null){
                    continue
                }
                msg.target?.dispatchMessage(msg)
            }
        }
    }
}

Handler:

open class Handler {
    private var mQueue:MessageQueue? = null
    private var mLooper:Looper?=null
    init {
        mLooper = Looper.myLooper()
        this.mQueue = mLooper?.mQueue
    }
    fun sendMessage(msg:Message){
        msg.target=this
        mQueue?.enqueueMessage(msg)
    }
    open fun handleMessage(msg:Message){
        Log.i("Hanlder", msg.obj.toString())
    }
    fun dispatchMessage(msg:Message){
        handleMessage(msg)
    }
}

MainActivity:

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        Looper.prepare()

        val handler:Handler = object :Handler(){
            override fun handleMessage(msg: Message) {
                System.out.println(Thread.currentThread().name+",received:"+msg.obj.toString())
            }
        }
        for (i in 1..5){
            Thread(object :Runnable{
                override fun run() {
                    val msg = Message()
                    msg.what = 1
                    synchronized(UUID::class.java){
                        msg.obj = "收到线程"+Thread.currentThread().name+"发过来的信息"
                    }
                    handler.sendMessage(msg)
                    try {
                        Thread.sleep(1000)
                    }catch (e:Exception){
                        e.printStackTrace()
                    }
                }
            }).start()
        }
        Looper.loop()
    }
}

只是为了好理解,这个例子还是会阻塞主线程的

你可能感兴趣的:(Android Handler机制,一个小例子助你了解+为什么Loop里的死循环为什么没有阻塞主线程?)