2020Android面试重难点之Handler机制,含字节、京东、腾讯经典面试真题解析!

}

}

Looper 还需要有一个用于循环从 MessageQueue 获取消息并处理的方法,就叫它loop()吧。其作用也能简单,就是循环从 MessageQueue 中取出 Message,然后将 Message 再反过来分发给 Handler 即可

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;

for (; ; ) {

Message msg = queue.next();//可能会阻塞

msg.target.dispatchMessage(msg);

}

}

这样,主线程就先通过调用prepareMainLooper()来完成 sMainLooper 的初始化,然后调用loop()开始向 mainMessageQueue 循环取值并进行处理,没有消息的话主线程就暂时休眠着。子线程拿到 sMainLooper 后就以此来初始化 Handler,这样子线程向 Handler 发送的消息就都会被存到 mainMessageQueue 中,最终在主线程被消费掉

5、做一个总结

这样一步步走下来后,读者对于 Message、MessageQueue、Handler、Looper 这四个类的定位就应该都很清晰了吧?不同线程之间就可以依靠拿到对方的 Looper 来实现消息的跨线程处理了

例如,对于以下代码,即使 Handler 是在 otherThread 中进行初始化,但 handleMessage 方法最终是会在 mainThread 被调用执行的,

Thread mainThread = new Thread() {

@Override

public void run() {

//初始化 mainLooper

Looper.prepareMainLooper();

//开启循环

Looper.loop();

}

};

Thread otherThread = new Thread() {

@Override

public void run() {

Looper mainLooper = Looper.getMainLooper();

Handler handler = new Handler(mainLooper.mQueue) {

@Override

public void handleMessage(Message msg) {

switch (msg.what) {

case 1: {

String ob = (String) msg.obj;

break;

}

case 2: {

List ob = (List) msg.obj;

break;

}

}

}

};

Message messageA = new Message();

messageA.what = 1;

messageA.obj = “https://github.com/leavesC”;

Message messageB = new Message();

messageB.what = 2;

messageB.obj = new ArrayList();

handler.sendMessage(messageA);

handler.sendMessage(messageB);

}

};

再来做个简单的总结:

  • Message:用来表示要执行的任务

  • Handler:子线程持有的 Handler 如果绑定到的是主线程的 MessageQueue 的话,那么子线程发送的 Message 就可以由主线程来消费,以此来实现线程切换,执行 UI 更新操作等目的

  • MessageQueue:即消息队列,通过 Handler 发送的消息并非都是立即执行的,需要先按照 Message 的优先级高低(延时时间的长短)保存到 MessageQueue 中,之后再来依次执行

  • Looper:Looper 用于从 MessageQueue 中循环获取 Message 并将之传递给消息处理者(即消息发送者 Handler 本身)来进行消费,每条 Message 都有个 target 变量用来指向 Handler,以此把 Message 和其处理者关联起来。不同线程之间通过互相拿到对方的 Looper 对象,以此来实现跨线程发送消息

有了以上的认知基础后,下面就来看看实际的源码实现 ~ ~

二、Handler 源码

1、Handler 如何初始化

Handler 的构造函数一共有七个,除去两个已经废弃的和三个隐藏的,实际上开发者可以使用的只有两个。而不管是使用哪个构造函数,最终的目的都是为了完成 mLooper、mQueue、mCallback、mAsynchronous 这四个常量的初始化,同时也可以看出来 MessageQueue 是由 Looper 来完成初始化的,而且 Handler 对于 Looper 和 MessageQueue 都是一对一的关系,一旦初始化后就不可改变

大部分开发者使用的应该都是 Handler 的无参构造函数,而在 Android 11 中 Handler 的无参构造函数已经被标记为废弃的了。Google 官方更推荐的做法是通过显式传入 Looper 对象来完成初始化,而非隐式使用当前线程关联的 Looper

