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

之前一篇文章简单整理了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;
}
                                            流程图

流程图

如何使用?
  • 配置

1.在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'

2.AndroidManifest.xml 添加权限

    <!--SDCard中创建与删除文件权限-->
    <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>
    <!--向SDCard写入数据权限-->
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

3.在代码中动态添加权限

private static final int REQUEST_EXTERNAL_STORAGE = 1;
      private static String[] PERMISSIONS_STORAGE = {
            "android.permission.READ_EXTERNAL_STORAGE",
            "android.permission.WRITE_EXTERNAL_STORAGE" };

    public void checkPermissions(Activity activity) {
        try {
            //检测是否有写的权限
            int permission = ActivityCompat.checkSelfPermission(activity,
                    "android.permission.WRITE_EXTERNAL_STORAGE");
            if (permission != PackageManager.PERMISSION_GRANTED) {
                // 没有写的权限,去申请写的权限,会弹出对话框
                Toast.makeText(this, "请开通权限,否则无法使用", Toast.LENGTH_LONG).show();
                ActivityCompat.requestPermissions(activity, PERMISSIONS_STORAGE,REQUEST_EXTERNAL_STORAGE);
            } else {
                Toast.makeText(this, "授权成功", Toast.LENGTH_LONG).show();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

在Activity 的 onCreate()方法里调用 checkPermission(this);

4.继承Application 在OnCreate() 方法里添加

BlockCanary.install(this, new BlockCanaryContext(){
            @Override
            public int provideBlockThreshold() {
                // 默认是10000毫秒
                return 500;
            }
        }).start();

demo演示

DevApplication.java
public class DevApplication extends Application {

    @Override
    public void onCreate() {
        super.onCreate();
        BlockCanary.install(this, new BlockCanaryContext(){
            @Override
            public int provideBlockThreshold() {
                // 默认是10000毫秒
                return 500;
            }
        }).start();
    }
}
MainActivity.java
public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        checkPermissions(this);
        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();
            }
        });

    }

}

      private static final int REQUEST_EXTERNAL_STORAGE = 1;
      private static String[] PERMISSIONS_STORAGE = {
            "android.permission.READ_EXTERNAL_STORAGE",
            "android.permission.WRITE_EXTERNAL_STORAGE" };

    public void checkPermissions(Activity activity) {
        try {
            //检测是否有写的权限
            int permission = ActivityCompat.checkSelfPermission(activity,
                    "android.permission.WRITE_EXTERNAL_STORAGE");
            if (permission != PackageManager.PERMISSION_GRANTED) {
                // 没有写的权限,去申请写的权限,会弹出对话框
                Toast.makeText(this, "请开通权限,否则无法使用", Toast.LENGTH_LONG).show();
                ActivityCompat.requestPermissions(activity, PERMISSIONS_STORAGE,REQUEST_EXTERNAL_STORAGE);
            } else {
                Toast.makeText(this, "授权成功", Toast.LENGTH_LONG).show();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

PS: 在最新8.0 版本验证不支持,Notification 的弹出,但是可以正常检测卡顿问题。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值