createMap(t, value);
}

set方法首先获取到了当前的线程,然后获取一个map。这个map是以键值对形式存储内容的。如果获取的map为空,就创建一个map。如果不为空就塞进去值。要注意的是,这里面的key是当前的线程,这里面的value就是Looper。也就是说,线程和Looper是一一对应的。也就是很多人说的Looper线程绑定了,其实就是以键值对形式存进了一个map中。没什么高大上的。你来你也行。

而这个Looper的构造方法我们也得去看一下:

private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}

Looper的构造方法中,可以看到它创建了一个MessageQueue,没错就是那个被Handler无耻使用的MessageQueue。需要注意的一点,上面的分析中提到了prepare方法必须调用但也只能调用一次,调用以后就会创建Looper对象,也就是说一个线程中只会创建一个Looper对象,而一个Looper对象也只会创建一个MessageQueue对象。

现在我们来梳理一下这个流程哈~

首先创建一个无参数的Handler,在这个Handler的构造方法中又去获取Looper对象,当然获取Looper对象其实是为了它的MessageQueueHandler巴结上了人家Looper对象的MessageQueue以后,发送消息的时候,把要发送的消息给了MessageQueue,添加到了队列中。是不是感觉缺少了什么?没错,好像在这个里面Looper的作用没体现出来,说好的分发消息呢?而且你刚刚说了得调用prepare()方法才会创建Looper,可我没调用过这个方法啊。那这个Looper谁创建的?

刚才提到了,Looper在创建的时候会被当成value塞入到一个map中去,这个mapThreadLocal。而key就是创建Looper时所在的线程。也就是所谓的Looper线程绑定。我们一般在用的时候从没创建过Looper,但是我们知道handle中的回调handleMessage方法是运行在主线程中的。Looper的职责不就是分发消息么,也就是说Looper对象在主线程中把消息分发给了Handler。那么这下就明白了,在我们创建Looper的时候,Looper所在的线程是主线程,换言之,与这个Looper绑定的线程就是主线程。

明白了,我这就去和面试官对线。

既然是主线程,那么大家应该知道,主线程是谁创建的?ActivityThread类。ActivityThread类也正是整个app的入口。以前我也很好奇,既然Android是用Java写的,按理说Java不应该是有个什么main方法么?怎么我写Android没用过这个main方法呢?其实呢,在ActivityThread中就有这个main方法,它是程序的入口,也就说当你点开app以后,首先会进入到这个main方法中,然后做了一大堆事情,这里就不分析了。你只需要知道,这个main方法才是真正的入口。

那我们来看看这个main方法到底干了什么事情:

//ActivityThread.java
//省略部分代码
public static void main(String[] args) {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, “ActivityThreadMain”);
Process.setArgV0("");
//1 敲黑板,划重点,就是这一句!
Looper.prepareMainLooper();
ActivityThread thread = new ActivityThread();
thread.attach(false, startSeq);

if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}

if (false) {
Looper.myLooper().setMessageLogging(new
LogPrinter(Log.DEBUG, “ActivityThread”));
}

// End of event ActivityThreadMain.
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
//2 敲黑板,划重点,这一句!
Looper.loop();

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

这段代码是不是很符合我们平常写的java程序呢?熟悉的main方法又回来了。main方法中可以看到,它调用了Looper的prepareMainLooper方法:

public static void prepareMainLooper() {
//设置不允许退出的Looper
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException(“The main Looper has already been prepared.”);
}
sMainLooper = myLooper();
}
}

可以看到注释1,这个方法最终还是调用了Looperprepare方法,这个方法干嘛的?创建Looper并且把它和当前线程一起塞进map中的啊。当前线程是哪个线程?主线程啊!

一切到这里就真相大白了,在APP启动的时候,入口方法中已经自动帮我们创建好了Looper,并且也自动的帮我们和主线程绑定了。也就是说我们平常用的Handler中的Looper就是主线程中创建的这个Looper。细心的小伙伴应该会发现,这个prepareMainLooper方法你是不能调用的。为啥?因为这个方法在入口的时候执行了一次,所以里面的sMainLooper不为Null了,如果你在调用一次,不就要抛异常了么~

现在Looper也有了,LooperMessageQueue也有了。接下来该分发消息了吧?我Handler发送消息可是已经很久过去了,你这里分析一大通,我还干不干活了?

好,我们现在先假设一个场景。

你买了一个快递,你知道迟早会给你送到,但是不确定到底什么时候才会送到。你想早点拿到快递应该怎么做?

你会不停的问快递公司,我的快递到哪了,到哪了。当然,现实中一般都是等快递员打电话才去拿快递~问题在于,这是程序。

Looper虽说要分发消息,但是它又不知道你什么时候会发送消息,只能开启一个死循环,不断的尝试从队列中拿数据。这个死循环在哪里开始的?没错就是注释2处,Looper.loop()开启了一个死循环,然后不断的尝试去队列中拿消息。

// Looper.java
public static void loop() {

//拿到当前线程的Looper
final Looper me = myLooper();

if (me == null) {
throw new RuntimeException(“No Looper; Looper.prepare() wasn’t called on this thread.”);
}

//拿到Looper的消息队列
final MessageQueue queue = me.mQueue;

// 省略一些代码…
//1 这里开启了死循环
for (;😉 {
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}

try {
msg.target.dispatchMessage(msg);
//省略一些代码…
} catch (Exception exception) {
//省略一些代码…
throw exception;
} finally {
//省略一些代码…
}
//省略一些代码…
msg.recycleUnchecked();
}
}

在循环中Looper不停的取出消息,拿到Message对象以后,会去调用Messagetarget字段的dispatchMessage方法,这个target字段还有印象吗?没错,就是发送它的Handlermessage在被发送出去的时候就已经暗暗记下了是谁发送出去的。现在轮到它报仇了~

我们可以跟进看一下这个dispatchMessage方法:

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

可以看到,消息会先分发给Meesgaecallback,我们没有定义这个callback,那我们接下来看,还有一个mCallback。这个mCallback是创建Handler的时候可以选择传一个CallBack回调,里面依然是handleMessage方法。也就是说你可以自定义一个类继承Handler,重写handleMessage方法,也可以直接new一个Handler传一个回调。当然,这个都很简单,我就不再赘述了,大家可以自行尝试体验。

我们关注重点,当Looper拿到Message以后,并且根据Messagetarget字段找到了发送消息的Handler,紧接着调用了HandlerhandleMessage方法。重点来了,这个Looper是在哪个线程运行的?主线程,它调用方法是在哪个线程运行的?依然是主线程!handleMessage方法此时在哪个线程运行的?依然是主线程!不知不觉中,线程已经切换过来了,神奇不?其实并不神奇,其实就是主线程中的Looper不断的尝试调用handleMessage方法,如果有消息就调用成功了,此时handleMessage方法就是在主线程中调用的。而handler在哪个线程,Looper并不关心,我反正只在主线程调用你的handleMessage方法。这就是线程切换的本质。就是没有线程切换,主线程的Looper不断的尝试调用而已。