Handler 对于 Looper 和 MessageQueue 都是一对一的关系,但是 Looper 和 MessageQueue 对于 Handler 可以是一对多的关系,这个后面会讲到

@UnsupportedAppUsage

final Looper mLooper;

final MessageQueue mQueue;

@UnsupportedAppUsage

final Callback mCallback;

final boolean mAsynchronous;

//省略其它构造函数

/**

  • @hide

*/

public Handler(@Nullable Callback callback, boolean async) {

if (FIND_POTENTIAL_LEAKS) {

final Class<? extends Handler> klass = getClass();

if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&

(klass.getModifiers() & Modifier.STATIC) == 0) {

Log.w(TAG, "The following Handler class should be static or leaks might occur: " +

klass.getCanonicalName());

}

}

mLooper = Looper.myLooper();

if (mLooper == null) {

throw new RuntimeException(

"Can’t create handler inside thread " + Thread.currentThread()

  • " that has not called Looper.prepare()");

}

mQueue = mLooper.mQueue;

mCallback = callback;

mAsynchronous = async;

}

2、Looper 如何初始化

在初始化 Handler 时,如果外部调用的构造函数没有传入 Looper,那就会调用Looper.myLooper()来获取和当前线程关联的 Looper 对象,再从 Looper 中取 MessageQueue。如果获取到的 Looper 对象为 null 就会抛出异常。根据异常信息 Can't create handler inside thread that has not called Looper.prepare() 可以看出来,在初始化 Handler 之前需要先调用 Looper.prepare()完成 Looper 的初始化

走进 Looper 类中可以看到,myLooper()方法是 Looper 类的静态方法,其只是单纯地从 sThreadLocal 变量中取值并返回而已。sThreadLocal 又是通过 prepare(boolean) 方法来进行初始化赋值的,且只能赋值一次,重复调用将抛出异常

我们知道,ThreadLocal 的特性就是可以为不同的线程分别维护单独的一个变量实例,所以,不同的线程就会分别对应着不同的 Looper 对象,是一一对应的关系

@UnsupportedAppUsage

static final ThreadLocal sThreadLocal = new ThreadLocal();

/**

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

}

/** Initialize the current thread as a looper.

  • This gives you a chance to create handlers that then reference

  • this looper, before actually starting the loop. Be sure to call

  • {@link #loop()} after calling this method, and end it by calling

  • {@link #quit()}.

*/

public static void prepare() {

prepare(true);

}

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

}

此外,Looper 类的构造函数也是私有的,会初始化两个常量值:mQueue 和 mThread,这说明了 Looper 对于 MessageQueue 和 Thread 都是一一对应的关系,关联之后不能改变

@UnsupportedAppUsage

final MessageQueue mQueue;

final Thread mThread;

private Looper(boolean quitAllowed) {

mQueue = new MessageQueue(quitAllowed);

mThread = Thread.currentThread();

}

在日常开发中,我们在通过 Handler 来执行 UI 刷新操作时,经常使用的是 Handler 的无参构造函数,那么此时肯定就是使用了和主线程关联的 Looper 对象,对应 Looper 类中的静态变量 sMainLooper

@UnsupportedAppUsage

private static Looper sMainLooper; // guarded by Looper.class

//被标记为废弃的原因是因为 sMainLooper 会交由 Android 系统自动来完成初始化,外部不应该主动来初始化

@Deprecated

public static void prepareMainLooper() {

prepare(false);

synchronized (Looper.class) {

if (sMainLooper != null) {

throw new IllegalStateException(“The main Looper has already been prepared.”);

}

sMainLooper = myLooper();

}

}

/**

  • Returns the application’s main looper, which lives in the main thread of the application.

*/

public static Looper getMainLooper() {

synchronized (Looper.class) {

return sMainLooper;

}

}

