1.androidä¸looperçå®ç°åçï¼ä¸ºä»ä¹è°ç¨looper.prepareå°±å¨å½å线ç¨å
³èäºä¸ä¸ªlo
2.android里面所说的源码looper是什么意思啊?
3.Handler 执行流程及源码解析
4.找到卡顿来源,BlockCanary源码精简分析
androidä¸looperçå®ç°åçï¼ä¸ºä»ä¹è°ç¨looper.prepareå°±å¨å½å线ç¨å ³èäºä¸ä¸ªlo
å®é ä¸ï¼æ¶æ¯åéå计åä»»å¡æ交ä¹åï¼å®ä»¬é½ä¼è¿å ¥æ线ç¨çæ¶æ¯éåä¸ï¼æ们å¯ä»¥æè¿ä¸ªçº¿ç¨ç§°ä¹ä¸ºç®æ 线ç¨ãä¸è®ºæ¯ä¸»çº¿ç¨è¿æ¯å线ç¨é½å¯ä»¥æ为ç®æ 线ç¨ãä¸ä¾ä¸ä¹æ以å¨ä¸»çº¿ç¨ä¸å¤çæ¶æ¯ï¼æ¯å 为æ们è¦æ´æ°UIï¼æç §androidä¸çè§å®æä»¬å¿ é¡»ç±ä¸»çº¿ç¨æ´æ°UIãæ以æ们让主线ç¨æ为äºç®æ 线ç¨ã
é£ä¹å¦ä½æ§å¶è®©æ个线ç¨æ为ç®æ 线ç¨å¢ï¼
è¿å°±å¼åºäºLooperçæ¦å¿µãAndroidç³»ç»ä¸å®ç°äºæ¶æ¯å¾ªç¯æºå¶ï¼Androidçæ¶æ¯å¾ªç¯æ¯é对线ç¨çï¼æ¯ä¸ªçº¿ç¨é½å¯ä»¥æèªå·±çæ¶æ¯éååæ¶æ¯å¾ªç¯ãAndroidç³»ç»ä¸çéè¿Looper帮å©çº¿ç¨ç»´æ¤çä¸ä¸ªæ¶æ¯éååæ¶æ¯å¾ªç¯ãéè¿Looper.myLooper()å¾å°å½å线ç¨çLooper对象ï¼éè¿Looper.getMainLooper()å¾å°å½åè¿ç¨ç主线ç¨çLooper对象ã
åé¢æå°æ¯ä¸ªçº¿ç¨é½å¯ä»¥æèªå·±çæ¶æ¯éååæ¶æ¯å¾ªç¯ï¼ç¶èæ们èªå·±å建ç线ç¨é»è®¤æ¯æ²¡ææ¶æ¯éååæ¶æ¯å¾ªç¯çï¼åLooperï¼ï¼è¦æ³è®©ä¸ä¸ªçº¿ç¨å ·ææ¶æ¯å¤çæºå¶æ们åºè¯¥å¨çº¿ç¨ä¸å è°ç¨Looper.prepare()æ¥å建ä¸ä¸ªLooper对象ï¼ç¶åè°ç¨Looper.loop()è¿å ¥æ¶æ¯å¾ªç¯ãå¦ä¸é¢çæºç æ示ã
å½æ们ç¨Handlerçæé æ¹æ³å建Handler对象æ¶ï¼æå®handler对象ä¸åªä¸ªå ·ææ¶æ¯å¤çæºå¶ç线ç¨ï¼å ·æLooperç线ç¨ï¼ç¸å ³èï¼è¿ä¸ªçº¿ç¨å°±æäºç®æ 线ç¨ï¼å¯ä»¥æ¥åæ¶æ¯å计åä»»å¡äºãHandlerä¸çæé æ¹æ³å¦ä¸ï¼
[java] view
plaincopyprint?源码
public Handler() {
if (FIND_POTENTIAL_LEAKS) {
final Class<? extends Handler> klass = getClass();
if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
(klass.getModifiers() & Modifier.STATIC) == 0) {
Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
klass.getCanonicalName());
}
}
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
mCallback = null;
}
public Handler(Looper looper) {
mLooper = looper;
mQueue = looper.mQueue;
mCallback = null;
}
public Handler() {
if (FIND_POTENTIAL_LEAKS) {
final Class<? extends Handler> klass = getClass();
if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
(klass.getModifiers() & Modifier.STATIC) == 0) {
Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
klass.getCanonicalName());
}
}
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
mCallback = null;
}
public Handler(Looper looper) {
mLooper = looper;
mQueue = looper.mQueue;
mCallback = null;
}
å¨ä¸è¿°ç计æ¶å¨çä¾åä¸ï¼ä¹æ以å¯ä»¥å¨ä¸»çº¿ç¨ä¸å¤çæ¶æ¯èæ们èªå·±å¹¶æ²¡æè°ç¨Looper.prepare()çæ¹æ³ï¼æ¯å 为Androidç³»ç»å¨Activityå¯å¨æ¶ä¸ºå ¶å建ä¸ä¸ªæ¶æ¯éååæ¶æ¯å¾ªç¯ï¼å½æ们ç¨æ åçHandleræé æ¹æ³å建对象æ¶åç¨äºå½å线ç¨çLooper对象ï¼åå°handlerä¸ä¸»çº¿ç¨ä¸çLooper对象è¿è¡äºå ³èã
androidä¸æ¯ä½¿ç¨Looperæºå¶æ¥å®ææ¶æ¯å¾ªç¯çï¼ä½æ¯æ¬¡å建线ç¨æ¶é½å åå§åLooperæ¯è¾éº»ç¦ï¼å æ¤Android为æ们æä¾äºä¸ä¸ªHandlerThreadç±»ï¼ä»å°è£ äºLooper对象ï¼æ¯æ们ä¸ç¨å ³å¿Looperçå¼å¯åéæ¾é®é¢ã
ä¸ç®¡æ¯ä¸»çº¿ç¨è¿æ¯å ¶ä»çº¿ç¨åªè¦æLooperç线ç¨ï¼å«ç线ç¨å°±å¯ä»¥åè¿ä¸ªçº¿ç¨çæ¶æ¯éåä¸åéæ¶æ¯åä»»å¡ã
æ们使ç¨HandlerThread类代æ¿ä¸ä¸ç¯æç« ä¸çå线ç¨ï¼å¹¶ç¨HandlerThreadç±»ä¸çLooper对象æé Handlerï¼åæ¥åæ¶æ¯çç®æ 线ç¨å°±ä¸æ¯ä¸»çº¿ç¨äºï¼èæ¯HandlerThread线ç¨ã代ç å¦ä¸ï¼
[java] view
plaincopyprint?
public class clockActivity extends Activity {
/** Called when the activity is first created. */
private String TAG="clockActivity";
private Button endButton;
private TextView textView;
private int timer=0;
private boolean isRunning=true;
private Handler handler;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
endButton=(Button)findViewById(R.id.endBtn);
textView=(TextView)findViewById(R.id.textview);
endButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
isRunning=false;
}
});
HandlerThread thread=new HandlerThread("myThread");
handler=new Handler(thread.getLooper());//ä¸HandlerThreadä¸çLooperå¯¹è±¡å ³è
thread.start();
Runnable r=new Runnable(){
@Override
public void run() {
// TODO Auto-generated method stub
if(isRunning){
textView.setText("èµ°äº"+timer+"ç§");
timer++;
handler.postDelayed(this, );//æ交任å¡rï¼å»¶æ¶1ç§æ§è¡
}
}
};
handler.postDelayed(r, );
}
}
ããpublic class clockActivity extends Activity {
/** Called when the activity is first created. */
private String TAG="clockActivity";
private Button endButton;
private TextView textView;
private int timer=0;
private boolean isRunning=true;
private Handler handler;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
endButton=(Button)findViewById(R.id.endBtn);
textView=(TextView)findViewById(R.id.textview);
endButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
isRunning=false;
}
});
HandlerThread thread=new HandlerThread("myThread");
handler=new Handler(thread.getLooper());//ä¸HandlerThreadä¸çLooperå¯¹è±¡å ³è
thread.start();
Runnable r=new Runnable(){
@Override
public void run() {
// TODO Auto-generated method stub
if(isRunning){
textView.setText("èµ°äº"+timer+"ç§");
timer++;
handler.postDelayed(this, );//æ交任å¡rï¼å»¶æ¶1ç§æ§è¡
}
}
};
handler.postDelayed(r, );
}
}
ããæ¤æ¶å¤çä»»å¡ä¼å¨handlerThread线ç¨ä¸å®æãå½ç¶è¿ä¸ªä¾åä¼åºçº¿å¼å¸¸ï¼ä¾ç¶æ¯å 为å¨é主线ç¨ä¸æ´æ°äºUIãè¿æ ·ååªæ¯ä¸ºäºå¤§å®¶è½å¤ç解è¿ç§æºå¶ã
ããæ·±å ¥ç解Androidæ¶æ¯å¤çæºå¶å¯¹äºåºç¨ç¨åºå¼åé常éè¦ï¼ä¹å¯ä»¥è®©æ们对线ç¨åæ¥ææ´å æ·±å»ç认è¯ï¼å¸æè¿ç¯æç« å¯ä»¥å¯¹æå们ææ帮å©ã
android里面所说的looper是什么意思啊?
Looper即:有消息循环的线程。
在Android里线程分为有消息循环的源码线程和没有消息循环的线程,有消息循环的源码线程一般都会有一个Looper,这个事android的源码新概念。主线程(UI线程)就是源码iapp注册源码一个消息循环的线程。针对这种消息循环的源码机制,引入一个新的源码机制Handle,有消息循环,源码就要往消息循环里 面发送相应的源码消息,自定义消息一般都会有对应的源码处理,消息的源码发送和清除,消息的源码处理,把这些都封装在Handle里面,源码注意Handle只是源码针对那些有Looper的线程,不管是赛马小游戏源码UI线程还是子线程,只要有Looper,就可以往消息队列里面添加东西,并做相应的处理。
Handler 执行流程及源码解析
本文深入解析了Handler的执行流程及源码,围绕Looper、MessageQueue、Message、Handler之间的协作运行机制,详细介绍了从sendMessage到handlerMessage的代码执行流程。
在UI线程中,Looper是自动创建的,通过调用Looper.prepareMainLooper()方法,此方法内部调用了Looper的prepare()方法来创建Looper对象,并将其存储在ThreadLocal中,实现线程内部的运动想像bci 源码数据存储。对于子线程,则需手动创建Looper,方法与UI线程一致,同样通过Looper.prepare()完成。
Handler在初始化时,通过ThreadLocal获取当前线程的Looper与MessageQueue。发送消息时,有三种方式:sendMessage、obtainMessage与post(runable),它们实质上操作相同,差异仅在于对Message的处理。最终,所有消息都会通过sendMessage方法调用到MessageQueue的enqueueMessage实现。
MessageQueue内部使用单链表维护消息列表,主要包含enqueueMessage与next两个操作:enqueueMessage实现数据插入,aps登录密码源码next通过死循环检查并删除链表中的消息。当MessageQueue中出现新消息时,Looper会立即检测到并处理。
Looper的loop()方法内有一个死循环,通过messageQueue.next()检查消息队列,获取并删除新消息。检测到新消息后,调用msg.target.dispatchMessage(msg)处理消息,此方法在Looper内执行,切换到Handler创建时的线程,由Handler发送的消息最终回到Handler内部,执行dispatchMessage(msg)方法。
Handler处理消息分为三种情况:执行run()方法,实现线程切换;使用Callback接口的实例作为mCallback,用于不使用Handler派生类的查看附件的源码情况;重写handlerMessage(msg)方法处理具体业务。至此,从sendMessage到handlerMessage的整个流程得以清晰展现。
整体流程总结如下:
1. 在Handler初始化时,获取线程的Looper与MessageQueue;
2. sendMessage方法最终调用enqueueMessage插入Message到队列,并将Handler赋值给Message对象的target属性;
3. MessageQueue在插入Message后,Looper检测到新消息,并开始处理;
4. Looper的loop方法通过traget属性获取到Handler对象,执行dispatchMessage方法;
5. 最终调用继承自Handler的handlerMessage方法处理具体业务。
找到卡顿来源,BlockCanary源码精简分析
通过屏幕渲染机制我们了解到,Android的屏幕渲染是通过vsync实现的。软件层将数据计算好后,放入缓冲区,硬件层从缓冲区读取数据绘制到屏幕上,渲染周期是ms,这让我们看到不断变化的画面。如果计算时间超过ms,就会出现卡顿现象,这通常发生在软件层,而不是硬件层。卡顿发生的原因在于软件层的计算时间需要小于ms,而计算的执行地点则在Handler中,具体来说是在UI的Handler中。Android进程间的交互通过Binder实现,线程间通信通过Handler。
软件层在收到硬件层的vsync信号后,会在Java层向UI的Handler中投递一个消息,进行view数据的计算。这涉及到测量、布局和绘制,通常在`ViewRootImpl`的`performTraversals()`函数中实现。因此,view数据计算在UI的Handler中执行,如果有其他操作在此执行且耗时过长,则可能导致卡顿,我们需要找到并优化这些操作。
要找到卡顿的原因,可以通过在消息处理前后记录时间,计算时间差,将这个差值与预设的卡顿阈值比较。如果大于阈值,表示发生了卡顿,此时可以dump主线程堆栈并显示给开发者。实现这一功能的关键在于在Looper中设置日志打印类。通过`Looper.loop()`函数中的日志打印,我们可以插入自定义的Printer,并在消息执行前后计算时间差。另一种方法是在日志中添加前缀和后缀,根据这些标志判断时间点。
BlockCanary是一个用于检测Android应用卡顿的工具,通过源码分析,我们可以了解到它的实现逻辑。要使用BlockCanary,首先需要定义一个继承`BlockCanaryContext`的类,并重写其中的关键方法。在应用的`onCreate()`方法中调用BlockCanary的安装方法即可。当卡顿发生时,BlockCanary会通知开发者,并在日志中显示卡顿信息。
BlockCanary的核心逻辑包括安装、事件监控、堆栈和CPU信息的采集等。在事件发生时,会创建LooperMonitor,同时启动堆栈采样和CPU采样。当消息将要执行时,开始记录开始时间,执行完毕后停止记录,并计算执行时间。如果时间差超过预设阈值,表示发生了卡顿,并通过回调传递卡顿信息给开发者。
堆栈和CPU信息的获取通过`AbstractSampler`类实现,它通过`post`一个`Runnable`来触发采样过程,循环调用`doSample()`函数。StackSampler和CpuSampler分别负责堆栈和CPU信息的采集,核心逻辑包括获取当前线程的堆栈信息和CPU速率,并将其保存。获取堆栈信息时,通过在`StackSampler`类中查找指定时间范围内的堆栈信息;获取CPU信息时,从`CpuSampler`类中解析`/proc/stat`和`/proc/mpid/stat`文件的CPU数据,并保存。
总结而言,BlockCanary通过在消息处理前后记录时间差,检测卡顿情况,并通过堆栈和CPU信息提供详细的卡顿分析,帮助开发者定位和优化性能问题。