安卓基础组件Looper - 03 java层面的剖析

文章目录

  • workflow
    • 工作线程 准备Looper
      • 创建Looper
        • Activity主线程
        • 其他情况
      • `Looper.prepare()`
        • 大体流程
        • java
          • 申请Looper
          • new Looper
          • MessageQueue初始化
        • native
          • jni
          • NativeMessageQueue
      • `Looper.loop()`
        • 大体流程
        • java
          • 获取Looper
          • 获取msg,处理msg
            • `Looper.loop()`
            • `Looper.loopOnce` (已过时)
          • 获取msg 休眠
        • native
          • jni
          • native
    • 其它线程 发送msg
      • 大体来说
      • 准备Handler
        • 构造函数
        • 重写 handleMessage
        • 重要的成员变量
      • Handler发送Msg
        • 准备Msg
        • Handelr发送msg
          • 主要函数
          • Handler::sendMessage
          • MessageQueue::enqueueMessage
          • nativeWake
    • (仅作了解) 工作线程中执行
      • 唤醒
        • native
        • java
          • `MessageQueue::next()`
          • Looper::loop
      • 唤醒后
        • 获取MSG
        • 处理MSG
          • Handler::dispatchMessage
          • Handler::handleMessage

workflow

工作线程 准备Looper

(app主线程中已经准备好了Looper,可以跳过这一节,直接new Handler)

创建Looper,让Looper运行起来。

  • 调用 Looper.prepare() 初始化 Looper

  • 启动轮询器 Looper.loop() 进入循环休眠状态,对queue进行轮询。

    (保证main函数一直运行,一直存活)主线程looper不允许退出。

    • 当消息达到执行时间

      • message queue队列 不为空,取出

      • message queue队列为空,队列阻塞,

    • 等消息队列调用入队 enqueuer Message方法被调用的时候,唤醒队列。从而取出消息停止阻塞。

创建Looper

Activity主线程

app主线程中,可以直接new Handler,跳过这一整章。

prepareMainLooper(),Activity创建时 主线程的Looper是已经准备好了的。

// frameworks/base/core/java/android/app/ActivityThread.java
public static void main(String[] args) {Looper.prepareMainLooper();// ...
}// frameworks/base/core/java/android/os/Looper.java
@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();}
}
其他情况

在app子线程 / framework中的线程中,想要需要自己做两件事:

  • 首先 要创建Looper Looper.prepare

  • 之后 让Looper运行起来Looper.loop()死循环

// Looper.prepare();  // 子线程
Handler mReportHandler;
mReportHandler = new Handler() {@Overridepublic void handleMessage(Message msg) {}
}
// Looper.loop();     // 子线程

Looper.prepare()

大体流程

Looper.prepare() 首先需要获取当前线程的 Looper

  • 如果有当前线程的Looper,获取该 Looper

  • 如果没有当前线程的Looper,则执行这一步会自动new一个 Looper 并存到线程本地数据区中。

如果涉及创建,则创建Looper过程中的核心内容是:

  • native层
    • 初始化一个 NativeMessageQueue 对象,
      • NativeMessageQueue 继承自MessageQueue
      • 它持有一个Looper指针
    • 初始化 Native 层 Looper 对象
    • 将NativeMessageQueue 对象的地址返回给 Java 层。
  • java层
    • 保存当前线程 Thread 对象。
    • 保存创建的 MessageQueue对象(保存有NativeMessageQueue的地址)
java
申请Looper

从本地线程区获取当前线程的 Looper 对象,没有的话,就初始化一个,并存在线程本地数据区中。

// frameworks/base/core/java/android/os/Looper.javapublic 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));
}// sThreadLocal.get() will return null unless you've called prepare().
@UnsupportedAppUsage
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
new Looper

构造函数中:new 一个 Looper 的过程

  • 创建了一个 MessageQueue对象
  • 获取到了当前线程 Thread 对象。
// frameworks/base/core/java/android/os/Looper.java
private Looper(boolean quitAllowed) {mQueue = new MessageQueue(quitAllowed);  //创建MessageQueue对象mThread = Thread.currentThread();
}
MessageQueue初始化

MessageQueue 的初始化过程:

