android源码学习-Handler机制及其六个核心点

article/2025/11/6 1:15:10

前言:

该文属于安卓源码探究专栏中的文章,专栏所有文章清单链接如下,欢迎大家阅读。

安卓源码探究

、Handler机制介绍

1.1 Handler是什么?

Handler是安卓中很常用的一种机制,其主要用于线程之间的通信。

1.2 为什么要了解Handler原理?

之前我在一个群里面聊天,说面试经常会问到handler,然后就被喷了,说现在都是MVVM,ViewBinding一类的架构,谁还用handler呀。确实,现在安卓上面封装的框架越来越多,也让我们使用起来越来越轻松,使用Handler的场景也会越来越少。但是使用的少,不代表没有用。现有的这些框架,甚至可预见的未来框架,都是基于Handler机制的。

打一个比方,我们普通人开车,也许只要会开就行,不必了解汽车的原理。但是如果你想成为一个赛车手,那么对汽车运行的机制和原理,还是要有一定的了解的。

最后,了解handler原理有哪些好处呢?

1.handler机制很经典,这种我们平常敲代码的过程中也可以视场景使用。

2.handler其实除了普通的同步消息,还有屏障消息和异步消息的,我们也可以视场景去使用。

3.handler还提供了闲时机制IdelHandler。

4.通过handler可以帮助我们排查到底是哪里的代码导致卡顿。

上面的这些点,后面文章中都会有详细的讲解。

1.3 Handler对于面试中的作用?

同时Handler也是面试当中经常会被问到的一种安卓机制。

我总结了一下,大约有六个点会被高频问到,这六个技术点特意用红字标注,属于面试中很容易被问到的技术点。

例如:技术点1

、Handler原理概述

如下图所示:安卓中Handler的机制是子线程中获取一个Handler对象,通过这个Handler对象像MessageQueue中发送任务。主线程中开启一个无限死循环,不断的从MessageQueue中取Message任务,如果取到了就执行Messagez中的任务,如果没有取到则进入休眠状态。

 几个涉及到的重要的对象

Handler:一般要在主线程中创建,持有一个主线程的Looper,负责向主线程进行message分发。

Message:可以理解为task,任务执行的单元。

MessageQueue:里面是一个单链表,单链表存储Message任务。

Looper:负责处理Message任务,其loop方法在主线程中开启无限循环。

三、Message添加的流程

3.1添加Meesage的写法

我们可以看到很多种添加message的写法,其实最终都是调用了sendMessageDelayed方法。

一般写法主要有以下几种:

写法一:sendMessageDelayed(Message msg, long delayMillis)

  public final boolean sendMessageDelayed(@NonNull Message msg, long delayMillis) {if (delayMillis < 0) {delayMillis = 0;}return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);}

写法二:sendMessage(Message msg)

 public final boolean sendMessage(@NonNull Message msg) {return sendMessageDelayed(msg, 0);}

其实是调用了sendMessageDelayed方法。

写法三:

mHandler.post(new Runnable() {@Overridepublic void run() {//do something }});

其实我们点进去看一下,

sendMessageDelayed(getPostMessage(r), 0);

再看getPostMessage方法:

    private static Message getPostMessage(Runnable r) {Message m = Message.obtain();m.callback = r;return m;}

好吧。其实也是构建一个Message,然后sendMessage出去。

3.2 sendMessageDelayed方法

 public final boolean sendMessageDelayed(@NonNull Message msg, long delayMillis) {if (delayMillis < 0) {delayMillis = 0;}return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);}

调用sendMessageAtTime时传入的第二个参数为执行时间。

这个执行时间是当前时间+延迟时间算出来的。

这里有涉及到技术点1:修改手机时间会不会影响Message的执行,当前使用使用的是SystemClock.uptimeMillis(),这个指的是手机开机后系统非深度休眠时间,而不是手机时间。所以手机上修改当前时间是不会影响Message执行的。一般我们也可以通过这个值来获取手机开机多久。

3.3 sendMessageAtTime

1.sendMessageAtTime里面就做一个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);}

3.4 enqueueMessage方法

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

把Message加入到queue里

3.5 enqueueMessage方法

1.这里面代码看起来有点多和乱。但是核心逻辑就是按照执行时间从前到后的纬度,把当前的message节点插入到单链表当中。技术点2:链表是按照什么方式来排列的。执行时间顺序