可能有的小伙伴已经懵逼了,我们再次从头到尾梳理一下哈~

  1. mainThreadActivityThread首先创建了一个运行在主线程的Looper,并且把它和主线程进行了绑定。
  2. Looper又创建了一个MessageQueue,然后调用Looper.loop方法不断地在主线程中尝试取出Message
  3. Looper如果取到了Message,那么就在主线程中调用发送这个MessageHandlerhandleMessage方法。
  4. 我们在主线程或者子线程中通过Looper.getMainLooper为参数创建了一个Handler
  5. 在子线程中发送了Message,主线程中的Looper不断循环,终于收到了Message,在主线程中调用了这个HandlerhandleMessage方法。

这里需要注意的是,Looper.loop方法中取到的Looper对象并不一定就是主线程的,因为它是取出当前线程的Looper对象。只不过在ActivityThread这里是主线程,所以拿到的是主线程的Looper对象。所以如果我们要在子线程中创建一个Looper也是可以的,一会我们就实现一下。

到这里可能有的小伙伴还是懵逼,我还是不太明白怎么切换的线程。我们通过一个比喻很好的解释一下

首先有一个小学生小明,小明的任务是写作业。然后有一个老师,老师的任务是批改作业。这个班里还有一个学习委员,学习委员的任务就是负责收作业然后交给老师去批改。

一般情况下,老师是学校已经聘请好的,我们不需要自己去聘请老师。老师一般也就只在办公室批改作业,办公室我们可以理解为主线程。学校就是我们的app。老师就是Looper对象。而小明同学就是Handler,学习委员就是MessageQueue,作业就是Message。老师(Looper)在办公室(主线程)不断的从学习委员(MessageQueue)那里拿到下一本要批改的作业(Message),老师突然发现作业里有错误,老师很生气,于是就从作业本上的姓名知道了是谁写的这个作业(对应Messagetarget字段),于是老师把小明(Handler)叫到办公室(主线程),让小明在办公室(主线程)把作业改好(handleMessage)。在这个例子中,小明作为Handler,他可以在任何地方写作业(sendMessage),也就是说他可以在家里写作业,可以在教室写作业,也可以在小公园写作业,这里的各个地方就是不同的线程。但是写完作业以后一定要交给学习委员,学习委员手里有一摞作业,这一摞作业就是消息的队列,而学习委员就是MessageQueue,他负责收集作业,也就是收集Message。老师在办公室批改作业,发现出错了,就把小明叫到了办公室,让小明在办公室改错。在这里,办公室就是主线程,老师不会管小明是在哪里写的作业,老师只是关心作业出错了,需要小明在办公室里改错。小明在办公室里改错这就是handleMessage方法运行在了主线程。但是也有个问题,不能说你小明在办公室改错改个没完没了,那岂不是影响了后面同学作业的批改?如果小明真的改错改的没完没了,也就是在主线程上作耗时操作很久,那么老师也无法进行下一个同学的作业批改,时间一长,教学就没法进行了。这就是著名的ANR问题。不知道这样比喻,小伙伴们能不能理解线程切换的意思和ANR的意思。如果还不能理解,那么你来砍我吧~

Looper和ANR

很多面试官喜欢问,Looperloop方法是个死循环,而loop方法又是运行在主线程的,主线程上有死循环为什么不会导致ANR存在呢?

其实这里面很有趣的一个点就是,很多小伙伴把Looperloop方法当做一个普通方法来看待,所以才会有这样的疑问。但是这个loop方法并不是一个普普通通的方法。

我们先思考一点,如果我们写一个app,里面一行代码也不写的话,app会不会崩溃?

答案显而易见,是不会的。

可是在上面提到了,本质上App就是一个Java程序,Java程序就有main方法,在ActivityThread类中也确实有这个main方法。我们一般写java程序的时候,是不是main方法中的代码执行完,程序也就结束了。但是app并没有,只要你不退出,它一直运行。那这是为什么呢?

很多小伙伴应该想到了,没错,让程序不退出的话,写一个死循环,那么main方法中的代码永远不会执行完,这样程序就不会自己退出了。Android当然也是这么干的,而且不止Android,基本上所有的GUI程序都是这么干的。正是因为Looper.loop方法这个死循环,它阻塞了主线程,所以我们的app才不会退出。那你可能有疑问了,那既然这里有死循环了,那我其他的代码怎么运行?界面交互怎么办?你问到点子上了。

本质上Android就是事件驱动的程序,界面刷新也好,交互也好,本质上都是事件,这些事件最后通通被作为了Message发送到了MessageQueue中。由Looper来进行分发,然后在进行处理。用人话来说就是,我们的Android程序就是运行在这个死循环中的。一旦这个死循环结束,app也就结束了。

那么ANR是什么呢?ANRApplication Not Responding也就是Android程序无响应。为什么没响应呢?因为主线程做了耗时操作啊。可我还是不明白,明明Looperloop方法就是阻塞了主线程,为什么不ANR呢。那我们就来说道说道,什么是响应?响应就是界面的刷新,交互的处理等等对吧。那么这个响应是谁来响应的?没错,就是loop方法中进行响应的。没响应什么意思?就是loop方法中被阻塞了,导致无法处理其他的Message了。

所以结论就来了,主线程做耗时操作本质上不是阻塞了主线程,而是阻塞了Looperloop方法。导致loop方法无法处理其他事件,导致出现了ANR事件。对比小明这个比喻的话,就是因为小明在办公室里没完没了的改作业,占用了老师的时间,让老师没法批改下一个同学的作业,才导致了教学活动无法正常进行。而老师不断的批改作业,这本身就是正常的教学活动,也正是因为老师不断批改作业,同学们才有提高,教学才能继续。

Handler在Java层几个需要注意的点

  1. 子线程Looper

如果要创建Handler,必须通过Looper.prepare()方法创建Looper,在主线程中ActivityThread已经帮我们创建好了,我们不需要自己去创建,但如果在子线程中创建Handler,要么使用Looper的mainLooper,要么自己调用Looper.prepare()方法创建属于这个线程的looper对象。如下是创建了一个子线程的Looper对象:

class LooperThread extends Thread {
public Handler mHandler;
public void run() {
Looper.prepare();
mHandler = new Handler() {
public void handleMessage(Message msg) {
//TODO 定义消息处理逻辑.
}
};
Looper.loop();
}
}

  1. 消息池

在生成消息的时候,最好是用 Message.obtain() 来获取一个消息,这是为什么呢?

// 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 flag
sPoolSize–;
return m;
}
}
return new Message();
}

可以看到,obtain方法是将一个Message对象的所有数据清空,然后添加到链表头中。sPool就是个消息池,默认的缓存是50个。而且在Looper的loop方法中最后一行是这样的

msg.recycleUnchecked();

Looper在分发结束以后,会将用完的消息回收掉,并添加到回收池里。

  1. Handler导致的内存泄露问题

什么是内存泄露?简而言之就是该回收的东西没有回收。在Handler中一般是这样使用:

class HandlerActivity: AppCompatActivity() {

private val mHandler = MyHandler()

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// 在子线程中通过自定义的 Handler 发消息
thread {
mHandler.sendEmptyMessageDelayed(1, 1000)
}
}

// 自定义一个 Handler
class MyHandler: Handler() {
override fun handleMessage(msg: Message) {
Log.i(“HandlerActivity”, “主线程:handleMessage: ${msg.what}”)
}
}
}