prepareMainLooper()就用于为主线程初始化 Looper 对象,该方法又是由 ActivityThread 类的 main() 方法来调用的。该 main() 方法即 Java 程序的运行起始点,所以当应用启动时系统就自动为我们在主线程做好了 mainLooper 的初始化,而且已经调用了Looper.loop()方法开启了消息的循环处理,应用在使用过程中的各种交互逻辑(例如:屏幕的触摸事件、列表的滑动等)就都是在这个循环里完成分发的

正是因为 Android 系统已经自动完成了主线程 Looper 的初始化,所以我们在主线程中才可以直接使用 Handler 的无参构造函数来完成 UI 相关事件的处理

public final class ActivityThread extends ClientTransactionHandler {

public static void main(String[] args) {

···

Looper.prepareMainLooper();

···

Looper.loop();

throw new RuntimeException(“Main thread loop unexpectedly exited”);

}

}

3、Handler 发送消息

Handler 用于发送消息的方法非常多,有十几个,其中大部分最终调用到的都是 sendMessageAtTime() 方法。uptimeMillis 即 Message 具体要执行的时间戳,如果该时间戳比当前时间大,那么就意味着要执行的是延迟任务。如果为 mQueue 为 null,就会打印异常信息并直接返回,因为 Message 是需要交由 MessageQueue 来处理的

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

}

需要注意 msg.target = this 这句代码,target 指向了发送消息的主体,即 Handler 对象本身,即由 Handler 对象发给 MessageQueue 的消息最后还是要交由 Handler 对象本身来处理

private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,

long uptimeMillis) {

msg.target = this;

msg.workSourceUid = ThreadLocalWorkSource.getUid();

if (mAsynchronous) {

msg.setAsynchronous(true);

}

//将消息交由 MessageQueue 处理

return queue.enqueueMessage(msg, uptimeMillis);

}

4、MessageQueue

MessageQueue 通过 enqueueMessage 方法来接收消息

  • 因为存在多个线程同时往一个 MessageQueue 发送消息的可能,所以 enqueueMessage 内部肯定需要进行线程同步

  • 可以看出 MessageQueue 内部是以链表的结构来存储 Message 的(Message.next),根据 Message 的时间戳大小来决定其在消息队列中的位置

  • mMessages 代表的是消息队列中的第一条消息。如果 mMessages 为空(消息队列是空的),或者 mMessages 的时间戳要比新消息的时间戳大,则将新消息插入到消息队列的头部;如果 mMessages 不为空,则寻找消息列队中第一条触发时间比新消息晚的非空消息,将新消息插到该消息的前面

到此,一个按照时间戳大小进行排序的消息队列就完成了,后边要做的就是从消息队列中依次取出消息进行处理了

boolean enqueueMessage(Message msg, long when) {

if (msg.target == null) {

throw new IllegalArgumentException(“Message must have a target.”);

}

synchronized (this) {

···

msg.markInUse();

msg.when = when;

Message p = mMessages;

//用于标记是否需要唤醒线程

boolean needWake;

//如果链表是空的,或者处于队头的消息的时间戳比 msg 要大,则将 msg 作为链表头部

//when == 0 说明 Handler 调用的是 sendMessageAtFrontOfQueue 方法,直接将 msg 插到队列头部

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 {

//如果当前线程处于休眠状态 + 队头消息是屏障消息 + msg 是异步消息

//那么就需要唤醒线程

needWake = mBlocked && p.target == null && msg.isAsynchronous();

Message prev;

//从链表头向链表尾遍历,寻找链表中第一条时间戳比 msg 大的消息,将 msg 插到该消息的前面

for (;😉 {

prev = p;

p = p.next;

if (p == null || when < p.when) {

break;

}

if (needWake && p.isAsynchronous()) {

//如果在 msg 之前队列中还有异步消息那么就不需要主动唤醒

//因为已经设定唤醒时间了

needWake = false;

}

}

msg.next = p; // invariant: p == prev.next

prev.next = msg;

}

// We can assume mPtr != 0 because mQuitting is false.

if (needWake) {

nativeWake(mPtr);

}

}

return true;

}

知道了 Message 是如何保存的了,再来看下 MessageQueue 是如何取出 Message 并回调给 Handler 的。在 MessageQueue 中读取消息的操作对应的是next() 方法。next() 方法内部开启了一个无限循环,如果消息队列中没有消息或者是队头消息还没到可以处理的时间,该方法就会导致 Loop 线程休眠挂起,直到条件满足后再重新遍历消息

@UnsupportedAppUsage

Message next() {

···

for (;😉 {

if (nextPollTimeoutMillis != 0) {

Binder.flushPendingCommands();

}

//将 Loop 线程休眠挂起

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;

}

···

}

···

}

···

}

}

