Handler中有Loop死循环,为什么没有阻塞主线程,原理是什么

您所在的位置:网站首页 looper阻塞主线程 Handler中有Loop死循环,为什么没有阻塞主线程,原理是什么

Handler中有Loop死循环,为什么没有阻塞主线程,原理是什么

2024-07-06 12:26| 来源: 网络整理| 查看: 265

@Override

public void run() {

Log.e(“qdx”, "step 0 ");

Looper.prepare();

Toast.makeText(MainActivity.this, “run on Thread”, Toast.LENGTH_SHORT).show();

Log.e(“qdx”, "step 1 ");

Looper.loop();

Log.e(“qdx”, "step 2 ");

}

}).start();

我们知道Looper.loop();里面维护了一个死循环方法,所以按照理论,上述代码执行的应该是 step 0 –>step 1 也就是说循环在Looper.prepare();与Looper.loop();之间。

在子线程中,如果手动为其创建了Looper,那么在所有的事情完成以后应该调用quit方法来终止消息循环,否则这个子线程就会一直处于等待(阻塞)状态,而如果退出Looper以后,这个线程就会立刻(执行所有方法并)终止,因此建议不需要的时候终止Looper。

执行结果也正如我们所说,这时候如果了解了ActivityThread,并且在main方法中我们会看到主线程也是通过Looper方式来维持一个消息循环

public static void main(String[] args) {

Looper.prepareMainLooper();//创建Looper和MessageQueue对象,用于处理主线程的消息

ActivityThread thread = new ActivityThread();

thread.attach(false);//建立Binder通道 (创建新线程)

if (sMainThreadHandler == null) {

sMainThreadHandler = thread.getHandler();

}

Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);

Looper.loop();

//如果能执行下面方法,说明应用崩溃或者是退出了…

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

}

那么回到我们的问题上,这个死循环会不会导致应用卡死,即使不会的话,它会慢慢的消耗越来越多的资源吗?

对于线程即是一段可执行的代码,当可执行代码执行完成后,线程生命周期便该终止了,线程退出。而对于主线程,我们是绝不希望会被运行一段时间,自己就退出,那么如何保证能一直存活呢?简单做法就是可执行代码是能一直执行下去的,死循环便能保证不会被退出,例如,binder线程也是采用死循环的方法,通过循环方式不同与Binder驱动进行读写操作,当然并非简单地死循环,无消息时会休眠。但这里可能又引发了另一个问题,既然是死循环又如何去处理其他事务呢?通过创建新线程的方式。真正会卡死主线程的操作是在回调方法onCreate/onStart/onResume等操作时间过长,会导致掉帧,甚至发生ANR,looper.loop本身不会导致应用卡死。

主线程的死循环一直运行是不是特别消耗CPU资源呢? 其实不然,这里就涉及到Linux pipe/epoll机制,简单说就是在主线程的MessageQueue没有消息时,便阻塞在loop的queue.next()中的nativePollOnce()方法里,此时主线程会释放CPU资源进入休眠状态,直到下个消息到达或者有事务发生,通过往pipe管道写端写入数据来唤醒主线程工作。这里采用的epoll机制,是一种IO多路复用机制,可以同时监控多个描述符,当某个描述符就绪(读或写就绪),则立刻通知相应程序进行读或写操作,本质同步I/O,即读写是阻塞的。 所以说,主线程大多数时候都是处于休眠状态,并不会消耗大量CPU资源。 Gityuan–Handler(Native层)

Looper.java的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.”);

}

final MessageQueue queue = me.mQueue;

// Make sure the identity of this thread is that of the local process,

// and keep track of what that identity token actually is.

Binder.clearCallingIdentity();

final long ident = Binder.clearCallingIdentity();

// Allow overriding a threshold with a system prop. e.g.

// adb shell ‘setprop log.looper.1000.main.slow 1 && stop && start’

final int thresholdOverride =

SystemProperties.getInt(“log.looper.”

Process.myUid() + “.”

Thread.currentThread().getName()

“.slow”, 0);

boolean slowDeliveryDetected = false;

for (;😉 {

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

if (msg == null) {

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

return;

}

// This must be in a local variable, in case a UI event sets the logger

final Printer logging = me.mLogging;

if (logging != null) {

logging.println(">>>>> Dispatching to " + msg.target + " " +

msg.callback + ": " + msg.what);

}

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;

try {

msg.target.dispatchMessage(msg);

dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;

} finally {

if (traceTag != 0) {

Trace.traceEnd(traceTag);

}

}

if (logSlowDelivery) {

if (slowDeliveryDetected) {

if ((dispatchStart - msg.when)



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3