乍一看没有问题,但是有没有想过一个问题,就是说再发送延时消息之前,app推出了,那么handleMessage方法还会执行吗?答案是会的。为什么?我明明退出了,为什么还会执行呢?其实这和java有关系

MyHandlerHandlerActivity 的内部类,会持有 HandlerActivity 的引用。

在进入页面以后,发送了一个延时 1s 的消息,如果 HandlerActivity 在 1s 内退出了,由于 Handler 会被 Message 持有,保存在其 target 变量中,而 Message 又会被保存在消息队列中,这一系列关联,导致 HandlerActivity 在退出的时候,依然会被持有,因此不能被 GC 回收,这就是内存泄漏!当这个 1s 延时的消息被执行完以后,HandlerActivity 会被回收。

虽然最终结果还是会被回收,但是内存泄露问题我们也必须去解决,如何解决?

1.将 MyHandler 改为静态类,这样它将不再持有外部类的引用。可以将 HandlerActivity 作为弱引用放到 MyHandler 中使用,页面退出的时候可以被及时回收。

2.页面退出的时候,在 onDestroy 中,调用 HandlerremoveMessages 方法,将所有的消息 remove 掉,这样也能消除持有链。

  1. 同步消息屏障

什么是同步消息屏障?

在Looper的loop方法中通过Message msg = queue.next();这么一行代码拿到Message进行分发,这个MessageQueue的next方法中有这么一行:

//MessageQueue.java
//省略部分代码
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;
//1 这一行很关键,同步消息屏障的关键点所在
if (msg != null && msg.target == null) {
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}

}

}
}

注释1下面的这一行代码,首先会判断msg不为null,然后紧接着判断msgtarget为null。我们知道messagetarget就是发送它的handler,所有的message都有一个handler,这里怎么可能没有handller呢?针对同步消息还真的是所有的message都有handler,而这里是异步消息。满足target == null的消息就是异步消息同步屏障是用来阻挡同步消息执行的。说得好,那么同步屏障有什么用呢?

似乎在日常的应用开发中,很少会用到同步屏障。那么,同步屏障在系统源码中有哪些使用场景呢?Android 系统中的 UI 更新相关的消息即为异步消息,需要优先处理。简而言之,如果在启动绘制之前,用户(开发者)插入了一个非常耗时的消息到队列中,那就会导致 UI 不能按时绘制,导致卡顿掉帧。,同步消息屏障就可以用来保证 UI 绘制的优先性。

Handler在C++层

如果你的目标是理解Handler在Java层是如何实现的,下面就不需要看了。下面主要讲解Handler在C++层是如何工作并实现的。

首先,细心的小伙伴们可能会有疑问。Looper一直处于死循环中,就像老师一直不断的问学习委员要作业批改,老师也是人,不会累么?你问对了,老师当然不会一直不断的问学习委员要作业,正常情况下,是有人交了作业以后,学习委员送过来,老师才会去批改。没有作业的时候,老师可能在休息,可能在玩游戏**。Looper也是一样,在消息队列为空的时候,Looper实际上处于休眠状态,只要当有Handler发送消息的时候,Looper才会被唤醒,去进行分发消息**。那么是怎么实现的呢?

在整个消息机制中,MessageQueue是连接Java层和Native层的纽带,换言之,Java层可以向MessageQueue消息队列中添加消息,Native层也可以向MessageQueue消息队列中添加消息。

这是MessageQueue中的Native方法:

// MessageQueue.java

private native static long nativeInit();
private native static void nativeDestroy(long ptr);
private native void nativePollOnce(long ptr, int timeoutMillis);
private native static void nativeWake(long ptr);
private native static boolean nativeIsPolling(long ptr);
private native static void nativeSetFileDescriptorEvents(long ptr, int fd, int events);

在MessageQueue的构造方法中是这样的:

//MessageQueue.java
MessageQueue(boolean quitAllowed) {
mQuitAllowed = quitAllowed;
mPtr = nativeInit();
}

调用了nativeInit方法,在native层创建了native层的MessageQueue,mPtr是保存了NativeMessageQueue的指针,后续的线程挂起和线程的唤醒都要通过这个指针来完成,其实就是通过Native层的MessageQueue来完成。

//android_os_MessageQueue.cpp
static jlong android_os_MessageQueue_nativeInit(JNIEnv* env, jclass clazz) {
//初始化native消息队列
NativeMessageQueue* nativeMessageQueue = new NativeMessageQueue();
nativeMessageQueue->incStrong(env); //增加引用计数
return
reinterpret_cast(nativeMessageQueue);
}

这个是NativeMessageQueue的构造方法:

//android_os_MessageQueue.cpp
NativeMessageQueue::NativeMessageQueue()
: mPollEnv(NULL), mPollObj(NULL), mExceptionObj(NULL) {

mLooper = Looper::getForThread(); //功能类比于Java层的Looper.myLooper();
if (mLooper == NULL) {
mLooper = new Looper(false); //创建native层的Looper
Looper::setForThread(mLooper); //保存native层的Looper到TLS,功能类比于Java层的ThreadLocal.set();
}
}

Looper的构造方法是这样的:

//Looper.cpp
Looper::Looper(bool allowNonCallbacks) :
mAllowNonCallbacks(allowNonCallbacks), mSendingMessage(false),
mPolling(false), mEpollFd(-1), mEpollRebuildRequired(false),
mNextRequestSeq(0), mResponseIndex(0), mNextMessageUptime(LLONG_MAX) {
mWakeEventFd = eventfd(0, EFD_NONBLOCK); //构造唤醒事件的fd
AutoMutex _l(mLock);
rebuildEpollLocked(); //重建Epoll事件
}

void Looper::rebuildEpollLocked() {
if (mEpollFd >= 0) {
close(mEpollFd); //关闭旧的epoll实例
}
mEpollFd = epoll_create(EPOLL_SIZE_HINT); //创建新的epoll实例,并注册wake管道
struct epoll_event eventItem;//新建唤醒监听事件
memset(& eventItem, 0, sizeof(epoll_event)); //把未使用的数据区域进行置0操作
eventItem.events = EPOLLIN; // 设置监听内容可读事件
eventItem.data.fd = mWakeEventFd;
//将唤醒事件(mWakeEventFd)添加到epoll实例(mEpollFd)
int result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeEventFd, & eventItem);

for (size_t i = 0; i < mRequests.size(); i++) {
const Request& request = mRequests.valueAt(i);
struct epoll_event eventItem;
request.initEventItem(&eventItem);
//将request队列的事件,分别添加到epoll实例
int epollResult = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, request.fd, & eventItem);
}
}

等等,你上来给我这一大段C++代码,我怎么可能看得懂。还有这个epoll是什么?不是讲如何Looper怎么休眠和唤醒的么?

没错,就是讲的Looper怎么休眠和唤醒的。Looper的休眠和唤醒都是在Native层实现的,实现的原理是Linux上的epoll机制。

什么是epoll机制呢?

epoll你可以简单的理解为一个监听事件,在Linux上通过epoll机制监听一个事件,没什么事的时候我就让出CPU,进行休眠,当这个事件触发的时候我就从沉睡中唤醒开始处理。就像按钮的点击事件一样,点击了,监听到这个点击事件就会触发按钮的onClick方法。不过在LInxu上是通过文件的读写来完成的。类比于

include <sys/epoll.h>