// frameworks/base/core/java/android/os/MessageQueue.java
MessageQueue(boolean quitAllowed) {mQuitAllowed = quitAllowed;mPtr = nativeInit();	// NativeMessageQueue的地址// 也包含了对epoll的初始化
}
// 调用了 native 方法 nativeInit,对应的 JNI 函数如下
private native static long nativeInit();
native
jni

nativeInit 是一个 Native 方法,对应的 JNI 注册函数如下:

// frameworks/base/core/jni/android_os_MessageQueue.cpp
static const JNINativeMethod gMessageQueueMethods[] = {/* name, signature, funcPtr */{ "nativeInit", "()J", (void*)android_os_MessageQueue_nativeInit },{ "nativeDestroy", "(J)V", (void*)android_os_MessageQueue_nativeDestroy },...
};static struct {jfieldID mPtr;   // native object attached to the DVM MessageQueuejmethodID dispatchEvents;
} gMessageQueueClassInfo;int register_android_os_MessageQueue(JNIEnv* env) {int res = RegisterMethodsOrDie(env, "android/os/MessageQueue", gMessageQueueMethods,NELEM(gMessageQueueMethods));jclass clazz = FindClassOrDie(env, "android/os/MessageQueue");gMessageQueueClassInfo.mPtr = GetFieldIDOrDie(env, clazz, "mPtr", "J");gMessageQueueClassInfo.dispatchEvents = GetMethodIDOrDie(env, clazz,"dispatchEvents", "(II)I");return res;
}
NativeMessageQueue

续上前文,native层这部分的核心内容是

  • 初始化一个 NativeMessageQueue 对象,
    • NativeMessageQueue 继承自MessageQueue
    • 它持有一个Looper指针
  • 初始化 Native 层 Looper 对象
  • 将NativeMessageQueue 对象的地址返回给 Java 层。
// frameworks/base/core/jni/android_os_MessageQueue.cpp
static jlong android_os_MessageQueue_nativeInit(JNIEnv* env, jclass clazz) {NativeMessageQueue* nativeMessageQueue = new NativeMessageQueue();if (!nativeMessageQueue) {jniThrowRuntimeException(env, "Unable to allocate native queue");return 0;}nativeMessageQueue->incStrong(env);return reinterpret_cast<jlong>(nativeMessageQueue);
}// frameworks/base/core/jni/android_os_MessageQueue.cpp
class NativeMessageQueue : public MessageQueue, public LooperCallback {
private:JNIEnv* mPollEnv;jobject mPollObj;jthrowable mExceptionObj;
};
// frameworks/base/core/jni/android_os_MessageQueue.h
class MessageQueue : public virtual RefBase {
protected:sp<Looper> mLooper;
};
// frameworks/base/core/jni/android_os_MessageQueue.cpp
// NativeMessageQueue 构造函数
NativeMessageQueue::NativeMessageQueue() :mPollEnv(NULL), mPollObj(NULL), mExceptionObj(NULL) {// 初始化 Native 层的 loopermLooper = Looper::getForThread();if (mLooper == NULL) {mLooper = new Looper(false);Looper::setForThread(mLooper);}
}

Looper.loop()

Looper.loop()在工作线程执行,MSG的消费,执行,都是在执行该函数的线程中,

(这里的工作线程是指,拥有Looper、处理MSG的线程)

大体流程
Looper::loop()MessageQueue::nextMessageQueue::nativePollOnce
---jni---
android_os_MessageQueue_nativePollOnceNativeMessageQueue::pollOnceLooper::pollOnceLooper::pollInnerepoll_wait # 进入阻塞

初始化工作完成后,看看 Looper.loop() 方法执行的操作:

核心功能就两点:

  • 获取 TLS 存储的 Looper 对象
  • 进入无限循环,调用 loopOnce 进入休眠状态
// frameworks/base/core/java/android/os/Looper.java
public static void loop() {// 获取 TLS 存储的 Looper 对象final Looper me = myLooper();// 进入无限循环, for (;;) {// MessageQueue.next() 获取 Message,可能阻塞Message msg = queue.next(); // might block// 分发消息,调用 handler 中的回调函数msg.target.dispatchMessage(msg); }
}
java
获取Looper

获取 TLS 存储的 Looper 对象很好理解。