next() 方法又是通过 Looper 类的 loop() 方法来循环调用的,loop() 方法内也是一个无限循环,唯一跳出循环的条件就是 queue.next()方法返回为 null。因为 next() 方法可能会触发阻塞操作,所以没有消息需要处理时也会导致 loop() 方法被阻塞着,而当 MessageQueue 有了新的消息,Looper 就会及时地处理这条消息并调用 msg.target.dispatchMessage(msg) 方法将消息回传给 Handler 进行处理

/**

  • Run the message queue in this thread. Be sure to call

  • {@link #quit()} to end the 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.”);

}

···

for (;😉 {

Message msg = queue.next(); // might block

if (msg == null) {

// No message indicates that the message queue is quitting.

return;

}

···

msg.target.dispatchMessage(msg);

···

}

}

Handler 的dispatchMessage方法就是在向外部分发 Message 了。至此,Message 的整个分发流程就结束了

/**

  • Handle system messages here.

*/

public void dispatchMessage(@NonNull Message msg) {

if (msg.callback != null) {

handleCallback(msg);

} else {

if (mCallback != null) {

if (mCallback.handleMessage(msg)) {

return;

}

}

handleMessage(msg);

}

}

5、消息屏障

Android 系统为了保证某些高优先级的 Message(异步消息) 能够被尽快执行,采用了一种消息屏障(Barrier)机制。其大致流程是:先发送一个屏障消息到 MessageQueue 中,当 MessageQueue 遍历到该屏障消息时,就会判断当前队列中是否存在异步消息,有的话则先跳过同步消息(开发者主动发送的都属于同步消息),优先执行异步消息。这种机制就会使得在异步消息被执行完之前,同步消息都不会得到处理

Handler 的构造函数中的async参数就用于控制发送的 Message 是否属于异步消息

public class Handler {

final boolean mAsynchronous;

public Handler(@NonNull Looper looper, @Nullable Callback callback, boolean async) {

mAsynchronous = async;

}

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 在取队头消息的时候,如果判断到队头消息就是屏障消息的话,那么就会向后遍历找到第一条异步消息优先进行处理

@UnsupportedAppUsage

Message next() {

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

}

}

}

}

6、退出 Looper 循环

Looper 类本身做了方法限制,除了主线程外,子线程关联的 MessageQueue 都支持退出 Loop 循环,即 quitAllowed 只有主线程才能是 false

public final class Looper {

private Looper(boolean quitAllowed) {

mQueue = new MessageQueue(quitAllowed);

mThread = Thread.currentThread();

}

public static void prepare() {

prepare(true);

}

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

}

}

MessageQueue 支持两种方式来退出 Loop:

  • safe 为 true,只移除所有尚未执行的消息,不移除时间戳等于当前时间的消息

  • safe 为 false,移除所有消息

void quit(boolean safe) {

if (!mQuitAllowed) {

//MessageQueue 设置了不允许退出循环,直接抛出异常

throw new IllegalStateException(“Main thread not allowed to quit.”);

}

synchronized (this) {

if (mQuitting) {

//避免重复调用

return;

}

mQuitting = true;

if (safe) {

//只移除所有尚未执行的消息,不移除时间戳等于当前时间的消息

removeAllFutureMessagesLocked();

} else {

//移除所有消息

removeAllMessagesLocked();

}

// We can assume mPtr != 0 because mQuitting was previously false.

nativeWake(mPtr);

}

}