2.Message中有一个参数when,这里会在被添加到链表的时候赋值,记录的就是执行时间。

3.同时通过nativeWake唤醒休眠。为何休眠,下面第四章时会讲到,一般无待执行Message时会进入休眠释放CPU资源。

boolean enqueueMessage(Message msg, long when) {if (msg.target == null) {throw new IllegalArgumentException("Message must have a target.");}synchronized (this) {if (msg.isInUse()) {throw new IllegalStateException(msg + " This message is already in use.");}if (mQuitting) {IllegalStateException e = new IllegalStateException(msg.target + " sending message to a Handler on a dead thread");Log.w(TAG, e.getMessage(), e);msg.recycle();return false;}msg.markInUse();msg.when = when;Message p = mMessages;boolean needWake;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 {// Inserted within the middle of the queue.  Usually we don't have to wake// up the event queue unless there is a barrier at the head of the queue// and the message is the earliest asynchronous message in the queue.needWake = mBlocked && p.target == null && msg.isAsynchronous();Message prev;for (;;) {prev = p;p = p.next;if (p == null || when < p.when) {break;}if (needWake && p.isAsynchronous()) {needWake = false;}}msg.next = p; // invariant: p == prev.nextprev.next = msg;}// We can assume mPtr != 0 because mQuitting is false.if (needWake) {nativeWake(mPtr);}}return true;}

至此,message就插入到MessageQueue当中,插入流程完成。

四、Message被执行的流程

4.1 调用Looper.prepare()与当前线程进行绑定。

一般我们是不需要调用prepare方法的,因为应用启动后,在ActivityThread的main方法中,已经帮我们调用了Looper.prepare方法了。

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

prepare的核心逻辑就是与当前线程进行绑定,通过ThreadLocal。保证一个Looper只会绑定唯一的一个Thread。

4.2 Looper.loop启动无限循环

调用Looper.loop方法,启动无限循环。这里会做一个检查,如果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.");}...不重要代码忽略for (;;) { //启动无限循环if (!loopOnce(me, ident, thresholdOverride)) {return;}}}

4.3 获取到message去尝试执行

1.会调用MessageQueue.next()里面尝试获取message,如果获取不到会被阻塞。具体我们下一章节分析。

2.如果获取不到消息并且返回了,这时说明应用处于退出状态了,所以循环也关闭。

3.msg.target.dispatchMessage() 转交给Handler去真正的处理消息,由于当前的执行线程是主线程,所以dispatchMessage里面执行的回调也是在主线程。

4.我们可以看到代码中有logging进行输入打印,这一点对我们性能监控很有帮助,具体我们第七章节讲解。

5.msg.recycleUnChecked标记msg已被使用,进入可复用状态。

4.4 dispatchMessage执行message

这里有两种回调的方式,

方式一直接执行Message中的callBack;

方式二自定义Handler,重写其handleMessage方法。

这里我们可以明显看到,callBack的优先级会更高。这是技术点3:CallBack和handleMessage哪个会优先被执行

public void dispatchMessage(@NonNull Message msg) {if (msg.callback != null) {handleCallback(msg);} else {if (mCallback != null) {if (mCallback.handleMessage(msg)) {return;}}handleMessage(msg);}}

handleCallBack中就是直接通过

message.callback.run();

执行Message的runnable任务。PS:run()是Runnable接口中定义的方法

五、MessageQueue.next()取消息

先看代码,具体分为下面的几个环节。

Message next() {...for (;;) {if (nextPollTimeoutMillis != 0) {Binder.flushPendingCommands();}nativePollOnce(ptr, nextPollTimeoutMillis);//4.1 nativePollOnecesynchronized (this) {// Try to retrieve the next message.  Return if found.final long now = SystemClock.uptimeMillis();Message prevMsg = null;Message msg = mMessages;...屏障消息相关代码先忽略if (msg != null) {//4.2 寻找可用messageif (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;}...idelHandler相关代码先忽略}}

5.1 nativePollOnce方法

这是一个native方法,阻塞的。我们上面所说的CPU休眠也就是通过nativePollOnce这个机制来实现的。调用这个方法的时候,CPU资源会被释放掉,直到被外界唤醒。底层的实现原理对应的是linux的epoll机制,这篇文章我们就不详细去分析了。我们只要知道其方法的功能就可以了。

nativePollOnce有两个参数,ptr和timeoutMillis

ptr可以理解为一个native标记的唯一值,用来识别绑定线程的。

timeoutMillis是超时时间。主要分为三种-1,0,>0。

=-1的时候是处于无限阻塞的状态,除非被外界唤醒。

=0的时候不会阻塞,会立马执行。

>0时会阻塞对应的时间,然后释放阻塞状态。

PS:nativePollOnce的native实现其实和安卓层有一些类似,也有一个阻塞的循环队列的。底层的实现机制是epoll,由于native层不是本文的核心,这里就不扩展介绍了,感兴趣的同学可以留言,我

5.2 遍历链表,寻找可以用的message

1.因为链表我们插入时是按照执行时间去插入的,所以最先执行的Message一定在链表的最头部;

2.首先获取一下当前系统非休眠状态时间;

3.首先尝试获取链表的头部,那么说明链表中无数据。则把nextPollTimeoutMillis赋值为-1,下一个循环时,就会进入无限阻塞状态,直接被唤醒。这里对应的就是2.5 章节时讲到的native唤醒机制。

4.如果头节点不为空,则判断其执行时间和当前时间做比较;

5.如果其执行时间小于当前时间,则计算差值nextPollTimeoutMillis。并且会跳出此次Message选择流程。并且在下一次循环的时候,nativePollOnce会使用该值休眠对应的时间。保证休眠时间一到,正好到了头节点的执行时间。

6.如果其执行时间是否大于当前时间,则表明该节点是可以被执行的。把头节点改成下一级节点。并且通过Message.markInUse标记当前Message已经被使用。

7.返回上一步查到的message对象

五、异步消息/屏障消息

技术点4:屏障异步消息的实现机制

1 屏障消息其实就是target为空的Message消息。

2 异步消息一定要搭配屏障消息来使用。

3 如果头节点为屏障消息时,则会从消息链表中从前向后依次寻找异步消息,找到异步消息则跳出循环。

4 并且从链表中移除当前的异步节点,并且把异步节点的前后两个节点关联起来。

5 这里要注意,头部节点仍然是屏障消息没有被移除。所以正常消息仍然是不会被执行的。

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;//把异步消息的next赋值给前面那个节点的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;}

六、IdleHandler

技术点5:IdelHandler执行时机

6.1 IdleHandler介绍

顾名思义,就是闲时任务。当前没有要被执行的Message时,则会触发执行IdelHandler,我们一般可以把那些需要在主线程执行,但是优先级不高的任务放在IdelHandler里面执行,比如预加载下一个页面,二屏加载等等。

6.2 IdleHandler何时触发

看代码我可以知道,获取Message的时候,如果没有获取到Message时,则会进入执行IdleHandler的流程。

Message next() {...代码省略for (;;) {if (DEBUG) Log.v(TAG, "Returning message: " + msg);msg.markInUse();return msg;  //如果取到了消息则返回}} else {// No more messages.nextPollTimeoutMillis = -1;}...//如果没有取到消息,则会执行下面的逻辑// 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 handlerboolean 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;}}

6.3 IdleHandler执行流程

IdleHandler可以有多个,在MeesageQueue中由mIdelHandlers存储每执行时,转换为数组依次执行。

 pendingIdleHandlerCount = mIdleHandlers.size(); 
mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);for (int i = 0; i < pendingIdleHandlerCount; i++) {final IdleHandler idler = mPendingIdleHandlers[i];mPendingIdleHandlers[i] = null; // release the reference to the handlerboolean keep = false;try {keep = idler.queueIdle();} catch (Throwable t) {Log.wtf(TAG, "IdleHandler threw exception", t);}if (!keep) {synchronized (this) {mIdleHandlers.remove(idler);}}}

这里要注意一点,IdleHandler的回调只会执行一次,执行完成后会被移除。

七、Message执行监听

技术点6:如何对主线程卡顿问题进行监控

7.1 触发原理

3.3章节的时候,我们有提到logging这个对象。我们在回头看一下代码,这里我们很清楚的看到,logging会在message执行前和执行后分别调用。那这两次打印之间的时间,就可以认为是Message中回调的执行时间。

private static boolean loopOnce(final Looper me,final long ident, final int thresholdOverride) {...省略代码final Printer logging = me.mLogging;if (logging != null) {logging.println(">>>>> Dispatching to " + msg.target + " "+ msg.callback + ": " + msg.what);}...省略代码try {msg.target.dispatchMessage(msg);if (observer != null) {observer.messageDispatched(token, msg);}dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;} catch (Exception exception) {...省略代码if (logging != null) {logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);}}

7.2 使用方式

我们看一下logging对应的Printer类,其实就是一个接口,

public interface Printer {/*** Write a line of text to the output.  There is no need to terminate* the given string with a newline.*/void println(String x);
}

并且我们发现logging是允许我们主动去设置的 。使用时取的是Looper.mLogging对象。

而setMessageLogging方法正好是设置mLogging的。

 public void setMessageLogging(@Nullable Printer printer) {mLogging = printer;}

所以我们只要创建一个Printer对象,然后注册到Looper里,那么通过两次回调的时间,就可以判断哪个Meesage执行超时了。如下代码就可以监控主线程所有Message消息执行超过100毫秒的场景。

//声明Printer对象
private Printer printer = new Printer() {@Overridepublic void println(String it) {long currentTimeMillis = System.currentTimeMillis();//其实这里应该是一一对应判断的,但是由于是运行主线程中,所以Dispatching之后一定是Finished,依次执行if (it.contains("Dispatching")) {lastFrameTime = currentTimeMillis;return;}if (it.contains("Finished")) {long useTime = currentTimeMillis - lastFrameTime;//记录时间if (useTime > 100) {//todo 要判断哪里耗时操作导致的Log.i(TAG, "执行超过100毫秒");}}}};//然后注册Looper mainLooper = Looper.getMainLooper();mainLooper.setMessageLogging(printer);

7.3 应用场景

继续扩展一下,如果我们仅仅知道主线程卡不卡肯定还不够的。我们肯定还想知道到底是哪里卡了?这也是BlockCanary所想要解决的。不过我们这里也有一个简单实现,一个类就可以完成性能监控。

1.我们可以开启一个子线程每隔制定时间(比如20毫秒)不断的去捕获主线程的堆栈状态。

2.当println回调通知Message的回调执行开始时,我们把每次的捕获到的堆栈存储到Map当中。

3.当println回调通知结束时,我们判断一下执行时间,超过超时,则打印Map中所有的堆栈结构。如果Map中存在两个一摸一样堆栈结构,则说明这个堆栈所对应的方法至少执行了20毫秒(最多40毫秒)。如果存在3个,则至少执行了40毫秒,以此类推。

4.所以通过打印的相同堆栈的次数,我们就知道了到底是哪里导致的卡顿。开发调试阶段我经常用,十分好用。

附小型性能监控类完整代码:

package com.common.monitor;import android.content.Context;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
import android.util.Log;
import android.util.Printer;import com.common.monitor.monitor.BaseMonitor;import java.util.HashMap;
import java.util.Hashtable;
import java.util.Map;public class ANRMonitor{final static String TAG = "anr";public static void init(Context context) {if (true){//开关return;}ANRMonitor anrMonitor = new ANRMonitor();anrMonitor.start(context);Log.i(TAG, "ANRMonitor init");}private void start(Context context) {Looper mainLooper = Looper.getMainLooper();mainLooper.setMessageLogging(printer);HandlerThread handlerThread = new HandlerThread(ANRMonitor.class.getSimpleName());handlerThread.start();//时间较长,则记录堆栈threadHandler = new Handler(handlerThread.getLooper());mCurrentThread = Thread.currentThread();}private long lastFrameTime = 0L;private Handler threadHandler;private long mSampleInterval = 40;private Thread mCurrentThread;//主线程private final Map<String, String> mStackMap = new HashMap<>();private Printer printer = new Printer() {@Overridepublic void println(String it) {long currentTimeMillis = System.currentTimeMillis();//其实这里应该是一一对应判断的,但是由于是运行主线程中,所以Dispatching之后一定是Finished,依次执行if (it.contains("Dispatching")) {lastFrameTime = currentTimeMillis;//开始进行记录threadHandler.postDelayed(mRunnable, mSampleInterval);synchronized (mStackMap) {mStackMap.clear();}return;}if (it.contains("Finished")) {long useTime = currentTimeMillis - lastFrameTime;//记录时间if (useTime > 20) {//todo 要判断哪里耗时操作导致的Log.i(TAG, "ANR:" + it + ", useTime:" + useTime);//大于100毫秒,则打印出来卡顿日志if (useTime > 100) {synchronized (mStackMap) {Log.i(TAG, "mStackMap.size:" + mStackMap.size());for (String key : mStackMap.keySet()) {Log.i(TAG, "key:" + key + ",state:" + mStackMap.get(key));}mStackMap.clear();}}}threadHandler.removeCallbacks(mRunnable);}}};private Runnable mRunnable = new Runnable() {@Overridepublic void run() {doSample();threadHandler.postDelayed(mRunnable, mSampleInterval);}};protected void doSample() {StringBuilder stringBuilder = new StringBuilder();for (StackTraceElement stackTraceElement : mCurrentThread.getStackTrace()) {stringBuilder.append(stackTraceElement.toString()).append("\n");}synchronized (mStackMap) {mStackMap.put(mStackMap.size() + "", stringBuilder.toString());}}}


http://chatgpt.dhexx.cn/article/k2MyV7DS.shtml

相关文章

Handler机制原理----全解

Handler机制&#xff0c;我们就会想到&#xff0c;他有四个重要的对象组成&#xff0c;分别是&#xff1a;Handler&#xff0c;Message,MessageQueue,Looper. 那Handler是如何工作的呢&#xff1f; 例如&#xff0c;你想刷新主界面的TextView&#xff0c;无奈你不在主线程&am…

Android面试必问之Handler机制

Android面试必问之Handler机制 1. 作用2. 基本使用2.1 创建Handler实例2.2 发送消息2.3 使用post方法2.4 使用sendMessage方法2.5 通过Message与Handler进行通信的步骤 3. 源码分析3.1 为什么Handler能够切换线程执行&#xff1f;3.2 Handler.post(Runnable) 方法是运行在新的线…

java中handler机制_自己动手撸一个Handler,让你彻底搞懂Handler机制,揍吻你服不服?...

原标题:自己动手撸一个Handler,让你彻底搞懂Handler机制,揍吻你服不服? 一、关于Handler Handler对于我们Android开发者来说应该是再熟悉不过了,这也是在android中最重要的消息机制,特别是在面试笔试时,Handler机制也是最常问到的话题。今天我们就来动手撸一个自己写的H…

一文搞懂Handler机制

什么是Handler? Handler是进程内部、线程间的一种通信机制。 Handler、Looper、MessageQueen、Message的关系 Message: 消息对象 MessageQueen: 存储消息对象的队列 Looper:负责循环读取MessageQueen中的消息&#xff0c;读到消息之后就把消息交给Handler去处理。 Handler&…

Handler机制(异步消息处理机制)

Android的异步消息处理由四个部分组成&#xff1a;Message、Handler 、MessageQueue和Looper。 先抛出自己的几个问题供自己以后进行回顾复习&#xff1a; 1、为什么需要进行异步消息处理&#xff1f; 大量耗时的操作我们一般是另开一个子线程来进行处理&#xff0c;但是在子线…

Android - 浅谈 Handler 机制

熟悉 Android 开发的小伙伴都知道&#xff0c;不能再非主线程中修改 UI 控件&#xff0c;而且当时老师告诉我们就是在非主线程的代码修改 UI 控件可以用 Handler。以至于后来我也都是这么做的&#xff0c;最近有人问我这个自定义的 Handler 为啥下面都是黄色的&#xff0c;是不…

【Android】Handler机制详解

【Android】Handler机制详解 本文是在 Carson带你学Android 作者的异步通信 专栏中Handler的基础上学习整理的kotlin版本&#xff0c;并且Android源码部分也更新至最新。 1.使用Handler消息传递机制的原因 2.相关概念 概念定义作用备注Main Thread应用程序初次启动时会自动开…

一文搞懂Handler机制原理

前言 Android提供了Handler来满足线程间的通信&#xff0c;开发中不管直接还是间接基本离不开Handler的使用&#xff0c;通常Handler被我们用来做子线程更新UI线程的工具&#xff0c;可以说只要有子线程与主线程通信的地方就会有Handler。 工欲善其事必先利其器&#xff0c;熟…

handler机制原理

一,什么是handler handler是消息机制的一个上层接口 更新UI的操作 耗时完毕后发送消息给主线程更新UI 耗时操作只能在子线程中执行,Android是线程不安全的你不能在子线程中更新UI 所以Android引入了handler机制 handler通过发送和处理message和Runnable对象来关联相对应线程的…

Android Handler消息机制原理最全解读(持续补充中)

本文主要详细去解读Android开发中最常使用的Handler&#xff0c;以及使用过程中遇到的各种各样的疑问。 Handler 在Android开发的过程中&#xff0c;我们常常会将耗时的一些操作放在子线程&#xff08;work thread&#xff09;中去执行&#xff0c;然后将执行的结果告诉UI线程…

Handler机制

1.为何引入Handler机制 Handler是线程间通讯的机制&#xff0c;Android中&#xff0c;网络访问、文件处理等耗时操作必须放到子线程中去执行&#xff0c;否则将会造成ANR异常。 ANR异常&#xff1a;Application Not Response 应用程序无响应 产生ANR异常的原因&#xff1a;在…

Handle消息机制解析

概述 Handler消息机制(由Handler/Looper/MessageQueue等构成)&#xff0c;Android有大量的消息驱动方法来进行交互&#xff0c;就像Android的四大组件(Activity、Service、Broadcast、ContentProvider)的启动过程交互&#xff0c;都离不开Handler的消息机制&#xff0c;所以An…

Handler机制(一)——Handler运行流程分析

1 概述 Handler机制是Android的异步消息处理机制&#xff0c;用于在线程间传递消息&#xff0c;主要涉及到四部分&#xff1a;Handler、Looper、Message和MessageQueue。其中Handler是消息的发送者和处理者&#xff1b;Message是消息主体&#xff1b;MessageQueue是消息队列&a…

reshape的作用

reshape就是矩阵的变换就是行和列相乘的数相等就可以相互变换

reshape函数

在opencv中&#xff0c;reshape函数比较有意思&#xff0c;它既可以改变矩阵的通道数&#xff0c;又可以对矩阵元素进行序列化&#xff0c;非常有用的一个函数。 函数原型&#xff1a; C: Mat Mat::reshape(int cn, int rows0) const 参数比较少&#xff0c;但设置的时候却要千…

Reshape的命令应用

import numpy as np tnp.arange(0,64).reshape(8,8) print(t) Reshape 的参考使用&#xff1a; (1条消息) Python的reshape的用法&#xff1a;reshape(1,-1)_冷月无声的博客-CSDN博客_reshape函数pythonhttps://blog.csdn.net/qq_29831163/article/details/90112000Reshape主…

matlab中reshape的用法,reshape2 函数 reshape 的用法

函数 reshape 的用法 请我在MATLAB编程中遇到了一个问题&#xff0c;函数reshape的用法我就是没有弄B reshape(A,m,n) 返回一个m*n的矩阵B&#xff0c; B中元素是按列从A中得到的。如果A中元素个数没有m*n个&#xff0c; 则会引发错误。 B reshape(A,m,n,p,...)和B reshape(…

Numpy之reshape()详解

Numpy中reshape的使用方法为:numpy.reshape(a, newshape, orderC) 参数详解&#xff1a;1.a: type:array_like(伪数组&#xff0c;可以看成是对数组的扩展&#xff0c;但是不影响原始数组。) 需要reshape的array2.newshape:新的数组 新形状应与原形状兼容。如果是整数&#xf…

利用Numpy库的方法reshape()对ndarray对象矩阵的形状进行调整

利用Numpy库的函数reshape()对ndarray对象矩阵的形状进行调整 调整矩阵或图像的形状是一个常用的操作。 在Numpy库中&#xff0c;可使用函数reshape()实现此操作。 其函数原型如下&#xff1a; dst numpy.reshape(a, newshape[, orderC])参数意义如下&#xff1a; a—需要调…

关于reshape

X.reshape(X.shape[0], -1).T和X.reshape(-1&#xff0c;X.shape[0]) 虽然矩阵形式仍然一致但矩阵元素排列完全不同 在降低测试集维度时注意&#xff0c;应使用X.reshape(X.shape[0], -1).T