// frameworks/base/core/java/android/os/Looper.java
public static void loop() {// 获取 TLS 存储的 Looper 对象final Looper me = myLooper(); me.mInLoop = true;//...
}
public static @Nullable Looper myLooper() {return sThreadLocal.get();
}
// sThreadLocal.get() will return null unless you've called prepare().
@UnsupportedAppUsage
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
获取msg,处理msg

获取msg、处理 msg 的过程,原先被封装在 loop::loopOnce 中。最终都是调用了MessageQueue.next()

具体,见下一章节。

Looper.loop()
// frameworks/base/core/java/android/os/Looper.java
public static void loop() {// 进入无限循环, for (;;) {// MessageQueue.next() 获取 Message,可能阻塞Message msg = queue.next(); // might block// 分发消息,调用 handler 中的回调函数msg.target.dispatchMessage(msg); }
}
Looper.loopOnce (已过时)

实际上,Looper.loopOnce是从MessageQueue中取出一个msg并执行。

但在使用中,无限循环内Looper.loopOnce被多次调用。因此,实际上也不需要这个封装了。直接使用 MessageQueue.next()获取msg,和 Handler .dispatchMessage 处理msg。

调用 loopOnce 进入休眠状态

核心流程

  • 通过 MessageQueue 的 next 方法拿到一个 Message,

    这里可能会阻塞休眠

  • 通过 dispatchMessage 调用 Handler 中的回调方法

// frameworks/base/core/java/android/os/Looper.java
private static boolean loopOnce(final Looper me,final long ident, final int thresholdOverride) {// 获取 Message,可能阻塞Message msg = me.mQueue.next(); if (msg == null) {}// ...try {//分发消息,调用 handler 中的回调函数msg.target.dispatchMessage(msg); if (observer != null) {observer.messageDispatched(token, msg); }dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;// ...msg.recycleUnchecked();return true;
}    
获取msg 休眠

通过 MessageQueue 的 next 方法拿到一个 Message,这里可能会阻塞休眠。

// frameworks/base/core/java/android/os/Looper.java
public static void loop() {// ...// 进入无限循环, for (;;) {// MessageQueue.next() 获取 Message,可能阻塞Message msg = queue.next(); // might block// 分发消息,调用 handler 中的回调函数// 但由于阻塞,这里会在 主线程2 唤醒后才调用// frameworks/base/core/java/android/os/Handler.java// public void dispatchMessage(@NonNull Message msg) {msg.target.dispatchMessage(msg); }
}

MessageQueue.next()

调用 nativePollOnce 陷入 Native 层,进入休眠状态,

// frameworks/base/core/java/android/os/MessageQueue.java
Message next() {final long ptr = mPtr;	// 这个指向 NativeMessageQueue 还记得吗for (;;) {// 陷入 Native 层,进入休眠状态nativePollOnce(ptr, nextPollTimeoutMillis);//......}
}private native void nativePollOnce(long ptr, int timeoutMillis);
native
jni

nativePollOnce 是一个 Native 方法,对应的 JNI 注册函数如下:

// frameworks/base/core/jni/android_os_MessageQueue.cpp
static const JNINativeMethod gMessageQueueMethods[] = {/* name, signature, funcPtr */{ "nativeInit", "()J", (void*)android_os_MessageQueue_nativeInit },// ...{ "nativePollOnce", "(JI)V", (void*)android_os_MessageQueue_nativePollOnce },// ...
};
static struct {jfieldID mPtr;   // native object attached to the DVM MessageQueuejmethodID dispatchEvents;
} gMessageQueueClassInfo;int register_android_os_MessageQueue(JNIEnv* env) {int res = RegisterMethodsOrDie(env, "android/os/MessageQueue", gMessageQueueMethods,NELEM(gMessageQueueMethods));jclass clazz = FindClassOrDie(env, "android/os/MessageQueue");gMessageQueueClassInfo.mPtr = GetFieldIDOrDie(env, clazz, "mPtr", "J");gMessageQueueClassInfo.dispatchEvents = GetMethodIDOrDie(env, clazz,"dispatchEvents", "(II)I");return res;
}
native

