收废品做网站,建网站 考虑,生态城门户网站 建设动态,织梦确定网站风格1、前言
在 Android 开发中#xff0c;Handler 的机制和运行原理这方面的知识可以说是每个人都需要熟悉的。这不仅是因为 Handler 是 Android 应用的基石之一#xff0c;也因为 Handler 整体设计上也是十分优秀的。接下来我就梳理总结一下常见的 Handler 相关知识点。
2、基…1、前言
在 Android 开发中Handler 的机制和运行原理这方面的知识可以说是每个人都需要熟悉的。这不仅是因为 Handler 是 Android 应用的基石之一也因为 Handler 整体设计上也是十分优秀的。接下来我就梳理总结一下常见的 Handler 相关知识点。
2、基本使用GPT 创建Handler对象要使用Handler首先需要创建一个Handler对象。Handler可以在UI线程或其他线程中创建但通常在UI线程中创建以便将消息发送到UI线程。 Handler handler new Handler();发送消息要将消息发送到Handler可以使用Handler的post方法或sendMessage方法。通常您将使用post方法执行一个Runnable任务。 handler.post(new Runnable() {Overridepublic void run() {// 在UI线程执行的任务// 可以更新UI元素}
});或者使用sendMessage方法 Message message handler.obtainMessage();
message.what MY_MESSAGE_CODE;
handler.sendMessage(message);处理消息在Handler所在的线程中可以覆盖handleMessage方法来处理消息。通常您需要继承Handler类并重写handleMessage方法。 class MyHandler extends Handler {Overridepublic void handleMessage(Message msg) {switch (msg.what) {case MY_MESSAGE_CODE:// 处理消息break;// 可以处理更多不同消息类型}}
}关联Handler与LooperHandler需要与Looper消息循环关联以便能够在消息队列中接收和处理消息。通常UI线程已经有一个与之关联的Looper所以在UI线程中创建Handler不需要额外配置。但如果您在其他线程中创建Handler需要先创建一个Looper。 Looper.prepare(); // 创建一个新的Looper
Handler handler new Handler(); // 关联Handler与新的Looper
Looper.loop(); // 开始消息循环必须调用以使Looper活动从后台线程向UI线程发送消息通常情况下Handler最常用于在后台线程执行任务后更新UI线程。例如如果您在后台线程中进行网络请求请求完成后可以使用Handler将结果传递给UI线程以更新UI元素。 new Thread(new Runnable() {Overridepublic void run() {// 后台线程执行任务// ...// 任务完成后使用Handler将结果传递给UI线程handler.post(new Runnable() {Overridepublic void run() {// 更新UI}});}
}).start();这些是Android Handler的基本用法。Handler是Android中处理异步任务和多线程通信的重要工具可以确保UI更新等操作在UI线程中执行从而避免应用程序崩溃或出现不稳定行为。
3、流程梳理
从 2 中可以看出 Handler 有两种发送信息的方式。第一种是发送 Message第二种是直接 post runnable。我们分别看下两种方法的源码处理。
3.1 获取 Message 的方式
发送 Message 首先需要获取一个 Message当然可以直接 new 一个对象出来但是也可以通过 Message.obtain() 方法来获取一个消息池里面的消息对象。 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();}可见这种方式可以减少内存分配和垃圾回收的开销因为它避免了频繁创建和销毁 Message 对象而是重复使用已有对象。这在Android中的消息处理机制中非常有用因为通常会有大量的消息对象需要创建和处理如Handler中的消息队列。因此Message.obtain 方法的使用方式类似于享元模式通过共享可复用的对象来减少系统资源的消耗提高性能。这有助于更有效地管理Android应用程序中的消息处理。
3.2 压入消息队列
这里面接着往下看会通过 Message.enqueueMessage 将这个消息压入消息队列中。这里就要说下这个 Looper 的获取方式了。查看代码可以看到 Looper 是从 ThreadLocal 里面获取到的。ThreadLocal 保证了在每个线程内只有一个 Looper 对象。到这里消息已经进入消息队列中了。
final MessageQueue mQueue;public Handler(Nullable Callback callback, boolean async) {mLooper Looper.myLooper();if (mLooper null) {throw new RuntimeException(Cant create handler inside thread Thread.currentThread() that has not called Looper.prepare());}mQueue mLooper.mQueue;
}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);
}// Looper.java
static final ThreadLocalLooper sThreadLocal new ThreadLocalLooper();public static Nullable Looper myLooper() {return sThreadLocal.get();
}接下来就是取消息的过程取消息的方法是在 Looper.java 的 loop 方法中如果是在子线程使用的情况下需要自己手动启动 Looper.loop 方法开启轮询。主线程中则是由系统在 ActivityThread.java 的 main 方法里面为我们开启了轮询。
public static void main(String[] args) {Looper.prepareMainLooper();ActivityThread thread new ActivityThread();thread.attach(false, startSeq);if (sMainThreadHandler null) {sMainThreadHandler thread.getHandler();}Looper.loop();throw new RuntimeException(Main thread loop unexpectedly exited);
}接着看 loop 方法这里面是一个死循环会一直从消息队列中获取消息。获取到了后会执行 msg.target.dispatchMessage(msg); 方法。可以看到在 android30 里面已经系统已经集成了检测耗时消息的机制logSlowDelivery 相关代码。
public static void loop() {final Looper me myLooper();if (me null) {throw new RuntimeException(No Looper; Looper.prepare() wasnt called on this thread.);}me.mInLoop true;final MessageQueue queue me.mQueue;for (;;) {Message msg queue.next(); // might blockif (msg null) {// No message indicates that the message queue is quitting.return;}// Make sure the observer wont 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 wasnt 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();}
}这里面 target 在 enqueueMessage 已经设置成了发送 handler。所以执行逻辑会回到 handler 的 dispatchMessage 方法里面。
3.3 消息执行
这里面可以先看一下 post runnable 方法。其实还是发送的 callback 是 runnable 的 Message。所以处理流程都是统一的。
// Handler.java
public void dispatchMessage(NonNull Message msg) {// 1、如果 msg 存在 callback 则直接执行post 方式都会走到这里if (msg.callback ! null) {handleCallback(msg);} else {// 2、mCallback 不为空则进入这里处理这个 mCallback 可以通过构造方法传入if (mCallback ! null) {if (mCallback.handleMessage(msg)) {return;}}// 3、最后会走到自身 handleMessage 方法这个方法可以通过继承重写handleMessage(msg);}
}public final boolean post(NonNull Runnable r) {return sendMessageDelayed(getPostMessage(r), 0);
}private static Message getPostMessage(Runnable r) {Message m Message.obtain();m.callback r;return m;
}
走到这里可以发现我们的 Handler 机制在 Java 层已经完全梳理一遍了。下面继续看下 native 层的部分。这里就引入了一个经典问题那就是主线程 loop 方法是死循环系统为什么不会卡死呢 从源码可以看到 loop 方法里面调用了消息队列的 next 方法这里面会调用继续调用 native 的 nativePollOnce(ptr, nextPollTimeoutMillis); 方法。这里面会通过 epoll 机制当等待消息的时候会释放系统资源当被唤醒时再继续操作。唤醒操作 nativeWake(mPtr); 。
// 0 立即返回2000 等待 2s-1 永久休眠
static void android_os_MessageQueue_nativePollOnce(JNIEnv* env, jobject obj,jlong ptr, jint timeoutMillis) {NativeMessageQueue* nativeMessageQueue reinterpret_castNativeMessageQueue*(ptr);nativeMessageQueue-pollOnce(env, obj, timeoutMillis);
}// 底层通过 epoll 的方式监听读端会进入等待等待写入端有数据写入
int eventCount epoll_wait(mEpollFd.get(), eventItems, EPOLL_MAX_EVENTS, timeoutMillis);// 插入消息的时候会调用 wake 方法会写入了 1唤醒
uint64_t inc 1;
ssize_t nWrite TEMP_FAILURE_RETRY(write(mWakeEventFd.get(), inc, sizeof(uint64_t)));
3.4 消息屏障
消息屏障的典型用例是在UI线程中执行UI更新以确保UI更新的操作按照它们被提交的顺序执行。例如如果在后台线程中进行了多次UI更新并将这些更新消息发送到UI线程的消息队列中可以使用 sendMessageAtFrontOfQueue 方法来确保这些UI更新按照它们被发送的顺序执行从而避免UI显示的不一致性。通过 sendMessageAtFrontOfQueue 方法会将时间设置为 0在进入消息队列的过程中会直接插入到队列头部所以可以确保执行优先级较高。
3.5 IdleHandler
IdleHandler是Android中的一个回调接口它用于在主线程空闲时执行任务。当主线程没有处理消息时即处于空闲状态IdleHandler中的回调方法将被触发允许您执行一些耗时较长的任务而不会影响到UI的响应性。这在某些情况下非常有用例如在后台预加载数据或执行其他非UI相关的工作。
// 创建一个IdleHandler
MessageQueue.IdleHandler idleHandler new MessageQueue.IdleHandler() {Overridepublic boolean queueIdle() {// 在主线程空闲时执行的任务// 可以执行一些耗时操作不会阻塞UI线程return false; // 返回true表示继续监听false表示不再监听}
};// 注册IdleHandler
Looper.myQueue().addIdleHandler(idleHandler);这块处理逻辑是在 MessageQueue 的 next 方法内部。
4、总结
到这里基本梳理了 Handler 的一些使用和原理虽然各种框架和 Kotlin 都可以很方便的执行切换线程的操作了但是这些原理性的东西还是值得我们学习并了解的。