Android应用开发中一种调试应用UI线程消息分发的方法

  如何在不修改Android平台代码的情况下,通过修改应用本身的代码实现主线程Looper的托管,并打印UI线程的消息队列实时内容。

  第三方应用或多系统平台同时开发时,不修改平台侧代码,而只通过应用本身接管UI线程就能实现打印UI线程的消息队列实时内容,进而达到分析应用性能的目的。避免了编译平台的耗时,增加了多平台开发的便利性。

  在优化应用性能时,我们希望通过应用的消息队列来分析在某时某刻,应用到底具体在做什么事情,并通过消息做为线索去查找消息来源,找到消息的发送来源后可以结合整个系统的框架、流程、实时帧率来进行优化。


代码如下:

需要托管主线程的Looper,则在应用启动时调用LooperAgent的install方法。

 

package com.xunyou.launcher.utils;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import android.os.Binder;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.os.MessageQueue;

/**
* LooperAgent will take over the main looper
*
*/
public class LooperAgent implements Runnable {
static final String TAG = LooperAgent.class.getSimpleName();
private static final Object EXIT = new Object();
private static final ThreadLocal RUNNINGS = new ThreadLocal();
private static Thread.UncaughtExceptionHandler uncaughtExceptionHandler;
private static Handler handler = new Handler(Looper.getMainLooper());

/**
* Install SafeLooper in the main thread
* Notice the action will take effect in the next event loop
*/
public static void install() {
handler.removeMessages(0, EXIT);
handler.post(new LooperAgent());
}

/**
* Exit SafeLooper after millis in the main thread
* Notice the action will take effect in the next event loop
*/
public static void uninstallDelay(long millis) {
handler.removeMessages(0, EXIT);
handler.sendMessageDelayed(handler.obtainMessage(0, EXIT), millis);
}

/**
* Exit SafeLooper in the main thread
* Notice the action will take effect in the next event loop
*/
public static void uninstall() {
uninstallDelay(0);
}

/**
* Tell if the SafeLooper is running in the current thread
*/
public static boolean isSafe() {
return RUNNINGS.get() != null;
}

/**
* The same as Thread.setDefaultUncaughtExceptionHandler
*/
public static void setUncaughtExceptionHandler(Thread.UncaughtExceptionHandler h) {
uncaughtExceptionHandler = h;
}

@Override
public void run() {
if (RUNNINGS.get() != null)
return;

Method next;
Field target;
Field callback;
try {
Method m = MessageQueue.class.getDeclaredMethod("next");
m.setAccessible(true);
next = m;
Field f = Message.class.getDeclaredField("target");
f.setAccessible(true);
target = f;
Field c = Message.class.getDeclaredField("callback");
c.setAccessible(true);
callback = c;
} catch (Exception e) {
return;
}

RUNNINGS.set(this);
MessageQueue queue = Looper.myQueue();
Binder.clearCallingIdentity();
final long ident = Binder.clearCallingIdentity();

while (true) {
try {
Message msg = (Message) next.invoke(queue);
if (msg == null || msg.obj == EXIT)
break;

Logger.debug(Logger.FUNC_COMMON, TAG, ">>>>> Dispatching to " + target.get(msg) + " " +
callback.get(msg) + ": " + msg.what);

Handler h = (Handler) target.get(msg);
h.dispatchMessage(msg);

Logger.debug(Logger.FUNC_COMMON, TAG, "<<<<< Finished to "
+ target.get(msg) + " " + callback.get(msg));

final long newIdent = Binder.clearCallingIdentity();
if (newIdent != ident) {
Logger.debug(Logger.FUNC_COMMON, TAG, "Thread identity changed from 0x"
+ Long.toHexString(ident) + " to 0x"
+ Long.toHexString(newIdent) + " while dispatching to "
+ target.get(msg).getClass().getName() + " "
+ callback.get(msg) + " what=" + msg.what);
}
msg.recycle();
} catch (Exception e) {
Thread.UncaughtExceptionHandler h = uncaughtExceptionHandler;
Throwable ex = e;
if (e instanceof InvocationTargetException) {
ex = ((InvocationTargetException) e).getCause();
if (ex == null) {
ex = e;
}
}
// e.printStackTrace(System.err);
if (h != null) {
h.uncaughtException(Thread.currentThread(), ex);
}
new Handler().post(this);
break;
}
}

RUNNINGS.set(null);
}
}

未经允许谢绝转载:迅优网络科技 » Android应用开发中一种调试应用UI线程消息分发的方法

赞 (0) 评论 (0) 分享 ()

评论 抢沙发

评论前必须登录!