准备☞Android 性能优化☞卡顿问题工具 BlockCanary

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/Ghost_tal/article/details/82182202

之前一篇文章简单整理了Android 性能问题,这里的卡顿问题,是其中一部分,想来想去 还是单独整理这一部分,如果想了解Android 性能问题,请移步到 准备☞Android 性能优化

背景介绍

之前开发项目的时候,总是遇到性能问题,其中卡顿问题最为突出,项目庞大,优化起来总是望而却步。后来一位同事介绍可以通过 BlockCanary 这个工具(一位名为MarkZhai的大神,利用业余时间开发的)简单直白的分析这类问题。如获至宝,在此研究学习一下其中的原理总结一下。

BlockCanary有什么特点?

  • 操作简单,两行代码就可以打开监控,并能够对主线程操作进行完全透明的监控
  • 直观明了,能到直接输出有效 trace 信息,并精确定位问题所在哪一行

基于什么原理?

需要对消息分发机制有简单了解。下面会涉及到 Handler & Looper & MessageQueue 运行机制,如有不懂 或想深入了解,请
点击准备☞Android 异步消息分发机制

Android 应用程序有一个主线程(UI线程)ActivityThread,主线程被创建的时候,会默认初始化Looper(Looper.prepar()),创建Looper 对象,Looper 又会关联 MessageQueue ,Handler 发送消息 插入 MessageQueue 消息链表中,Looper的Loop() 方法不断从 MessageQueue 中获取消息更新UI。

由于整个应用的主线程只有一个Looper 对象,不管有多少 Handler ,都会回到这里。

接着看 Looper 的 Loop 方法

public static void loop() {
    ...
    for (;;) {
        ...
        // This must be in a local variable, in case a UI event sets the logger
        Printer logging = me.mLogging;
        if (logging != null) {
            logging.println(">>>>> Dispatching to " + msg.target + " " +
                    msg.callback + ": " + msg.what);
        }
        msg.target.dispatchMessage(msg);
        if (logging != null) {
            logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
        }
        ...
    }
}

接着看一下,msg.target.dispatchMessage(msg),其中msg.target 就是 发送消息的 Handler 对象,查看 dispatchMessage() 方法

    public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }

如果通过Handler.post(Runnable)方法 发送消息,会回调 Runnable 的 run 方法
如果通过 Handler.sendMessage 方法发送消息, 会回调 handleMessage 方法,最后都会调用 dispatchMessage 方法。
主线程更新UI,回调一定发生在主线程,由此可以看出 如果发生卡顿,一定发在 dispatchMessage 方法执行了耗时操作。通过统计 dispatchMessage 方法前后时间差,获取dispatchMessage 执行时间,是否查过阈值。

@Override
public void println(String x) {
    if (!mStartedPrinting) {
        mStartTimeMillis = System.currentTimeMillis();
        mStartThreadTimeMillis = SystemClock.currentThreadTimeMillis();
        mStartedPrinting = true;
    } else {
        final long endTime = System.currentTimeMillis();
        mStartedPrinting = false;
        if (isBlock(endTime)) {
            notifyBlockEvent(endTime);
        }
    }
}
private boolean isBlock(long endTime) {
    return endTime - mStartTimeMillis > mBlockThresholdMillis;
}
                                            流程图

流程图

如何使用?

  • 配置

在build.gradle 里
添加
compile ‘com.github.markzhai:blockcanary-android:1.5.0’
或者仅在debug包启用BlockCanary进行卡顿监控和提示
debugCompile ‘com.github.markzhai:blockcanary-android:1.5.0 ’
releaseCompile com.github.markzhai:blockcanary-android-no-op:1.5.0’

  • demo
public class DevApplication extends Application {

    @Override
    public void onCreate() {
        super.onCreate();
        BlockCanary.install(this, new BlockCanaryContext(){

            @Override
            public int provideBlockThreshold() {
                // 默认是10000毫秒
                return 500;
            }
        }).start();
    }
}
public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        findViewById(R.id.btn).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //模拟一个长时间操作,产生ANR
                try {
                    Thread.sleep(3 * 1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                Toast.makeText(getApplicationContext(), "点击完成", Toast.LENGTH_LONG).show();
            }
        });

    }
}

猜你喜欢

转载自blog.csdn.net/Ghost_tal/article/details/82182202