0
点赞
收藏
分享

微信扫一扫

Android源码剖析:基于-Handler、Looper-实现拦截全局崩溃,apk瘦身

m逆光生长 2022-02-17 阅读 111

try {
msg.target.dispatchMessage(msg);
} finally {}
if (logging != null) {
logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
}
msg.recycleUnchecked();
}
}
public void quit() {
mQueue.quit(false);
}
}

如果主线程发生了异常,就会退出循环,意味着APP崩溃,所以我们我们需要进行try-catch,避免APP退出,我们可以在主线程再启动一个 Looper.loop() 去执行主线程任务,然后try-catch这个Looper.loop()方法,就不会退出。

基于 Handler 实现单线程的线程池

从上面的 Looper.loop() ,我们可以利用 Handler 实现单线程池功能,而且这个线程池和主线程一样拥有立刻执行post()、延迟执行postDelayed()、定时执行postAtTime()等强大功能。

// 错误用法
var handler: Handler? = null
Thread({
handler = Handler()
}).start()

当我们在异步线程执行上面的代码,就会报错 Can't create handler inside thread Thread[Thread-2,5,main] that has not called Looper.prepare()。 这个是因为 Handler 的工作是依靠 Looper ,必须为线程创建 Looper 才能正常功能,正确的用法如下:

// 正确用法
var handler: Handler? = null
Thread({
Looper.prepare()
handler = Handler()
Looper.loop()
}).start()

测试:

button.setOnClickListener {
handler?.post {
println(Thread.currentThread())
}
handler?.post {
println(Thread.currentThread())
}
}

输出结果:

System.out: Thread[Thread-2,5,main]
System.out: Thread[Thread-2,5,main]

HandlerThread

HandlerThread 是 Android 对Thread的封装,增加了Handler的支持,实现就是实现了前面例子的功能

val handlerThread = HandlerThread(“test”)
handlerThread.start()
handler = Handler(handlerThread.looper)

MessageQueue 源码剖析

我们都知道Handler的功能非常丰富,拥有立刻执行post()、延迟执行postDelayed()、定时执行postAtTime()等执行方式。下面就从源码分析是如何实现的。

public final class MessageQueue {
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;
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;
}
}
}

MessageQueue.next() 是一个带有阻塞的方法,只有退出或者有任务才会return,起阻塞的实现是使用Native层的 nativePollOnce() 函数,如果消息队列中没有消息存在nativePollOnce就不会返回,一直处于Native层等待状态。直到调用 quit() 退出或者调用 enqueueMessage(Message msg, long when) 有新的任务进来调用了Native层的nativeWake()函数,才会重新唤醒。 android_os_MessageQueue.cpp

nativePollOnce(long ptr, int timeoutMillis)

nativePollOnce 是一个带有两个参数的Native函数,第一个参数是作为当前任务队列ID;第二个参数是等待时长,如果是-1,就代表无消息,会进入等待状态,如果是 0,再次查找未等待的消息。如果大于0,就等到指定时长然后返回。

nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);

最后

我这里整理了一份完整的学习思维以及Android开发知识大全PDF,有需要的同学可以自行领取。

资料获取方式:Android完整知识学习体系路线

当然实践出真知,即使有了学习线路也要注重实践,学习过的内容只有结合实操才算是真正的掌握。

er/Android%E5%BC%80%E5%8F%91%E4%B8%8D%E4%BC%9A%E8%BF%99%E4%BA%9B%EF%BC%9F%E5%A6%82%E4%BD%95%E9%9D%A2%E8%AF%95%E6%8B%BF%E9%AB%98%E8%96%AA%EF%BC%81.md)**

当然实践出真知,即使有了学习线路也要注重实践,学习过的内容只有结合实操才算是真正的掌握。

举报

相关推荐

0 条评论