这节介绍些Handler的隐藏小技巧:
(1)利用Handler统计耗时任务:
Loop.loop方法源码可以看出,处理消息是可以统计时长的,也就是1和3之间时差;
Loop#loop
public static void loop() {
...
for (;;) {
Message msg = queue.next(); // might block
...
//1.处理消息之前的日志
final Printer logging = me.mLogging;
if (logging != null) {
logging.println(">>>>> Dispatching to " + msg.target + " " +
msg.callback + ": " + msg.what);
}
...
//2.处理消息
try {
msg.target.dispatchMessage(msg);
dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
...
//3.处理完消息之后的日志
if (logging != null) {
logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
}
...
}
统计时长方法:
private static final String MONITOR_START = ">>>>> Dispatching to";
private static final String MONITOR_END = "<<<<< Finished to";
looper.setMessageLogging(new Printer() {
long startTime = 0;
@Override
public void println(String x) {
if (x.contains(MONITOR_START)){
startTime = SystemClock.currentThreadTimeMillis();
}
if (x.contains(MONITOR_END)){
if (SystemClock.currentThreadTimeMillis()-startTime>1000){
Log.w(TAG,"slow dispatch message");
}
}
}
});
当然光统计时长也不行,往往需要定位具体哪个地方引起了耗时,因此需要线程栈打印信息更好:
private static final String MONITOR_START = ">>>>> Dispatching to";
private static final String MONITOR_END = "<<<<< Finished to";
private static final int TIME_THRESHOLD = 300;
private static HandlerThread thread;
public static void startThreadMonitor(Looper looper, int timeThreshold){
thread = new HandlerThread("handler-monitor:"+looper.getThread().getName());
thread.start();
Handler threadHandler = new Handler(thread.getLooper());
//耗时任务触发时候,打印stackTrace信息
Runnable runnable = new Runnable() {
@Override
public void run() {
StringBuilder sb = new StringBuilder();
StackTraceElement[] stackTrace = looper.getThread().getStackTrace();
for (StackTraceElement s:stackTrace){
sb.append(s.toString());
sb.append("\n");
}
Log.w(TAG,sb.toString());
}
};
looper.setMessageLogging(new Printer() {
@Override
public void println(String x) {
if (x.contains(MONITOR_START)){
postMessage(threadHandler,runnable,timeThreshold);
}
if (x.contains(MONITOR_END)){
removeMessage(threadHandler,runnable);
}
}
});
}
public static void stopThreadMonitor(){
thread.quit();
}
public static void postMessage(Handler handler, Runnable runnable,
int timeThreshold) {
handler.postDelayed(runnable, timeThreshold);
}
public static void removeMessage(Handler handler, Runnable runnable) {
handler.removeCallbacks(runnable);
}
其中大概原理就是在MONITOR_START写日志时候,开始往HandlerThread线程中的MessageQueue发送一个延迟消息,消息类型是Runnable,在MONITOR_END移除消息,如果两个时差超过设定是时间阈值,那么就会触发Runnable方法,执行一次stackTrace打印输出,反之因移除了消息,所以不会有任何打印;其实这里思想和Android上报ANR思想是一样的,Input事件,broadcast,service也都是提前埋一个延迟爆炸雷,然后等结束后拆雷,如果中途耗时太久,雷直接爆炸了;
(2)等待其他线程执行完执行
这个主要是利用runWithScissors,不过没对外公开API
handler除了发送send/post发送消息外还有一个runWithScissors,源码如下
public final boolean runWithScissors(final Runnable r, long timeout) {
if (r == null) {
throw new IllegalArgumentException("runnable must not be null");
}
if (timeout < 0) {
throw new IllegalArgumentException("timeout must be non-negative");
}
// 当为同一个线程时,直接执行runnable,而不需要加入到消息队列
if (Looper.myLooper() == mLooper) {
r.run();
return true;
}
// new 一个BlockRunnable对象
BlockingRunnable br = new BlockingRunnable(r);
return br.postAndWait(this, timeout);
}
public boolean postAndWait(Handler handler, long timeout) {
if (!handler.post(this)) {
return false;
}
synchronized (this) {
if (timeout > 0) {
final long expirationTime = SystemClock.uptimeMillis() + timeout;
while (!mDone) {
long delay = expirationTime - SystemClock.uptimeMillis();
if (delay <= 0) {
return false; // timeout
}
try {
// post runnable 之后,将调用线程变为wait状态
wait(delay);
} catch (InterruptedException ex) {
}
}
} else {
while (!mDone) {
try {
// post runnable 之后,将调用线程变为wait状态
wait();
} catch (InterruptedException ex) {
}
}
}
}
return true;
}
public void run() {
try {
mTask.run();
} finally {
synchronized (this) {
mDone = true;
// runnable 执行完之后,会通知wait的线程不再wait
notifyAll();
}
}
}
runWithScissors(runnable) 发送并执行一个同步的runnable。
- 当在同一个线程时,会直接执行这个runnable,而不需要放到queue里面。
- 当不在同一个线程时,会等这个runnable执行完,才返回,不然会block在这里。也就是Handler中提供的BlockRunnable的作用。
具体使用场景,比如初始化任务,如下WindowManagerService的main和initPolicy初始化时候:
public static WindowManagerService main(final Context context, final InputManagerService im,
final boolean haveInputMethods, final boolean showBootMsgs, final boolean onlyCore,
WindowManagerPolicy policy) {
DisplayThread.getHandler().runWithScissors(() ->
sInstance = new WindowManagerService(context, im, haveInputMethods, showBootMsgs,
onlyCore, policy), 0);
return sInstance;
}
private void initPolicy() {
UiThread.getHandler().runWithScissors(new Runnable() {
@Override
public void run() {
WindowManagerPolicyThread.set(Thread.currentThread(), Looper.myLooper());
mPolicy.init(mContext, WindowManagerService.this, WindowManagerService.this);
}
}, 0);
}
剩下的好像暂时没有发现其他特别的,Handler大概看完了,下一章节,看下AIDL;
网友评论