7、IdleHandler

IdleHandler 是 MessageQueue 的一个内部接口,可以用于在 Loop 线程处于空闲状态的时候执行一些优先级不高的操作

public static interface IdleHandler {

boolean queueIdle();

}

MessageQueue 在获取队头消息时,如果发现当前没有需要执行的 Message 的话,那么就会去遍历 mIdleHandlers,依次执行 IdleHandler

private final ArrayList mIdleHandlers = new ArrayList();

@UnsupportedAppUsage

Message next() {

···

int pendingIdleHandlerCount = -1; // -1 only during first iteration

int nextPollTimeoutMillis = 0;

for (;😉 {

···

synchronized (this) {

···

//如果队头消息 mMessages 为 null 或者 mMessages 需要延迟处理

//那么就来执行 IdleHandler

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

}

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 {

//执行 IdleHandler

//如果返回 false 的话说明之后不再需要执行,那就将其移除

keep = idler.queueIdle();

} catch (Throwable t) {

Log.wtf(TAG, “IdleHandler threw exception”, t);

}

if (!keep) {

synchronized (this) {

mIdleHandlers.remove(idler);

}

}

}

}

}

例如,ActivityThread 就向主线程 MessageQueue 添加了一个 GcIdler,用于在主线程空闲时尝试去执行 GC 操作

public final class ActivityThread extends ClientTransactionHandler {

@UnsupportedAppUsage

void scheduleGcIdler() {

if (!mGcIdlerScheduled) {

mGcIdlerScheduled = true;

//添加 IdleHandler

Looper.myQueue().addIdleHandler(mGcIdler);

}

mH.removeMessages(H.GC_WHEN_IDLE);

}

final class GcIdler implements MessageQueue.IdleHandler {

@Override

public final boolean queueIdle() {

//尝试 GC

doGcIfNeeded();

purgePendingResources();

return false;

}

}

}

8、做一个总结

再来总结下以上的所有内容

  1. 每个 Handler 都会和一个 Looper 实例关联在一起,可以在初始化 Handler 时通过构造函数主动传入实例,否则就会默认使用和当前线程关联的 Looper 对象

  2. 每个 Looper 都会和一个 MessageQueue 实例关联在一起,每个线程都需要通过调用 Looper.prepare()方法来初始化本线程独有的 Looper 实例,并通过调用Looper.loop()方法来使得本线程循环向 MessageQueue 取出消息并执行。Android 系统默认会为每个应用初始化和主线程关联的 Looper 对象,并且默认就开启了 loop 循环来处理主线程消息

  3. MessageQueue 按照链接结构来保存 Message,执行时间早(即时间戳小)的 Message 会排在链表的头部,Looper 会循环从链表中取出 Message 并回调给 Handler,取值的过程可能会包含阻塞操作

  4. Message、Handler、Looper、MessageQueue 这四者就构成了一个生产者和消费者模式。Message 相当于产品,MessageQueue 相当于传输管道,Handler 相当于生产者,Looper 相当于消费者

  5. Handler 对于 Looper、Handler 对于 MessageQueue、Looper 对于 MessageQueue、Looper 对于 Thread ,这几个之间都是一一对应的关系,在关联后无法更改,但 Looper 对于 Handler、MessageQueue 对于 Handler 可以是一对多的关系

  6. Handler 能用于更新 UI 包含了一个隐性的前提条件:Handler 与主线程 Looper 关联在了一起。在主线程中初始化的 Handler 会默认与主线程 Looper 关联在一起,所以其 handleMessage(Message msg) 方法就会由主线程来调用。在子线程初始化的 Handler 如果也想执行 UI 更新操作的话,则需要主动获取 mainLooper 来初始化 Handler

  7. 对于我们自己在子线程中创建的 Looper,当不再需要的时候我们应该主动退出循环,否则子线程将一直无法得到释放。对于主线程 Loop 我们则不应该去主动退出,否则将导致应用崩溃

  8. 我们可以通过向 MessageQueue 添加 IdleHandler 的方式,来实现在 Loop 线程处于空闲状态的时候执行一些优先级不高的任务。例如,假设我们有个需求是希望当主线程完成界面绘制等事件后再执行一些 UI 操作,那么就可以通过 IdleHandler 来实现,这可以避免拖慢用户看到首屏页面的速度

