looper 心跳机制
Looper是Android系统中的一个类,它主要用于线程间消息传递和任务处理。在Looper的实现中,心跳机制是一个非常重要的部分。
Looper在启动后会进入一个无限循环中,不断地从消息队列MessageQueue中取出消息,然后交给对应的Handler进行处理。这个循环中的每一次迭代被称为一个“消息循环周期”,也就是Looper在执行一次完整的任务处理流程后所需的时间间隔。
Looper的心跳机制就是通过控制每个消息循环周期的时间间隔来保证程序运行的稳定性和响应性。具体流程如下:
- 在Looper.prepare()方法中,会创建一个MessageQueue对象和一个Looper对象,并将该Looper对象绑定到当前线程中。
- 在Looper.loop()方法中,主线程会进入一个无限循环中,不断地从消息队列MessageQueue中取出消息,直到退出应用或调用quit()方法停止循环。在每一次循环中,Looper都会检查一下是否有新的消息到达,如果有的话就会立即进行处理。
- 在每一次消息循环结束之后,Looper会等待一个心跳时间间隔,然后再开始下一轮的循环。这个心跳时间间隔的长度会根据当前系统的负载情况、CPU利用率、内存使用状况等因素而自动调整。通常情况下,心跳周期会在1~10毫秒之间波动。
Looper的心跳机制是通过控制消息循环周期的长度来保证应用程序的运行稳定性和响应性,确保在应用程序处理大量任务时不会因为线程阻塞或其他原因导致程序崩溃或出现异常情况。
MessageQueue的消息处理机制
MessageQueue是Android系统中非常重要的组成部分,它们实现了线程间通信和消息传递。
具体流程如下:1.首先,在主线程或子线程创建一个Handler时,Handler会自动绑定当前线程的Looper对象,以便能够通过Looper将消息发送到MessageQueue中。
Handler mHandler = new Handler();
2.当我们使用handler.sendMessage()方法时,实际上是将一个Message对象放入到消息队列MessageQueue中,该对象包括要处理的消息内容、Handler对象以及一些标记信息等。
Message msg = Message.obtain(); msg.what = 0; msg.obj = "Hello World"; mHandler.sendMessage(msg);
3.接着,在Looper.loop()方法中,主线程会进入一个无限循环中,不断地从消息队列MessageQueue中取出消息,直到退出应用或调用quit()方法停止循环。
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; while (true) { Message msg = queue.next(); // Might block. if (msg != null) { msg.target.dispatchMessage(msg); //交给Handler处理 msg.recycleUnchecked(); } } }
4.当消息队列MessageQueue中有消息时,Looper会从队列中取出消息,然后调用Message.target的dispatchMessage()方法,将消息发送给对应的Handler。
public final void dispatchMessage(Message msg) { if (msg.callback != null) { handleCallback(msg); } else { if (mCallback != null) { if (mCallback.handleMessage(msg)) { return; } } handleMessage(msg); //处理消息 } }
5.在Handler的handleMessage()方法中,我们可以根据消息的类型和内容进行相应的处理操作。例如,对于msg.what = 0和obj = "Hello World"的消息,可以在handleMessage()方法体中通过Toast或Log等方式将该消息打印出来。
public void handleMessage(Message msg) { switch (msg.what) { case 0: String text = (String) msg.obj; Toast.makeText(mContext, text, Toast.LENGTH_SHORT).show(); break; default: super.handleMessage(msg); } }
Handler和MessageQueue机制是Android系统中非常重要的组成部分,它们实现了线程间通信和消息传递。当我们需要从子线程更新UI等任务时,就可以使用Handler进行异步消息传递,让主线程处理相关任务。
消息的执行方式
消息的执行方式主要包括同步、异步、同步屏障三种方式。
1.同步消息:同步消息是指在消息发送时,发送者线程会阻塞直到接收者线程处理完这个消息才会继续执行。同步消息的发送方式使用sendMessage()方法,在该方法返回前,发送者线程会一直等待直到接收者线程处理完这个消息。
//发送一条同步消息,直到消息处理完成后才会继续执行 Message msg = Message.obtain(); msg.what = 0; mHandler.sendMessage(msg);
2.异步消息:异步消息是指在消息发送时,发送者线程不会阻塞,而是继续往下执行,而接收者线程会在Looper中循环处理这个消息。异步消息的发送方式使用sendMessage()方法,在该方法返回后,发送者线程就可以继续执行,而不需要等待消息处理完成。(UI刷新就是异步消息)
//发送一条异步消息,无需等待接收者线程处理完成 Message msg = Message.obtain(); msg.what = 0; mHandler.sendMessageDelayed(msg, 1000); //延迟1秒发送
3.同步屏障:同步屏障是指一组消息在处理过程中,要求先完成其中一个消息的处理,才能继续处理其他消息。同步屏障的发送方式使用sendMessageAtFrontOfQueue()方法,它会将当前消息插入到消息队列的最前面,等待队列中已有的消息全部处理完成后再进行处理。(在UI刷新消息之前就会有同步屏障)
//发送一条同步屏障消息,要求先处理完msg1之后才能处理其他消息 Message msg = Message.obtain(); msg.what = 0; mHandler.sendMessageAtFrontOfQueue(msg);
阻塞的实现
阻塞的实现主要是通过使用Looper的loop()方法来实现的。当调用Looper.loop()方法时,会进入一个无限循环中,不断地从消息队列MessageQueue中取出消息进行处理,如果队列中没有消息,那么就会等待一段时间(即心跳间隔)再重新尝试从队列中取出消息。
这个等待的过程是一个阻塞的过程,它会把当前的线程阻塞在这里,直到有新的消息到达或程序退出或调用了Looper.quit()方法退出循环。
ANR
ANR(Application Not Responding)指的是应用程序无响应的错误,它表示应用程序在执行某个操作时长时间没有响应。在Android系统中,如果一个应用程序在主线程中执行了耗时的操作而导致主线程被阻塞,那么系统就会弹出一个对话框警告用户当前应用程序出现了ANR错误,并提示用户选择“等待”或“关闭应用程序”。
ANR通常是由于一些长时间的I/O操作、耗时的计算或者其他阻塞主线程的原因引起的。当主线程被阻塞时,应用程序的用户界面就会无响应,用户无法与应用程序进行交互,这就给用户带来了不好的体验。
为了避免ANR错误,开发人员可以采取以下措施:
- 将耗时的操作放在子线程中执行,避免在主线程中执行。
- 使用异步任务或线程池等机制来执行耗时的操作,从而避免阻塞主线程。
- 在主线程中使用Handler或者AsyncTask等机制来更新UI界面。
- 优化应用程序的代码,减少不必要的计算和I/O操作。
阻塞和anr的区别
阻塞和ANR都表示应用程序在执行某个操作时没有响应,但它们的具体含义和产生的原因有所不同。
阻塞指的是一个线程的执行被暂停,等待某些资源变得可用或等待某个操作完成。在并发编程中,为了避免线程之间的竞争和死锁等问题,需要特别关注那些可能会造成阻塞的函数调用。
ANR(Application Not Responding)指的是应用程序无响应的错误,它表示应用程序在执行某个操作时长时间没有响应。在Android系统中,如果一个应用程序在主线程中执行了耗时的操作而导致主线程被阻塞,那么系统就会弹出一个对话框警告用户当前应用程序出现了ANR错误,并提示用户选择“等待”或“关闭应用程序”。
可以看出,阻塞是实现多线程编程中的一个概念,而ANR则是针对Android应用程序中主线程被阻塞的一种错误。虽然阻塞可能会导致ANR错误,但两者的产生原因和概念是不同的。
Skipped X frames! The application may be doing too much work on its main thread 错误
这个错误提示通常出现在Android开发中,它表示应用程序在主线程中做了太多的工作,导致UI渲染线程被阻塞,最终导致掉帧。
该错误提示一般是由于以下原因引起的:
- 复杂的布局或绘制操作导致CPU负载过高。
- 执行了耗时的计算或I/O操作。
- 数据加载慢导致UI更新被阻塞。
为了解决这个问题,可以采取以下措施:
- 将复杂的布局和绘制操作放在子线程中执行,避免在主线程中执行。
- 使用异步任务或线程池等机制来执行耗时的计算或I/O操作,从而避免阻塞主线程。
- 使用图片压缩和内存缓存等机制优化图片加载,加快数据加载速度。
- 对于卡顿比较严重的情况,可以使用Hierarchy Viewer等工具进行性能分析,找到问题所在。