一.概述
很多APP都要做性能优化,但是做性能优化首先得要发现问题,本本篇文章将教会你怎么发现android卡顿的地方。
二.如何发现卡顿点
卡顿一般都是线程处理消息卡顿,那我们就看看如何发现某一个消息卡顿吧,看这里前建议先了解一下Handler的原理,我就不再介绍了。
我们每一个线程都会有一个loop,这个负责循环处理消息,我们直接上代码吧:
/**
* Run the message queue in this thread. Be sure to call
* {@link #quit()} to end the 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();
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;
if (traceTag != 0) {
Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
}
try {
msg.target.dispatchMessage(msg);
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
if (logging != null) {
logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
}
// Make sure that during the course of dispatching the
// identity of the thread wasn't corrupted.
final long newIdent = Binder.clearCallingIdentity();
if (ident != newIdent) {
Log.wtf(TAG, "Thread identity changed from 0x"
+ Long.toHexString(ident) + " to 0x"
+ Long.toHexString(newIdent) + " while dispatching to "
+ msg.target.getClass().getName() + " "
+ msg.callback + " what=" + msg.what);
}
msg.recycleUnchecked();
}
}
重点在这:
// 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;
if (traceTag != 0) {
Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
}
try {
msg.target.dispatchMessage(msg);
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
if (logging != null) {
logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
}
我们能看到在分发消息前后会打印一个log。这里可以告诉我们这个消息处理了多长时间。你可以让你的每一个loop都实现println方法。可以看看我写的一个例子:
public class MainActivity extends Activity implements Printer {
String TAG = "Activity";
long mesgStart = 0;
long mesgEnd = 0;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Handler handler = new MyHandler();
getMainLooper().setMessageLogging(this);
Message message = new Message();
message.what = 1;
handler.sendMessage(message);
}
@Override
public void println(String x) {
if (x.contains("Dispatching")) {
mesgStart = System.currentTimeMillis();
Log.d(TAG, x);
} else if (x.contains("Finished")) {
mesgEnd = System.currentTimeMillis();
Log.d(TAG, "Message processing time : " + (mesgEnd - mesgStart) + "ms " + x);
}
}
}
class MyHandler extends Handler {
String TAG = "Activity";
public void handleMessage(Message msg) {
Log.d(TAG, "handleMessage msg is!!!!!!!!!!!!!!!!!!! " + msg.what);
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
代码很简单,就是实现了println方法,并把它set到mainLooper中,然后自己重写了一个hander,里边sleep 3s,我们运行代码看一下效果吧:
12-23 16:11:14.504 18882 18882 D Activity: >>>>> Dispatching to Handler (water.android.testapp.MyHandler) {9f39ff9} null: 1
12-23 16:11:14.504 18882 18882 D Activity: handleMessage msg is!!!!!!!!!!!!!!!!!!! 1
12-23 16:11:17.505 18882 18882 D Activity: Message processing time : 3001ms <<<<< Finished to Handler (water.android.testapp.MyHandler) {9f39ff9} nul
三.总结
性能优化的难点主要是发现问题,其次是解决问题的过程。这里就教会大家一个发现问题的方法,APP开发的同学也可以通过这里来打点数据,看看自己APP每个方法的平均耗时时间,然后着重解决耗时长的消息