三、Handler 在系统中的应用

1、HandlerThread

HandlerThread 是 Android SDK 中和 Handler 在同个包下的一个类,从其名字就可以看出来它是一个线程,而且使用到了 Handler

其用法类似于以下代码。通过 HandlerThread 内部的 Looper 对象来初始化 Handler,同时在 Handler 中声明需要执行的耗时任务,主线程通过向 Handler 发送消息来触发 HandlerThread 去执行耗时任务

class MainActivity : AppCompatActivity() {

private val handlerThread = HandlerThread(“I am HandlerThread”)

private val handler by lazy {

object : Handler(handlerThread.looper) {

override fun handleMessage(msg: Message) {

Thread.sleep(2000)

Log.e(“MainActivity”, “这里是子线程,可以用来执行耗时任务:” + Thread.currentThread().name)

}

}

}

override fun onCreate(savedInstanceState: Bundle?) {

super.onCreate(savedInstanceState)

setContentView(R.layout.activity_main)

btn_test.setOnClickListener {

handler.sendEmptyMessage(1)

}

handlerThread.start()

}

}

HandlerThread 的源码还是挺简单的,只有一百多行

HandlerThread 是 Thread 的子类,其作用就是为了用来执行耗时任务,其 run()方法会自动为自己创建一个 Looper 对象并保存到 mLooper,之后就主动开启消息循环,这样 HandlerThread 就会来循环处理 Message 了

public class HandlerThread extends Thread {

//线程优先级

int mPriority;

//线程ID

int mTid = -1;

//当前线程持有的 Looper 对象

Looper mLooper;

private @Nullable Handler mHandler;

public HandlerThread(String name) {

super(name);

mPriority = Process.THREAD_PRIORITY_DEFAULT;

}

public HandlerThread(String name, int priority) {

super(name);

mPriority = priority;

}

@Override

public void run() {

mTid = Process.myTid();

//触发当前线程创建 Looper 对象

Looper.prepare();

synchronized (this) {

//获取 Looper 对象

mLooper = Looper.myLooper();

//唤醒所有处于等待状态的线程

notifyAll();

}

//设置线程优先级

Process.setThreadPriority(mPriority);

onLooperPrepared();

//开启消息循环

Looper.loop();

mTid = -1;

}

}

此外,HandlerThread 还包含一个getLooper()方法用于获取 Looper。当我们在外部调用handlerThread.start()启动线程后,由于其run()方法的执行时机依然是不确定的,所以 getLooper()方法就必须等到 Looper 初始化完毕后才能返回,否则就会由于wait()方法而一直阻塞等待。当run()方法初始化 Looper 完成后,就会调用notifyAll()来唤醒所有处于等待状态的线程。所以外部在使用 HandlerThread 前就记得必须先调用 start() 方法来启动 HandlerThread

//获取与 HandlerThread 关联的 Looper 对象

//因为 getLooper() 可能先于 run() 被执行

//所以当 mLooper 为 null 时调用者线程就需要阻塞等待 Looper 对象创建完毕

public Looper getLooper() {

if (!isAlive()) {

return null;

}

// If the thread has been started, wait until the looper has been created.

synchronized (this) {

while (isAlive() && mLooper == null) {

try {

wait();

} catch (InterruptedException e) {

}

}

}

return mLooper;

}