通过传入的指针,获取到 Native 层的 NativeMessageQueue 对象,接着调用 NativeMessageQueue 对象的 pollOnce 方法。

// frameworks/base/core/jni/android_os_MessageQueue.cpp
static void android_os_MessageQueue_nativePollOnce(JNIEnv* env, jobject obj,jlong ptr, jint timeoutMillis) {NativeMessageQueue* nativeMessageQueue = reinterpret_cast<NativeMessageQueue*>(ptr);nativeMessageQueue->pollOnce(env, obj, timeoutMillis);
}

Native 层 Looper 的 pollOnce 函数,pollOnce 内部主要是调用 epoll_wait 来进入休眠状态。

// frameworks/base/core/jni/android_os_MessageQueue.cpp
void NativeMessageQueue::pollOnce(JNIEnv* env, jobject pollObj, int timeoutMillis) {// ...// 内部调用 epoll_wait 阻塞mLooper->pollOnce(timeoutMillis);// ...
}

上面已经调用 Native 层 Looper::pollOnce 函数,从而调用 epoll_wait 来进入休眠状态。

// frameworks/core/core/libutils/Looper.cpp
// system/core/libutils/Looper.cpp
int Looper::pollOnce(int timeoutMillis, int* outFd, int* outEvents, void** outData) {for (;;) {while (mResponseIndex < mResponses.size()) {const Response& response = mResponses.itemAt(mResponseIndex++);if (ident >= 0) {... return ident;}}if (result != 0) {return result;}// 如果没有 在 Vector<Response> mResponses; 其中有 sp<LooperCallback> callback;// 则进入下面的休眠状态result = pollInner(timeoutMillis);}
}
// system/core/libutils/Looper.cpp
int Looper::pollInner(int timeoutMillis) {// ...int eventCount = epoll_wait(epollfd.get(), eventItems, EPOLL_MAX_EVENTS, timeoutMillis);

pollOnce 的具体代码分析可以参考上述Looper - native - pollAll 部分,这里不再重复。

至此,我们的线程就下 cpu,进入休眠状态。

其它线程 发送msg

大体来说

初始化一个 Handler 对象,再传入 msg。发送消息大致三步:

  • 初始化一个 Handler 对象,覆写 Handler 的 handleMessage 方法
  • 构建一个 Message 对象
  • 通过 Handler 的 sendMessage 方法发送消息

后续,会执行 handleMessage

准备Handler

在使用 Looper 的线程中通常会初始化一个 Handler 对象,重写其handleMessage方法。

构造函数

Handler时,需要指定 Looper 意味着于此同时确定了对应的MessageQueue。

// frameworks/base/core/java/android/os/Handler.javapublic Handler(@NonNull Looper looper) {this(looper, null, false);}public Handler(@NonNull Looper looper, @Nullable Callback callback) {this(looper, callback, false);}public Handler(@NonNull Looper looper, @Nullable Callback callback, boolean async,boolean shared) {mLooper = looper;mQueue = looper.mQueue;mCallback = callback;mAsynchronous = async;mIsShared = shared;}
重写 handleMessage

Handler.sendMessage 方法调用时,(无论是本线程的Handler还是其它线程的Handler),在MessageQueue中排队后都会调用该HandelerhandleMessage方法。

// new 一个 Handler,覆写 handleMessage 方法
mHandler = new Handler() {  public void handleMessage(Message msg) {//定义消息处理逻辑. }
};
// 典型的关于Handler/Looper的线程
class LooperThread extends Thread {public Handler mHandler;public void run() {Looper.prepare();   // 子线程需要,主线程不需要mHandler = new Handler() {  public void handleMessage(Message msg) {//定义消息处理逻辑. Message msg = Message.obtain();}};Looper.loop();   // 子线程需要,主线程不需要}
}
重要的成员变量

Handler 初始化过程中,要对 Handler 中有两个重要变量进行赋值:

  • mLooper:当前线程的 Looper
  • mQueue: 当前进程的 MessageQueue
public Handler() {this(null, false);
}public Handler(@Nullable Callback callback, boolean async) {// 当前线程的 Looper 对象mLooper = Looper.myLooper();if (mLooper == null) {throw new RuntimeException("Can't create handler inside thread " + Thread.currentThread()+ " that has not called Looper.prepare()");}// 当前进程的 MessageQueue mQueue = mLooper.mQueue;mCallback = callback;mAsynchronous = async;
}

Handler发送Msg

准备Msg

做好以上的准备工作后,准备Message

// 构建一个 Message
Message msg = new Message();
// 获知遵循享元模式:
Message msg = Message.obtain();
msg.what = 2; 
msg.obj = "B"; Message msg = mReportHandler.obtainMessage(MSG_SWITCH_TO_SDR_MEMC);
mReportHandler.sendMessage(message);// 通过 Handler 的 sendMessage 方法发送消息
mHandler.sendMessage(msg);

我们先看看 Message 的初始化过程:

// frameworks/base/core/java/android/os/Message.java
public static Message obtain() {synchronized (sPoolSync) {if (sPool != null) {Message m = sPool;sPool = m.next;m.next = null;m.flags = 0; // clear in-use flagsPoolSize--;return m;}}return new Message();
}

从缓存 sPool 里面取 Message,如果没有的话就 new 一个 Message。

Handelr发送msg
主要函数

子线程 Handler为了发送MSG,归根结底,都是使用了 入队MSG Handler.enqueueMessage()

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Handler::sendMessage

mHandler.sendMessage(msg) 内部继续对传入的 Message 进行进一步赋值。例如:把handler的this指针,赋值给了 msg 的 target 成员,这样调用时就知道应该执行哪一个Handler对应的函数了

// frameworks/base/core/java/android/os/Handler.java
public final boolean sendMessage(@NonNull Message msg) { ... }
public boolean sendMessageAtTime(@NonNull Message msg, long uptimeMillis) {MessageQueue queue = mQueue;return enqueueMessage(queue, msg, uptimeMillis);
}
private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg, long uptimeMillis) {// 注意这里msg.target = this;  // 把handler的this指针,赋值给了msg的 target 成员msg.workSourceUid = ThreadLocalWorkSource.getUid();// 最终调用到 MessageQueue 的 enqueueMessage 方法return queue.enqueueMessage(msg, uptimeMillis);
}
MessageQueue::enqueueMessage

MessageQueue.enqueueMessage 代码有点长,核心逻辑:

  • 把 Message 插入 mMessages 链表
  • 调用 native 方法 nativeWake 唤醒 native 层的 epoll
// frameworks/base/core/java/android/os/MessageQueue.java
boolean enqueueMessage(Message msg, long when) {// 添加msg到链表Message p = mMessages;msg.next = p;mMessages = msg;// We can assume mPtr != 0 because mQuitting is false.// 唤醒Looper,进入nativeif (needWake) {nativeWake(mPtr);}
nativeWake

nativeWake 最终会调用 native 层 mLooper 的 wake 函数,

private native static void nativeWake(long ptr);

向 eventfd 写入数据,唤醒与 eventfd 绑定的 epoll。

// nativeWake 对应的 JNI 函数 
static void android_os_MessageQueue_nativeWake(JNIEnv* env, jclass clazz, jlong ptr) {NativeMessageQueue* nativeMessageQueue = reinterpret_cast<NativeMessageQueue*>(ptr);nativeMessageQueue->wake();
}void NativeMessageQueue::wake() {mLooper->wake();
}// eventfd 写数据,唤醒 epoll
void Looper::wake() {// 大体就是write(mWakeEventFd.get(), &inc, sizeof(uint64_t));
}
// 这个mWakeEventFd就是epoll关注的句柄之一
void Looper::rebuildEpollLocked() {struct epoll_event eventItem;memset(& eventItem, 0, sizeof(epoll_event));eventItem.data.fd = mWakeEventFd.get();
// 之前陷入阻塞,也是和eventItems有关
int Looper::pollInner(int timeoutMillis) {// ...epoll_wait(epollfd.get(), eventItems, EPOLL_MAX_EVENTS, timeoutMillis);

接下来我们就来看看 epoll 唤醒以后的流程。

(仅作了解) 工作线程中执行

以下仅作了解,一般来说使用时不需要关心。

之前我们 或被动,或主动的在一个/一些线程中调用了 Looper::loop()

在线程(一般是主线程)中执行Looper.loop(),则这个线程就是“工作线程”。可以认为是MSG的消费者:所有的MSG的消费,执行都是在该线程中,

  • 调用了 Looper::loop() 的线程,会从进程的MessageQueue中取出、处理MSG,并执行Hanlder的重载。

  • 因为 MessageQueue 是线程安全的,所以可以有多个线程调用 Looper::loop(),这些线程并行的处理Hanlder。

唤醒

native

Looper::pollInner 解除阻塞后,一路上函数调用栈恢复。相对应的,Java 层的 next 方法被唤醒。

Looper::loop()MessageQueue::nextMessageQueue::nativePollOnce
---jni---
android_os_MessageQueue_nativePollOnceNativeMessageQueue::pollOnceLooper::pollOnceLooper::pollInnerepoll_wait # 解除阻塞
java

唤醒后,一般的流程就是从 mMessages 中找个合适的 message 返回,

MessageQueue::next()
// frameworks/base/core/java/android/os/MessageQueue.java
public final class MessageQueue {
Message next() {// ...for (;;) {// 在这个位置陷入 Native 层,进入休眠状态// 所以也是从这个位置唤醒nativePollOnce(ptr, nextPollTimeoutMillis);// 从 mMessages 链表中选取一个合适的 message 返回synchronized (this) {// Try to retrieve the next message.  Return if found.final long now = SystemClock.uptimeMillis();Message prevMsg = null;Message msg = mMessages;
Looper::loop

再继续返回后进入上一层

// frameworks/base/core/java/android/os/Looper.java
// frameworks/base/core/java/android/os/Looper.java
public static void loop() {// 进入无限循环, for (;;) {// 从这里恢复阻塞Message msg = queue.next(); // MessageQueue.next() 获取 Message// 分发消息,调用 handler 中的回调函数msg.target.dispatchMessage(msg); }
}

代码很多,核心的流程只有两点:

  • next() 处返回一个合适的 message
  • 通过 msg.target.dispatchMessage(msg) 调用 handler 中的回调

至此,整个 Looper 流程就走完了。

唤醒后

Looper.loop() # 唤醒后执行如下步骤MessageQueue.next()			# 获取msgHandler.dispatchMessage 	# 处理msgHandler.handleMessage
获取MSG

Looper不断的调用,从消息队列中获取消息使用for循环

// frameworks/base/core/java/android/os/Looper.java
/*** 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();for (;;) {// MessageQueue.next() 获取 Message,可能阻塞Message msg = queue.next(); // might blockif (msg == null) {// No message indicates that the message queue is quitting.return;}// 处理MSG,下文会介绍
处理MSG

主线程处理,之前传入MessageQueue的MSG,在主线程中执行。

// frameworks/base/core/java/android/os/Looper.java
public static void loop() {// ...// 由于阻塞,在 主线程唤醒后 开始处理获得的msg// 分发消息,调用 handler 中的回调函数msg.target.dispatchMessage(msg); }
}

Message持有Handler,Handler持有Callback。

// frameworks/base/core/java/android/os/Message.java
public final class Message implements Parcelable {@UnsupportedAppUsage// 这个msg.target就是Handler的this指针,是其他线程初始化Msg时自动填入/*package*/ Handler target;
// frameworks/base/core/java/android/os/Handler.java
public class Handler {public void handleMessage(@NonNull Message msg) {}final Callback mCallback;public interface Callback {boolean handleMessage(@NonNull Message msg);}
}
Handler::dispatchMessage

Handler的方法 handleMessage dispatchMessage 等,最终会调用到以下方法

// frameworks/base/core/java/android/os/Handler.java
/*** Handle system messages here.*/
public void dispatchMessage(@NonNull Message msg) {if (msg.callback != null) {handleCallback(msg);	// 1 Runnable run} else {if (mCallback != null) { // 2 final Callback mCallback;if (mCallback.handleMessage(msg)) {return;}}handleMessage(msg);}
}private static void handleCallback(Message message) {message.callback.run(); // 1 Runnable callback;
}public Handler(@Nullable Callback callback, boolean async) {mLooper = Looper.myLooper();mQueue = mLooper.mQueue;mCallback = callback;		// 2 执行这里的 handleMessage
}
public void handleMessage(@NonNull Message msg) { // 3 执行子类的重载(通常说的就是这里)
}
Handler::handleMessage

这也是初始化Handler时,需要重写 handleMessage 的原因

在使用 Looper 的线程中通常会初始化一个 Handler 对象:

// new 一个 Handler,覆写 handleMessage 方法
mHandler = new Handler() {  public void handleMessage(Message msg) {//定义消息处理逻辑. }
};

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/71292.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

docker关闭mysql端口映射的使用

需求 项目中的数据库为mysql&#xff0c;如果将端口映射到宿主机上&#xff0c;容易被工具扫描出&#xff0c;且随着国产化的进程推进&#xff0c;mysql将不被允许。为了提高安全性与满足项目需求&#xff0c;这里采用隐藏mysql端口方式&#xff0c;不映射宿主机端口&#xff…

fps项目总结:动画蓝图

文章目录 状态不同状态的并存性。 状态 不同状态的并存性。

【计算机网络】考研复试高频知识点总结

文章目录 一、基础概念1、计算机⽹络的定义2、计算机⽹络的目标3、计算机⽹络的组成4、计算机⽹络的分类5、计算机⽹络的拓扑结构6、计算机⽹络的协议7、计算机⽹络的分层结构8、OSI 参考模型9、TCP/IP 参考模型10、五层协议体系结构 二、物理层1、物理层的功能2、传输媒体3、 …

健康医疗大数据——医疗影像

一、 项目概述 1.1 项目概述 1.2 项目框架 1.3 项目环境 1.4 项目需求 二、项目调试与运行 2.1需求分析 2.2具体实现 三、项目总结 项目概述 项目概述 本项目旨在应用大数据技术于医疗影像领域&#xff0c;通过实训培养团队成员对医疗大数据处理和分析的实际…

Mybatis-Plus 配合Sharding-JDBC 实现分库分表

在现代数据库设计中&#xff0c;随着数据量的不断增长&#xff0c;单一数据库往往无法满足高并发、高性能的业务需求。因此&#xff0c;分库分表策略成为数据库架构优化的重要手段。本文将介绍分库分表的基本概念&#xff0c;并重点探讨垂直拆分与水平拆分的区别&#xff0c;以…

3.1、密码学基础

目录 密码学概念与法律密码安全分析密码体制分类 - 私钥密码/对称密码体制密码体制分类 - 公钥密码/非对称密码体制密码体制分类 - 混合密码体制 密码学概念与法律 密码学主要是由密码编码以及密码分析两个部分组成&#xff0c;密码编码就是加密&#xff0c;密码分析就是把我们…

同一个问题对比

问题&#xff1a;如何看torch 和torchvision版本&#xff0c;如何看CUDA的版本&#xff0c;我是ubuntu的系统。 chat复制粘贴很好用。 一、chat 在 Ubuntu 下&#xff0c;你可以使用以下命令来检查 Torch&#xff08;PyTorch&#xff09;、Torchvision 以及 CUDA 版本信息。 …

Unity 对象池技术

介绍 是什么&#xff1f; 在开始时初始化若干对象&#xff0c;将它们存到对象池中。需要使用的时候从对象池中取出&#xff0c;使用完后重新放回对象池中。 优点 可以避免频繁创建和销毁对象带来性能消耗。 适用场景 如果需要对某种对象进行频繁创建和销毁时&#xff0c;例…

Github 2025-03-03 开源项目周报Top14

根据Github Trendings的统计,本周(2025-03-03统计)共有14个项目上榜。根据开发语言中项目的数量,汇总情况如下: 开发语言项目数量Python项目5TypeScript项目4Jupyter Notebook项目3Go项目2JavaScript项目2C++项目2Vue项目1Rust项目1Dify.AI: 开源的LLM应用程序开发平台 创建…

spark 虚拟机基本命令(2)

cp 命令&#xff0c;复制 格式&#xff1a;cp 源文件 目标文件 cp -r 命令 复制目录 格式&#xff1a;cp -r 源文件 目标文件夹 mv 命令&#xff0c;重命名和移动 格式&#xff1a;mv 源文件 目标文件 说明&#xff1a;若源文件和目标文件在同一个目录下&#xff0c;就是重命…

阿里管理三板斧课程和管理工具包(视频精讲+工具文档).zip

阿里管理三板斧课程和管理工具包&#xff08;视频精讲工具文档&#xff09;&#xff0c;共18课。 阿里管理三板斧工具包 阿里绩效考核文档 阿里人力资源实践全集文档 阿里文化构建工具包 阿里正委体系工具包 阿里三板斧.pdf 阿里三板斧-学员手册.pdf 第1集 三板斧的底层逻辑.…

Java RPC(远程过程调用)技术详解

在当今分布式系统盛行的时代&#xff0c;服务间的通信变得至关重要。Java RPC&#xff08;Remote Procedure Call&#xff0c;远程过程调用&#xff09;作为一种高效、透明的远程通信手段&#xff0c;在微服务架构、分布式计算等领域扮演着重要角色。本文将深入介绍Java RPC的基…

Java【多线程】(2)线程属性与线程安全

目录 1.前言 2.正文 2.1线程的进阶实现 2.2线程的核心属性 2.3线程安全 2.3.1线程安全问题的原因 2.3.2加锁和互斥 2.3.3可重入&#xff08;如何自己实现可重入锁&#xff09; 2.4.4死锁&#xff08;三种情况&#xff09; 2.4.4.1第一种情况 2.4.4.2第二种情况 2.4…

深入解析Java虚拟机(JVM)的核心组成

深入解析Java虚拟机&#xff08;JVM&#xff09;的核心组成 Java虚拟机&#xff08;JVM&#xff09;作为Java语言跨平台的核心实现&#xff0c;其架构设计精妙而复杂。理解JVM的组成部分&#xff0c;是掌握Java内存管理、性能调优和问题排查的关键。本文将从四大核心模块剖析J…

16.8 LangChain RAG 实战指南:构建知识驱动的智能问答系统

LangChain RAG 实战指南:构建知识驱动的智能问答系统 关键词:RAG 实现、向量检索优化、多源知识融合、LCEL 链式编排、生产级知识库 1. RAG 核心架构解析 1.1 技术实现流程图 #mermaid-svg-8Xfi8BXbIrAXc35l {font-family:"trebuchet ms",verdana,arial,sans-ser…

DeepSeek如何快速开发PDF转Word软件

一、引言 如今&#xff0c;在线工具的普及让PDF转Word成为了一个常见需求&#xff0c;常见的PDF转Word工具有收费的WPS&#xff0c;免费的有PDFGear&#xff0c;以及在线工具SmallPDF、iLovePDF、24PDF等。然而&#xff0c;大多数免费在线转换工具存在严重隐私风险——文件需上…

[KEIL]单片机技巧 01

1、查看外设寄存器的值 配合对应的芯片开发手册以查看寄存器及其每一位的意义&#xff0c;可以解决90%以上的单纯的片内外设bug&#xff0c;学会如何通过寄存器的值来排外设上的蛊是嵌入式开发从小白到入门的重要一步&#xff0c;一定要善于使用这个工具&#xff0c;而不是外设…

Redis详解(实战 + 面试)

目录 Redis 是单线程的&#xff01;为什么 Redis-Key(操作redis的key命令) String 扩展字符串操作命令 数字增长命令 字符串范围range命令 设置过期时间命令 批量设置值 string设置对象,但最好使用hash来存储对象 组合命令getset,先get然后在set Hash hash命令: h…

计算机视觉(opencv-python)入门之图像的读取,显示,与保存

在计算机视觉领域&#xff0c;Python的cv2库是一个不可或缺的工具&#xff0c;它提供了丰富的图像处理功能。作为OpenCV的Python接口&#xff0c;cv2使得图像处理的实现变得简单而高效。 示例图片 目录 opencv获取方式 图像基本知识 颜色空间 RGB HSV 图像格式 BMP格式 …

【SpringBoot】【log】 自定义logback日志配置

前言&#xff1a;默认情况下&#xff0c;SpringBoot内部使用logback作为系统日志实现的框架&#xff0c;将日志输出到控制台&#xff0c;不会写到日志文件。如果在application.properties或application.yml配置&#xff0c;这样只能配置简单的场景&#xff0c;保存路径、日志格…