AI智能
改变未来

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 libcoreEventLogger.setReporter(new EventLoggingReporter());// Make sure TrustedCertificateStore looks in the right place for CA certificatesfinal 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(newLogPrinter(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(newLogPrinter(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 blockif (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 loggerfinal 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?=nullvar what: Int = 0var obj : Any? = null}

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

class MessageQueue {var putIndex:Int = 0var takeIndex:Int = 0var count:Int = 0private var lock: Lock? = nullprivate var notEmpty: Condition? = nullprivate var notFull: Condition? = nullvar 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 putIndexcount++notEmpty?.signalAll()}finally {lock?.unlock()}}fun next():Message?{var msg: Message? = nulltry {lock?.lock()while (count==0){notEmpty?.await()}items.let {msg = it?.get(takeIndex)it?.set(takeIndex, null)takeIndex = if (++takeIndex == items?.size) 0 else takeIndexcount--notFull?.signalAll()}}finally {lock?.unlock()}return  msg;}}

Looper:

class Looper {var mQueue:MessageQueue? = nullinit {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.mQueuewhile (true){val msg = queue?.next()if(msg ==  null){continue}msg.target?.dispatchMessage(msg)}}}}

Handler:

open class Handler {private var mQueue:MessageQueue? = nullprivate var mLooper:Looper?=nullinit {mLooper = Looper.myLooper()this.mQueue = mLooper?.mQueue}fun sendMessage(msg:Message){msg.target=thismQueue?.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 = 1synchronized(UUID::class.java){msg.obj = \"收到线程\"+Thread.currentThread().name+\"发过来的信息\"}handler.sendMessage(msg)try {Thread.sleep(1000)}catch (e:Exception){e.printStackTrace()}}}).start()}Looper.loop()}}

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

赞(0) 打赏
未经允许不得转载:爱站程序员基地 » Android Handler机制,一个小例子助你了解+为什么Loop里的死循环为什么没有阻塞主线程?