HandlerThread 起到的作用就是方便了主线程和子线程之间的交互,主线程可以直接通过 Handler 来声明耗时任务并交由子线程来执行。使用 HandlerThread 也方便在多个线程间共享,主线程和其它子线程都可以向 HandlerThread 下发任务,且 HandlerThread 可以保证多个任务执行时的有序性

2、IntentService

IntentService 是系统提供的 Service 子类,用于在后台串行执行耗时任务,在处理完所有任务后会自动停止,不必来手动调用 stopSelf() 方法。而且由于IntentService 是四大组件之一,拥有较高的优先级,不易被系统杀死,因此适合用于执行一些高优先级的异步任务

Google 官方以前也推荐开发者使用 IntentService,但是在 Android 11 中已经被标记为废弃状态了,但这也不妨碍我们来了解下其实现原理

IntentService 内部依靠 HandlerThread 来实现,其 onCreate()方法会创建一个 HandlerThread,拿到 Looper 对象来初始化 ServiceHandler。ServiceHandler 会将其接受到的每个 Message 都转交由抽象方法 onHandleIntent来处理,子类就通过实现该方法来声明耗时任务

public abstract class IntentService extends Service {

private volatile Looper mServiceLooper;

@UnsupportedAppUsage

private volatile ServiceHandler mServiceHandler;

private final class ServiceHandler extends Handler {

public ServiceHandler(Looper looper) {

super(looper);

}

@Override

public void handleMessage(Message msg) {

onHandleIntent((Intent)msg.obj);

stopSelf(msg.arg1);

}

}

@Override

public void onCreate() {

super.onCreate();

HandlerThread thread = new HandlerThread(“IntentService[” + mName + “]”);

//触发 HandlerThread 创建 Looper 对象

thread.start();

//获取 Looper 对象,构建可以向 HandlerThread 发送 Message 的 Handler

mServiceLooper = thread.getLooper();

mServiceHandler = new ServiceHandler(mServiceLooper);

}

@WorkerThread

protected abstract void onHandleIntent(@Nullable Intent intent);

}

每次 start IntentService 时,onStart()方法就会被调用,将 intentstartId 包装为一个 Message 对象后发送给mServiceHandler。需要特别注意的是 startId 这个参数,它用于唯一标识每次对 IntentService 发起的任务请求,每次回调 onStart() 方法时,startId 的值都是自动递增的。IntentService 不应该在处理完一个 Message 之后就立即停止 IntentService,因为此时 MessageQueue 中可能还有待处理的任务还未取出来,所以如果当调用 stopSelf(int)方法时传入的参数不等于当前最新的 startId 值的话,那么stopSelf(int) 方法就不会导致 IntentService 被停止,从而避免了将尚未处理的 Message 给遗漏了

@Override

public void onStart(@Nullable Intent intent, int startId) {

Message msg = mServiceHandler.obtainMessage();

msg.arg1 = startId;

msg.obj = intent;

mServiceHandler.sendMessage(msg);

}

@Override

public int onStartCommand(@Nullable Intent intent, int flags, int startId) {

onStart(intent, startId);

return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;

}

四、Handler 在三方库中的应用

1、EventBus

EventBus 的 Github 上有这么一句介绍:EventBus is a publish/subscribe event bus for Android and Java. 这说明了 EventBus 是普遍适用于 Java 环境的,只是对 Android 系统做了特殊的平台支持而已。EventBus 的四种消息发送策略包含了ThreadMode.MAIN 用于指定在主线程进行消息回调,其内部就是通过 Handler 来实现的

EventBusBuilder 会去尝试获取 MainLooper,如果拿得到的话就可以用来初始化 HandlerPoster,从而实现主线程回调

