一、前言
程序的优化在做过线上bug处理,布局层级优化,项目依赖库版本更新,重复库合并,删除未使用的资源,删除冗余的库,避免因为一个类而引入一个库的问题,leakcanary
(参考链接:https://square.github.io/leakcanary/)内存检测处理,lint
(参考链接:https://developer.android.google.cn/studio/write/lint?hl=zh-cn)静态代码检测后。如果想要继续优化,那么就需要使用其他工具了。比如开启严格模式StrictMode,该模式会将相关异常信息输出到控制台或者弹出对话框。亦或者使用Profiler等工具进行进行检测(需要注意的是Profiler只能解决一部分问题,不能解决所有问题)
需要注意的是本篇文章采用了Android Studio新版的UI进行操作,具体开启方式为Preferences->Appearance & Behavior ->New UI 。然后勾选Enable new UI并重启Android Studio。开发工具版本为
Android Studio Giraffe | 2022.3.1 Patch 4
Build #AI-223.8836.35.2231.11090377, built on November 14, 2023
Runtime version: 17.0.6+0-17.0.6b829.9-10027231 aarch64
VM: OpenJDK 64-Bit Server VM by JetBrains s.r.o.
目前为最新版本开发工具。
测试工具为红米K60,版本为Android13
据官方文档所说Profiler的工具监控UI在Android11、12、13上面的UI有不同的结果,这里需要注意版本不同带来的问题
如需打开 Profiler 窗口,请依次选择 View > Tool Windows > Profiler,或点击工具栏中的 Profile 图标 。
二、页面卡顿的排查方式
1、耗时操作的监控
可以选择采取以下方式启动应用
或者常规方式启动后使用以下方式打开监控工具
打开后选择CPU栏目打开
然后选择Java/Kotlin Method Sample (legacy)
进行记录。Java/Kotlin Method Trace
在测试时候记录的数据无法打开,所以没办法进行测试,后续可以打开的时候再进行尝试。需要注意的是对数据记录时间不能过长,否则会导致文件过大无法打开。所以只对关键操作进行记录分析即可。记录完后点击Stop
进行完成分析
记录完成后会自动打开,效果如下
选择左侧main
选项卡进行确定然后查看Flame Chart
。结果如下
其中右侧火焰图的绿色格子的内容是开发者可以尝试修改的地方。调用顺序从上往下为,应用函数到系统函数的调用顺序,鼠标光标悬停在某个方格上面可以看到该函数调用时长。然后开发者可以根据此进行相关代码修改。当然即使如此能修改的绿色格子也不足十分之一。
2、页面卡顿的监控
这里选择System Trace
进行记录
记录完后的页面如下
然后选择All Frames
->Janky Frames
。
可以通过点击Frame Duration
进行选择排序,查看耗时最长的Frame的ID,如果该选项卡下面没有内容,表示应用没有页面卡顿。根据此帧在左侧Janky Frames
的选项卡中进行查找该帧。
锁定后按动键盘上的M
键放大或者屏幕右上角的放大缩小按钮放大缩小。
然后可查看具体帧的渲染时间,当然不是所有帧都能提供有效信息,那么就只能针对可以修改的地方进行修改
Profiler
本文只记录部分用法,其余用法需查看官网或自己研究
五、优化的几种手段
- 在线程空闲时候操作
比如有时候某些控件加载耗时可以在其余组件加载完毕后再进行加载。
private static final String TAG = "MainActivity";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Looper.myQueue().addIdleHandler(mKeepIdleHandler);
Looper.myQueue().addIdleHandler(mOnlyOnceIdleHandler);
findViewById(R.id.textView).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Log.e(TAG, " 按钮被点击了 ");
}
});
}
private MessageQueue.IdleHandler mKeepIdleHandler = new MessageQueue.IdleHandler() {
@Override
public boolean queueIdle() {
Log.i(TAG, " mKeepIdleHandler queueIdle 被回调了 ");
return true;
}
};
private MessageQueue.IdleHandler mOnlyOnceIdleHandler = new MessageQueue.IdleHandler() {
@Override
public boolean queueIdle() {
Log.i(TAG, "mOnlyOnceIdleHandler queueIdle 被回调了 ");
return false;
}
};
参考链接: IdleHandler的使用及原理
2. 异步加载View,AsyncLayoutInflater原理。这样可以避免所有UI都在主线程绘制
https://blog.csdn.net/binzeefox/article/details/124168109