// 创建句柄 相当于初始化onClickListener
int epoll_create(int size);
// 添加/删除/修改 监听事件 相当于addOnClicklistener
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
// 进入等待 这就相当于onCLick方法了
int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout);

epoll_wait就类似于Java中的onCLick方法,当监听的文件有变化的时候,它就会收到结果。其实更像是Kotlin协程中的suspend方法,就一直在等着,阻塞着,有结果才会进行下一步。onClick方法是使用接口回调的形式来实现的,是非阻塞的。而epoll_wait方法是阻塞的。

在上面的Looper构造方法中,调用了rebuildEpollLocked方法,这个方法就是设置监听器的,可以理解为setOnClickListener,不过它监听的是文件的可读事件。 即eventItem.events = EPOLLIN;这行代码。什么是可读事件?就是说,文件里面有内容了是不是就可以读了,没错就是这样喵~

好了,事件也已经监听了,那么Looper是在哪沉睡的呢?

是在MessageQueue中的这行代码:

//MessageQueue.java
Message next() {
final long ptr = mPtr;
if (ptr == 0) {
return null;
}

for (;😉 {

nativePollOnce(ptr, nextPollTimeoutMillis); //阻塞操作

}

就是通过这行代码进行阻塞操作。

调用关系是这样的:

MessageQueue::nativePollOnce ->NativeMessageQueue::pollOnce()->Looper::pollOnce()->Looper::pollInner

int Looper::pollInner(int timeoutMillis) {

struct epoll_event eventItems[EPOLL_MAX_EVENTS];

//1. 等待事件发生或者超时,如果nativeWake()方法中向管道写端写入字符,则该方法会返回;
int eventCount = epoll_wait(mEpollFd.get(), eventItems, EPOLL_MAX_EVENTS, timeoutMillis);

mPolling = false;

mLock.lock();

if (mEpollRebuildRequired) {
mEpollRebuildRequired = false;
rebuildEpollLocked();
goto Done;
}

if (eventCount < 0) {
if (errno == EINTR) {
goto Done;
}
result = POLL_ERROR;
goto Done;
}

if (eventCount == 0) {
result = POLL_TIMEOUT;
goto Done;
}

//循环遍历,处理所有的事件
for (int i = 0; i < eventCount; i++) {
int fd = eventItems[i].data.fd;
uint32_t epollEvents = eventItems[i].events;
//唤醒事件
if (fd == mWakeEventFd.get()) {
if (epollEvents & EPOLLIN) {
已经唤醒了,则读取并清空管道数据【7】
awoken();
} else {
ALOGW(“Ignoring unexpected epoll events 0x%x on wake event fd.”, epollEvents);
}
} else {
// 处理其他事件,Handler没有
// 省略一些代码…
}
}
Done: ;

//省略一些代码…

// Release lock.
mLock.unlock();
//省略一些代码…
return result;
}

代码到了注释1处就开始了阻塞,也就是所谓的休眠。那么什么时候才能唤醒它呢?超时了,或者文件发生了变化,可以读了就可以唤醒了。注意,这个超时就是在Java层设置的延时发送,也就是说Java的sendMessageDelayed方法最后是通过epoll设置超时的机制实现延迟发送的。

不知道大家注意到没有,在我们发送Message的时候有这么一行代码:

// MessageQueue.java

boolean enqueueMessage(Message msg, long when) {

// 省略一些代码…

synchronized (this) {

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 {
//消息插到队列中间
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.next
prev.next = msg;
}

if (needWake) {
// 敲黑板划重点:唤醒
nativeWake(mPtr);
}
}
return true;
}

在最后nativeWake(mPtr);这行代码进行了唤醒。不过必须neekWaketrue的时候才会唤醒,那么neekWake什么时候才是True呢?

两种情况会唤醒线程:

  1. 队列为空 || 消息无需延时 || 或消息执行时间比队列头部消息早) && (线程处于挂起状态时(mBlocked = true)
  2. 【线程挂起(mBlocked = true)&& 消息循环处于同步屏障状态】,这时如果插入的是一个异步消息,则需要唤醒。

唤醒操作具体是如何去做的?

调用链是这样的:

MessageQueue::nativeWake—>android_os_MessageQueue_nativeWake()—>NativeMessageQueue::wake()—>Looper::wake()

//Looper.cpp
void Looper::wake() {
uint64_t inc = 1;
// 向管道mWakeEventFd写入字符1
ssize_t nWrite = TEMP_FAILURE_RETRY(write(mWakeEventFd, &inc, sizeof(uint64_t)));
if (nWrite != sizeof(uint64_t)) {
if (errno != EAGAIN) {
ALOGW(“Could not write wake signal, errno=%d”, errno);
}
}
}

前面说了,epoll_wait在监听文件的可读事件,那么现在有消息来了,我要触发这个事件只需要往文件里随便写点什么就可以,Looper里面只是写了一个字符1。成功的唤醒了线程。然后开始轮询取出消息分发。

总结

环处于同步屏障状态】,这时如果插入的是一个异步消息,则需要唤醒。

唤醒操作具体是如何去做的?

调用链是这样的:

MessageQueue::nativeWake—>android_os_MessageQueue_nativeWake()—>NativeMessageQueue::wake()—>Looper::wake()

//Looper.cpp
void Looper::wake() {
uint64_t inc = 1;
// 向管道mWakeEventFd写入字符1
ssize_t nWrite = TEMP_FAILURE_RETRY(write(mWakeEventFd, &inc, sizeof(uint64_t)));
if (nWrite != sizeof(uint64_t)) {
if (errno != EAGAIN) {
ALOGW(“Could not write wake signal, errno=%d”, errno);
}
}
}

前面说了,epoll_wait在监听文件的可读事件,那么现在有消息来了,我要触发这个事件只需要往文件里随便写点什么就可以,Looper里面只是写了一个字符1。成功的唤醒了线程。然后开始轮询取出消息分发。

总结

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

相关文章

  1. wireshark和tcpdump命令

    wireshark 源IP ip.src IP 目的IP ip.dst IP 端口 tcp.port tcp.srcport tcp.dstport HTTP http.request.methodGET\POST MAC地址 eth.addr/host tcpdump -c 数量 -D 查看哪些端口可以抓包 -i 指定网卡 -n 以IP地址显示 -nn 端口用数…...

    2024/4/13 22:27:06
  2. selenium爬虫find_element_by_*已被废弃使用find_element来代替

    原先的写法 el web.find_element_by_xpath(//*[id"changeCityBox"]/p[1]/a) web.find_element_by_xpath(//*[id"search_input"]).send_keys(python,Keys.ENTER) li_list web.find_elements_by_xpath(//*[id"jobList"]/div[1]/div) 出现一下警…...

    2024/4/18 21:39:04
  3. python双向链表以及双向循环链表

    深度学习入门小菜鸟&#xff0c;希望像做笔记记录自己学的东西&#xff0c;也希望能帮助到同样入门的人&#xff0c;更希望大佬们帮忙纠错啦~侵权立删。 目录 一、双向链表和双向循环链表概念 二、双向链表的实现 1、建立节点对象 2、链表对象的初始定义 3、判断链表是否…...

    2024/4/13 22:27:16
  4. 【U8+】用友U8通过数据库中取消模块启用

    --固定资产模块反启用&#xff1a; update accinformation set cvalue where csysidFA and ctypeddate; --可省 and (cid601or cid05or cid603OR CID602 OR CID03OR CID06) update gl_mend set bflag_FA0; delete ufsystem..ua_account_sub where (cacc_id001) and (cSub_I…...

    2024/4/7 18:56:34
  5. 在Pycharm中使用git上传项目(看了不会来找我)

    写在前面 不知道是不是倒霉区up看多了&#xff0c;导致我现在也倒霉。 几天前我电脑的硬盘突然爆炸&#xff0c;导致我费了很大的劲才把数据全部找回&#xff0c;虽然之前我也有备份的习惯&#xff0c;但是都是物理备份&#xff0c;拷到我的移动硬盘上&#xff0c;但是要是哪天…...

    2024/4/7 18:56:33
  6. 【Android春招每日一练】(十六) 剑指4题+Android进阶

    文章目录概览剑指offer1.61 翻转单词顺序1.62 左旋转字符串1.63 滑动窗口的最大值1.64 队列的最大值Android进阶Android布局优化Android权限处理总结概览 剑指offer&#xff1a;翻转单词顺序、左旋转字符串、滑动窗口的最大值、队列的最大值 Android进阶&#xff1a;Android布…...

    2024/4/18 14:27:17
  7. STM32CubeMX | STM32使用HAL库串口收发

    一、串口实现printf 2.添加代码 /* USER CODE BEGIN Includes */ #include "stdio.h" /* USER CODE END Includes */int fputc(int ch, FILE *f) {HAL_UART_Transmit(&huart1,(uint8_t*)&ch,1,5,0xffff);//20220113//串口重定向&#xff0c;执行任意长度字符…...

    2024/4/20 5:55:25
  8. BERT书籍阅读笔记(一)NLP基础知识

    文章目录常见的中文分词工具激活函数批量归一化批量归一化与激活函数在模型中的前后关系常见的中文分词工具 结巴分词&#xff0c;清华分词&#xff0c;hanlp&#xff0c;LTP&#xff0c;Stanford NER   除了Stanford NER需要加载jar包以外&#xff0c;其他的用pip都可以直接…...

    2024/4/13 22:27:11
  9. mongodb磁盘碎片整理

    mongodb磁盘碎片产生 删除文档或集合后&#xff0c;mongodb不会将disk空间释放的为OS&#xff0c;mongodb会在数据文件中维护Empty Records的列表。 重新插入数据后&#xff0c;mongodb从Empty Records列表中分配的存储空间给新的文档 一、整理数据碎片的方法 使用compact命…...

    2024/4/13 22:26:56
  10. sqlmap命令

    1、判断网站是否存在注入 get&#xff1a;sqlmap -u “url” post&#xff1a;sqlmap -r “请求数据包” 2、爆数据库 当前数据库 --current-db 线程 --thread &#xff08;1-10&#xff09; 爆所有库 --dbs 指定库 -D 爆所有表 --tables 指定表 -T 爆所有列 --colu…...

    2024/4/13 22:27:01
  11. mongodb数据类型

    1.mongodb数据类型 object id:文档自动生成的_id,唯一标识string:字符串&#xff0c;必须是utf-8boolean:布尔型&#xff0c;true/falseinterger:整数double:浮点数arrays:数组或列表object:字典类型null:空数据类型timestamp:时间戳date:日期 2.mongodb的Number NumberLong…...

    2024/4/18 10:05:57
  12. 13 while循环和for循环

    1 while循环 1.1 一般形式 while语句最完整的形式是&#xff1a;首行以及测试表达式有一行或多行正常缩进语句构成的主体以及一个可选的else部分&#xff08;else 部分会在控制权离开循环而又没有碰到break语句时执行&#xff09;。Python会一直计算头部的测试&#xff0c;然…...

    2024/4/13 22:27:01
  13. 服务器导购指南

    #我该不该买服务器 既然说到了服务器的选购&#xff0c;那么第一个问题当然是确定是否真的有这方面的需求。毕竟服务器一般都不便宜&#xff01;而且买好服务器后还要各种备案折腾&#xff0c;很是麻烦&#xff01; 如果你只是想简单的写写博客&#xff0c;那么建议直接在简书…...

    2024/4/13 22:27:21
  14. 关于VHDL语言书写格式的学习(使用quartus Ⅱ)

    本文并不是对VHDL的系统的讲解&#xff0c;而是我认为的关键部分&#xff0c;知道了这些&#xff0c;基本上可以使用VHDL语言进行一些相应的设计。并且在使用的过程中发现问题&#xff0c;再进行一些相应的检索&#xff0c;深入学习&#xff0c;最后达到精通。 首先要明白VHDL是…...

    2024/4/15 5:12:02
  15. 跟着鹏哥学C语言第三天(关机程序的实现)

    第三天&#xff0c;跟着鹏哥学C语言真的很有意思&#xff0c;很坏的教我们如何整蛊好友。先是一个goto函数的介绍&#xff08;此函数主要用于实现多层循环的跳出&#xff09;&#xff0c;然后是实现关机的方法system&#xff08;“shutdown -s ”&#xff09;若要控制时间还要后…...

    2024/4/18 22:20:03
  16. 几个虚拟机的对比和介绍

    1、Bochs软件&#xff1a; 完全仿真X86的硬件环境 2、Virtual PC软件&#xff1a; 仿真了X86的大部分指令&#xff0c;而其它部分则采用了虚拟技术来实现 3、VMware WorkStation 软件&#xff1a; 仅仿真了一些I/O功能&#xff0c;而所有其它部分则是在X86实时硬件上直接执…...

    2024/4/13 22:26:56
  17. 关于VHDL语言的学习(使用quartus Ⅱ)

    本文并不是对VHDL的系统的讲解&#xff0c;而是我认为的关键部分&#xff0c;知道了这些&#xff0c;基本上可以使用VHDL语言进行一些相应的设计。并且在使用的过程中发现问题&#xff0c;再进行一些相应的检索&#xff0c;深入学习&#xff0c;最后达到精通。 首先要明白VHDL是…...

    2024/4/13 22:26:56
  18. 关于C++ 中的const-member function和non-const-member function

    诸位直接看图...

    2024/4/18 16:44:12
  19. 【密码学】中国剩余定理

    实验目的与要求 利用扩展欧几里得定理计算乘法逆元。理解并掌握中国剩余定理。 原理 实验内容 实验步骤与结果 实现本次实验所用的环境为jdk1.8下的Java代码&#xff0c;代码测试结果在最下面。 由于扩展欧几里得定理和中国剩余定理都要基于求最大公因数的方法getGcd()&…...

    2024/4/13 22:28:03
  20. 如何查看yarn的日志

    yarn的上container的日志存放在哪里 我们从yarn ui上看到的application的日志具体是从哪里加载的呢。 如果yarn.log-aggregation-enable参数设置为true&#xff0c;使运行完成的任务将日志推送到HDFS上&#xff0c;以方便作业日志集中管理和分析。那么日志就会存放在yarn.node…...

    2024/4/15 2:40:29

最新文章

  1. Python实践应用|NC文件读取

    import netCDF4 as nc import numpy as np import matplotlib.pyplot as plt# 打开NC文件 nc_file E:/NC_file/air.sig995.2012.nc # 将your_file.nc替换为你的NC文件路径 nc_data nc.Dataset(nc_file, r)# 查看NC文件中包含的变量 print("Variables in the NC file:&q…...

    2024/4/27 22:01:47
  2. 梯度消失和梯度爆炸的一些处理方法

    在这里是记录一下梯度消失或梯度爆炸的一些处理技巧。全当学习总结了如有错误还请留言&#xff0c;在此感激不尽。 权重和梯度的更新公式如下&#xff1a; w w − η ⋅ ∇ w w w - \eta \cdot \nabla w ww−η⋅∇w 个人通俗的理解梯度消失就是网络模型在反向求导的时候出…...

    2024/3/20 10:50:27
  3. Docke搭建Lidarr

    Lidarr 是一个基于音乐的下载管理器&#xff0c;它可以监控多个 RSS 订阅源以查找用户指定类型的音乐并与支持的下载客户端协同工作。Lidarr 旨在自动化音乐下载过程&#xff0c;并整合到用户的音乐库中。它可以搜索歌曲&#xff0c;自动下载和整理音乐文件&#xff0c;更新元数…...

    2024/4/22 18:24:51
  4. MXNet库

    MXNet&#xff08;MatriX Network&#xff09;是一个开源的深度学习框架&#xff0c;最初由亚马逊公司开发并于2015年发布。它是一个高效、灵活且可扩展的框架&#xff0c;旨在支持大规模的分布式深度学习模型训练和部署。 以下是 MXNet 库的一些主要特点和组成部分&#xff1…...

    2024/4/26 1:27:59
  5. 【外汇早评】美通胀数据走低,美元调整

    原标题:【外汇早评】美通胀数据走低,美元调整昨日美国方面公布了新一期的核心PCE物价指数数据,同比增长1.6%,低于前值和预期值的1.7%,距离美联储的通胀目标2%继续走低,通胀压力较低,且此前美国一季度GDP初值中的消费部分下滑明显,因此市场对美联储后续更可能降息的政策…...

    2024/4/26 18:09:39
  6. 【原油贵金属周评】原油多头拥挤,价格调整

    原标题:【原油贵金属周评】原油多头拥挤,价格调整本周国际劳动节,我们喜迎四天假期,但是整个金融市场确实流动性充沛,大事频发,各个商品波动剧烈。美国方面,在本周四凌晨公布5月份的利率决议和新闻发布会,维持联邦基金利率在2.25%-2.50%不变,符合市场预期。同时美联储…...

    2024/4/26 20:12:18
  7. 【外汇周评】靓丽非农不及疲软通胀影响

    原标题:【外汇周评】靓丽非农不及疲软通胀影响在刚结束的周五,美国方面公布了新一期的非农就业数据,大幅好于前值和预期,新增就业重新回到20万以上。具体数据: 美国4月非农就业人口变动 26.3万人,预期 19万人,前值 19.6万人。 美国4月失业率 3.6%,预期 3.8%,前值 3…...

    2024/4/26 23:05:52
  8. 【原油贵金属早评】库存继续增加,油价收跌

    原标题:【原油贵金属早评】库存继续增加,油价收跌周三清晨公布美国当周API原油库存数据,上周原油库存增加281万桶至4.692亿桶,增幅超过预期的74.4万桶。且有消息人士称,沙特阿美据悉将于6月向亚洲炼油厂额外出售更多原油,印度炼油商预计将每日获得至多20万桶的额外原油供…...

    2024/4/27 4:00:35
  9. 【外汇早评】日本央行会议纪要不改日元强势

    原标题:【外汇早评】日本央行会议纪要不改日元强势近两日日元大幅走强与近期市场风险情绪上升,避险资金回流日元有关,也与前一段时间的美日贸易谈判给日本缓冲期,日本方面对汇率问题也避免继续贬值有关。虽然今日早间日本央行公布的利率会议纪要仍然是支持宽松政策,但这符…...

    2024/4/27 17:58:04
  10. 【原油贵金属早评】欧佩克稳定市场,填补伊朗问题的影响

    原标题:【原油贵金属早评】欧佩克稳定市场,填补伊朗问题的影响近日伊朗局势升温,导致市场担忧影响原油供给,油价试图反弹。此时OPEC表态稳定市场。据消息人士透露,沙特6月石油出口料将低于700万桶/日,沙特已经收到石油消费国提出的6月份扩大出口的“适度要求”,沙特将满…...

    2024/4/27 14:22:49
  11. 【外汇早评】美欲与伊朗重谈协议

    原标题:【外汇早评】美欲与伊朗重谈协议美国对伊朗的制裁遭到伊朗的抗议,昨日伊朗方面提出将部分退出伊核协议。而此行为又遭到欧洲方面对伊朗的谴责和警告,伊朗外长昨日回应称,欧洲国家履行它们的义务,伊核协议就能保证存续。据传闻伊朗的导弹已经对准了以色列和美国的航…...

    2024/4/26 21:56:58
  12. 【原油贵金属早评】波动率飙升,市场情绪动荡

    原标题:【原油贵金属早评】波动率飙升,市场情绪动荡因中美贸易谈判不安情绪影响,金融市场各资产品种出现明显的波动。随着美国与中方开启第十一轮谈判之际,美国按照既定计划向中国2000亿商品征收25%的关税,市场情绪有所平复,已经开始接受这一事实。虽然波动率-恐慌指数VI…...

    2024/4/27 9:01:45
  13. 【原油贵金属周评】伊朗局势升温,黄金多头跃跃欲试

    原标题:【原油贵金属周评】伊朗局势升温,黄金多头跃跃欲试美国和伊朗的局势继续升温,市场风险情绪上升,避险黄金有向上突破阻力的迹象。原油方面稍显平稳,近期美国和OPEC加大供给及市场需求回落的影响,伊朗局势并未推升油价走强。近期中美贸易谈判摩擦再度升级,美国对中…...

    2024/4/27 17:59:30
  14. 【原油贵金属早评】市场情绪继续恶化,黄金上破

    原标题:【原油贵金属早评】市场情绪继续恶化,黄金上破周初中国针对于美国加征关税的进行的反制措施引发市场情绪的大幅波动,人民币汇率出现大幅的贬值动能,金融市场受到非常明显的冲击。尤其是波动率起来之后,对于股市的表现尤其不安。隔夜美国股市出现明显的下行走势,这…...

    2024/4/25 18:39:16
  15. 【外汇早评】美伊僵持,风险情绪继续升温

    原标题:【外汇早评】美伊僵持,风险情绪继续升温昨日沙特两艘油轮再次发生爆炸事件,导致波斯湾局势进一步恶化,市场担忧美伊可能会出现摩擦生火,避险品种获得支撑,黄金和日元大幅走强。美指受中美贸易问题影响而在低位震荡。继5月12日,四艘商船在阿联酋领海附近的阿曼湾、…...

    2024/4/25 18:39:16
  16. 【原油贵金属早评】贸易冲突导致需求低迷,油价弱势

    原标题:【原油贵金属早评】贸易冲突导致需求低迷,油价弱势近日虽然伊朗局势升温,中东地区几起油船被袭击事件影响,但油价并未走高,而是出于调整结构中。由于市场预期局势失控的可能性较低,而中美贸易问题导致的全球经济衰退风险更大,需求会持续低迷,因此油价调整压力较…...

    2024/4/26 19:03:37
  17. 氧生福地 玩美北湖(上)——为时光守候两千年

    原标题:氧生福地 玩美北湖(上)——为时光守候两千年一次说走就走的旅行,只有一张高铁票的距离~ 所以,湖南郴州,我来了~ 从广州南站出发,一个半小时就到达郴州西站了。在动车上,同时改票的南风兄和我居然被分到了一个车厢,所以一路非常愉快地聊了过来。 挺好,最起…...

    2024/4/26 22:01:59
  18. 氧生福地 玩美北湖(中)——永春梯田里的美与鲜

    原标题:氧生福地 玩美北湖(中)——永春梯田里的美与鲜一觉醒来,因为大家太爱“美”照,在柳毅山庄去寻找龙女而错过了早餐时间。近十点,向导坏坏还是带着饥肠辘辘的我们去吃郴州最富有盛名的“鱼头粉”。说这是“十二分推荐”,到郴州必吃的美食之一。 哇塞!那个味美香甜…...

    2024/4/25 18:39:14
  19. 氧生福地 玩美北湖(下)——奔跑吧骚年!

    原标题:氧生福地 玩美北湖(下)——奔跑吧骚年!让我们红尘做伴 活得潇潇洒洒 策马奔腾共享人世繁华 对酒当歌唱出心中喜悦 轰轰烈烈把握青春年华 让我们红尘做伴 活得潇潇洒洒 策马奔腾共享人世繁华 对酒当歌唱出心中喜悦 轰轰烈烈把握青春年华 啊……啊……啊 两…...

    2024/4/26 23:04:58
  20. 扒开伪装医用面膜,翻六倍价格宰客,小姐姐注意了!

    原标题:扒开伪装医用面膜,翻六倍价格宰客,小姐姐注意了!扒开伪装医用面膜,翻六倍价格宰客!当行业里的某一品项火爆了,就会有很多商家蹭热度,装逼忽悠,最近火爆朋友圈的医用面膜,被沾上了污点,到底怎么回事呢? “比普通面膜安全、效果好!痘痘、痘印、敏感肌都能用…...

    2024/4/25 2:10:52
  21. 「发现」铁皮石斛仙草之神奇功效用于医用面膜

    原标题:「发现」铁皮石斛仙草之神奇功效用于医用面膜丽彦妆铁皮石斛医用面膜|石斛多糖无菌修护补水贴19大优势: 1、铁皮石斛:自唐宋以来,一直被列为皇室贡品,铁皮石斛生于海拔1600米的悬崖峭壁之上,繁殖力差,产量极低,所以古代仅供皇室、贵族享用 2、铁皮石斛自古民间…...

    2024/4/25 18:39:00
  22. 丽彦妆\医用面膜\冷敷贴轻奢医学护肤引导者

    原标题:丽彦妆\医用面膜\冷敷贴轻奢医学护肤引导者【公司简介】 广州华彬企业隶属香港华彬集团有限公司,专注美业21年,其旗下品牌: 「圣茵美」私密荷尔蒙抗衰,产后修复 「圣仪轩」私密荷尔蒙抗衰,产后修复 「花茵莳」私密荷尔蒙抗衰,产后修复 「丽彦妆」专注医学护…...

    2024/4/26 19:46:12
  23. 广州械字号面膜生产厂家OEM/ODM4项须知!

    原标题:广州械字号面膜生产厂家OEM/ODM4项须知!广州械字号面膜生产厂家OEM/ODM流程及注意事项解读: 械字号医用面膜,其实在我国并没有严格的定义,通常我们说的医美面膜指的应该是一种「医用敷料」,也就是说,医用面膜其实算作「医疗器械」的一种,又称「医用冷敷贴」。 …...

    2024/4/27 11:43:08
  24. 械字号医用眼膜缓解用眼过度到底有无作用?

    原标题:械字号医用眼膜缓解用眼过度到底有无作用?医用眼膜/械字号眼膜/医用冷敷眼贴 凝胶层为亲水高分子材料,含70%以上的水分。体表皮肤温度传导到本产品的凝胶层,热量被凝胶内水分子吸收,通过水分的蒸发带走大量的热量,可迅速地降低体表皮肤局部温度,减轻局部皮肤的灼…...

    2024/4/27 8:32:30
  25. 配置失败还原请勿关闭计算机,电脑开机屏幕上面显示,配置失败还原更改 请勿关闭计算机 开不了机 这个问题怎么办...

    解析如下&#xff1a;1、长按电脑电源键直至关机&#xff0c;然后再按一次电源健重启电脑&#xff0c;按F8健进入安全模式2、安全模式下进入Windows系统桌面后&#xff0c;按住“winR”打开运行窗口&#xff0c;输入“services.msc”打开服务设置3、在服务界面&#xff0c;选中…...

    2022/11/19 21:17:18
  26. 错误使用 reshape要执行 RESHAPE,请勿更改元素数目。

    %读入6幅图像&#xff08;每一幅图像的大小是564*564&#xff09; f1 imread(WashingtonDC_Band1_564.tif); subplot(3,2,1),imshow(f1); f2 imread(WashingtonDC_Band2_564.tif); subplot(3,2,2),imshow(f2); f3 imread(WashingtonDC_Band3_564.tif); subplot(3,2,3),imsho…...

    2022/11/19 21:17:16
  27. 配置 已完成 请勿关闭计算机,win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机...

    win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机”问题的解决方法在win7系统关机时如果有升级系统的或者其他需要会直接进入一个 等待界面&#xff0c;在等待界面中我们需要等待操作结束才能关机&#xff0c;虽然这比较麻烦&#xff0c;但是对系统进行配置和升级…...

    2022/11/19 21:17:15
  28. 台式电脑显示配置100%请勿关闭计算机,“准备配置windows 请勿关闭计算机”的解决方法...

    有不少用户在重装Win7系统或更新系统后会遇到“准备配置windows&#xff0c;请勿关闭计算机”的提示&#xff0c;要过很久才能进入系统&#xff0c;有的用户甚至几个小时也无法进入&#xff0c;下面就教大家这个问题的解决方法。第一种方法&#xff1a;我们首先在左下角的“开始…...

    2022/11/19 21:17:14
  29. win7 正在配置 请勿关闭计算机,怎么办Win7开机显示正在配置Windows Update请勿关机...

    置信有很多用户都跟小编一样遇到过这样的问题&#xff0c;电脑时发现开机屏幕显现“正在配置Windows Update&#xff0c;请勿关机”(如下图所示)&#xff0c;而且还需求等大约5分钟才干进入系统。这是怎样回事呢&#xff1f;一切都是正常操作的&#xff0c;为什么开时机呈现“正…...

    2022/11/19 21:17:13
  30. 准备配置windows 请勿关闭计算机 蓝屏,Win7开机总是出现提示“配置Windows请勿关机”...

    Win7系统开机启动时总是出现“配置Windows请勿关机”的提示&#xff0c;没过几秒后电脑自动重启&#xff0c;每次开机都这样无法进入系统&#xff0c;此时碰到这种现象的用户就可以使用以下5种方法解决问题。方法一&#xff1a;开机按下F8&#xff0c;在出现的Windows高级启动选…...

    2022/11/19 21:17:12
  31. 准备windows请勿关闭计算机要多久,windows10系统提示正在准备windows请勿关闭计算机怎么办...

    有不少windows10系统用户反映说碰到这样一个情况&#xff0c;就是电脑提示正在准备windows请勿关闭计算机&#xff0c;碰到这样的问题该怎么解决呢&#xff0c;现在小编就给大家分享一下windows10系统提示正在准备windows请勿关闭计算机的具体第一种方法&#xff1a;1、2、依次…...

    2022/11/19 21:17:11
  32. 配置 已完成 请勿关闭计算机,win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机”的解决方法...

    今天和大家分享一下win7系统重装了Win7旗舰版系统后&#xff0c;每次关机的时候桌面上都会显示一个“配置Windows Update的界面&#xff0c;提示请勿关闭计算机”&#xff0c;每次停留好几分钟才能正常关机&#xff0c;导致什么情况引起的呢&#xff1f;出现配置Windows Update…...

    2022/11/19 21:17:10
  33. 电脑桌面一直是清理请关闭计算机,windows7一直卡在清理 请勿关闭计算机-win7清理请勿关机,win7配置更新35%不动...

    只能是等着&#xff0c;别无他法。说是卡着如果你看硬盘灯应该在读写。如果从 Win 10 无法正常回滚&#xff0c;只能是考虑备份数据后重装系统了。解决来方案一&#xff1a;管理员运行cmd&#xff1a;net stop WuAuServcd %windir%ren SoftwareDistribution SDoldnet start WuA…...

    2022/11/19 21:17:09
  34. 计算机配置更新不起,电脑提示“配置Windows Update请勿关闭计算机”怎么办?

    原标题&#xff1a;电脑提示“配置Windows Update请勿关闭计算机”怎么办&#xff1f;win7系统中在开机与关闭的时候总是显示“配置windows update请勿关闭计算机”相信有不少朋友都曾遇到过一次两次还能忍但经常遇到就叫人感到心烦了遇到这种问题怎么办呢&#xff1f;一般的方…...

    2022/11/19 21:17:08
  35. 计算机正在配置无法关机,关机提示 windows7 正在配置windows 请勿关闭计算机 ,然后等了一晚上也没有关掉。现在电脑无法正常关机...

    关机提示 windows7 正在配置windows 请勿关闭计算机 &#xff0c;然后等了一晚上也没有关掉。现在电脑无法正常关机以下文字资料是由(历史新知网www.lishixinzhi.com)小编为大家搜集整理后发布的内容&#xff0c;让我们赶快一起来看一下吧&#xff01;关机提示 windows7 正在配…...

    2022/11/19 21:17:05
  36. 钉钉提示请勿通过开发者调试模式_钉钉请勿通过开发者调试模式是真的吗好不好用...

    钉钉请勿通过开发者调试模式是真的吗好不好用 更新时间:2020-04-20 22:24:19 浏览次数:729次 区域: 南阳 > 卧龙 列举网提醒您:为保障您的权益,请不要提前支付任何费用! 虚拟位置外设器!!轨迹模拟&虚拟位置外设神器 专业用于:钉钉,外勤365,红圈通,企业微信和…...

    2022/11/19 21:17:05
  37. 配置失败还原请勿关闭计算机怎么办,win7系统出现“配置windows update失败 还原更改 请勿关闭计算机”,长时间没反应,无法进入系统的解决方案...

    前几天班里有位学生电脑(windows 7系统)出问题了&#xff0c;具体表现是开机时一直停留在“配置windows update失败 还原更改 请勿关闭计算机”这个界面&#xff0c;长时间没反应&#xff0c;无法进入系统。这个问题原来帮其他同学也解决过&#xff0c;网上搜了不少资料&#x…...

    2022/11/19 21:17:04
  38. 一个电脑无法关闭计算机你应该怎么办,电脑显示“清理请勿关闭计算机”怎么办?...

    本文为你提供了3个有效解决电脑显示“清理请勿关闭计算机”问题的方法&#xff0c;并在最后教给你1种保护系统安全的好方法&#xff0c;一起来看看&#xff01;电脑出现“清理请勿关闭计算机”在Windows 7(SP1)和Windows Server 2008 R2 SP1中&#xff0c;添加了1个新功能在“磁…...

    2022/11/19 21:17:03
  39. 请勿关闭计算机还原更改要多久,电脑显示:配置windows更新失败,正在还原更改,请勿关闭计算机怎么办...

    许多用户在长期不使用电脑的时候&#xff0c;开启电脑发现电脑显示&#xff1a;配置windows更新失败&#xff0c;正在还原更改&#xff0c;请勿关闭计算机。。.这要怎么办呢&#xff1f;下面小编就带着大家一起看看吧&#xff01;如果能够正常进入系统&#xff0c;建议您暂时移…...

    2022/11/19 21:17:02
  40. 还原更改请勿关闭计算机 要多久,配置windows update失败 还原更改 请勿关闭计算机,电脑开机后一直显示以...

    配置windows update失败 还原更改 请勿关闭计算机&#xff0c;电脑开机后一直显示以以下文字资料是由(历史新知网www.lishixinzhi.com)小编为大家搜集整理后发布的内容&#xff0c;让我们赶快一起来看一下吧&#xff01;配置windows update失败 还原更改 请勿关闭计算机&#x…...

    2022/11/19 21:17:01
  41. 电脑配置中请勿关闭计算机怎么办,准备配置windows请勿关闭计算机一直显示怎么办【图解】...

    不知道大家有没有遇到过这样的一个问题&#xff0c;就是我们的win7系统在关机的时候&#xff0c;总是喜欢显示“准备配置windows&#xff0c;请勿关机”这样的一个页面&#xff0c;没有什么大碍&#xff0c;但是如果一直等着的话就要两个小时甚至更久都关不了机&#xff0c;非常…...

    2022/11/19 21:17:00
  42. 正在准备配置请勿关闭计算机,正在准备配置windows请勿关闭计算机时间长了解决教程...

    当电脑出现正在准备配置windows请勿关闭计算机时&#xff0c;一般是您正对windows进行升级&#xff0c;但是这个要是长时间没有反应&#xff0c;我们不能再傻等下去了。可能是电脑出了别的问题了&#xff0c;来看看教程的说法。正在准备配置windows请勿关闭计算机时间长了方法一…...

    2022/11/19 21:16:59
  43. 配置失败还原请勿关闭计算机,配置Windows Update失败,还原更改请勿关闭计算机...

    我们使用电脑的过程中有时会遇到这种情况&#xff0c;当我们打开电脑之后&#xff0c;发现一直停留在一个界面&#xff1a;“配置Windows Update失败&#xff0c;还原更改请勿关闭计算机”&#xff0c;等了许久还是无法进入系统。如果我们遇到此类问题应该如何解决呢&#xff0…...

    2022/11/19 21:16:58
  44. 如何在iPhone上关闭“请勿打扰”

    Apple’s “Do Not Disturb While Driving” is a potentially lifesaving iPhone feature, but it doesn’t always turn on automatically at the appropriate time. For example, you might be a passenger in a moving car, but your iPhone may think you’re the one dri…...

    2022/11/19 21:16:57