MainThreadSupport getMainThreadSupport() {

if (mainThreadSupport != null) {

return mainThreadSupport;

} else if (AndroidLogger.isAndroidLogAvailable()) {

Object looperOrNull = getAndroidMainLooperOrNull();

return looperOrNull == null ? null :

new MainThreadSupport.AndroidHandlerMainThreadSupport((Looper) looperOrNull);

} else {

return null;

}

}

static Object getAndroidMainLooperOrNull() {

try {

return Looper.getMainLooper();

学习福利

【Android 详细知识点思维脑图(技能树)】

其实Android开发的知识点就那么多,面试问来问去还是那么点东西。所以面试没有其他的诀窍,只看你对这些知识点准备的充分程度。so,出去面试时先看看自己复习到了哪个阶段就好。

虽然 Android 没有前几年火热了,已经过去了会四大组件就能找到高薪职位的时代了。这只能说明 Android 中级以下的岗位饱和了,现在高级工程师还是比较缺少的,很多高级职位给的薪资真的特别高(钱多也不一定能找到合适的),所以努力让自己成为高级工程师才是最重要的。

这里附上上述的面试题相关的几十套字节跳动,京东,小米,腾讯、头条、阿里、美团等公司19年的面试题。把技术点整理成了视频和PDF(实际上比预期多花了不少精力),包含知识脉络 + 诸多细节。

由于篇幅有限,这里以图片的形式给大家展示一小部分。

网上学习 Android的资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。希望这份系统化的技术体系对大家有一个方向参考。

obtainMessage();

msg.arg1 = startId;

msg.obj = intent;

mServiceHandler.sendMessage(msg);

}

@Override

public int onStartCommand(@Nullable Intent intent, int flags, int startId) {

onStart(intent, startId);

return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;

}

四、Handler 在三方库中的应用

1、EventBus

EventBus 的 Github 上有这么一句介绍:EventBus is a publish/subscribe event bus for Android and Java. 这说明了 EventBus 是普遍适用于 Java 环境的,只是对 Android 系统做了特殊的平台支持而已。EventBus 的四种消息发送策略包含了ThreadMode.MAIN 用于指定在主线程进行消息回调,其内部就是通过 Handler 来实现的

EventBusBuilder 会去尝试获取 MainLooper,如果拿得到的话就可以用来初始化 HandlerPoster,从而实现主线程回调

MainThreadSupport getMainThreadSupport() {

if (mainThreadSupport != null) {

return mainThreadSupport;

} else if (AndroidLogger.isAndroidLogAvailable()) {

Object looperOrNull = getAndroidMainLooperOrNull();

return looperOrNull == null ? null :

new MainThreadSupport.AndroidHandlerMainThreadSupport((Looper) looperOrNull);

} else {

return null;

}

}

static Object getAndroidMainLooperOrNull() {

try {

return Looper.getMainLooper();

学习福利

【Android 详细知识点思维脑图(技能树)】

[外链图片转存中…(img-KAeTCl1E-1719244377953)]

其实Android开发的知识点就那么多,面试问来问去还是那么点东西。所以面试没有其他的诀窍,只看你对这些知识点准备的充分程度。so,出去面试时先看看自己复习到了哪个阶段就好。

虽然 Android 没有前几年火热了,已经过去了会四大组件就能找到高薪职位的时代了。这只能说明 Android 中级以下的岗位饱和了,现在高级工程师还是比较缺少的,很多高级职位给的薪资真的特别高(钱多也不一定能找到合适的),所以努力让自己成为高级工程师才是最重要的。

这里附上上述的面试题相关的几十套字节跳动,京东,小米,腾讯、头条、阿里、美团等公司19年的面试题。把技术点整理成了视频和PDF(实际上比预期多花了不少精力),包含知识脉络 + 诸多细节。

由于篇幅有限,这里以图片的形式给大家展示一小部分。

[外链图片转存中…(img-zl07DTW6-1719244377954)]

网上学习 Android的资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。希望这份系统化的技术体系对大家有一个方向参考。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值