1.减少APK体积
1.删除无用资源
可通过lint扫描出无用资源,直接删除。
2.删除重复资源
通过python等脚本扫描出MD5值一样的图片或者、xml资源进行删除。
3.过大图片压缩
通过python等脚本扫描出多大图片进行图片压缩。
4.so库只加入指定平台
比如只加入armeabi、armeabi-v7a平台。
5.开启代码混淆与资源压缩
资源压缩、代码混淆一定程度上能减少些APK大小。
debug {
minifyEnabled true //代码混淆
shrinkResources true //资源压缩
proguardFiles getDefaultProguardFile('proguard-android.txt'),
'proguard-rules.pro'
}
2.启动优化
冷启动:启动时,后台没有任何该应用的进程,系统需要重新创建一个进程,并结合启动参数启动该应用。
热启动:启动时,系统已经有该应用的进程(比如按 home 键临时退出该应用)下启动该应用。
1.开启异步线程处理耗时操作
比如IO操作、sqlite初始化、一些其他第三方库的初始化等
2.检查并去除没必要启动时候初始化的逻辑
比如一些业务逻辑、一些工具类的初始化,可以在使用时初始化,没必要在启动时就初始化
3.闪屏页优化
设置闪屏页windowBackground图片,这样启动窗口的图片就会是闪屏页图片,而不是白屏。
闪屏页广告图片首次安装app打开后不加载到下载存储到本地,第二次启动再加载。
3.Crash与ANR
1.异常处理
接入bugly等第三方捕获异常平台,或者定义自己的UncaughtExceptionHandler将APP崩溃保存进文件并传给自己的服务器
2. ANR - 应用未响应
ANR 全称 Applicatipon No Response;Android 设计 ANR 的用意,是系统通过与之交互的组件(Activity,Service,Receiver,Provider)以及用户交互(InputEvent)进行超时监控,以判断应用进程(主线程)是否存在卡死或响应过慢的问题,通俗来说就是很多系统中看门狗(watchdog)的设计思想。
主要是通过 /data/anr 目录下面生成 traces.txt 文件,查找出相关Blocked、4大组件的anr日志等。可以接入爱奇艺XCrash、bugly上报 线上traces.txt文件。
4.内存优化
1.内存泄露工具
接入leackcanary到app中,开发、测试、产品验收、app发布前,尽最大努力处理掉内存泄漏问题。
2.内存泄漏场景
1) 单例导致内存泄露,单例Context要传getApplicationContext。
2) 静态变量导致内存泄露,静态变量存储在方法区,它的生命周期从类加载开始,到整个进程结束。一旦静态变量初始化后,它所持有的引用只有等到进程结束才会释放,同上Context问题。
3) 非静态内部类内存泄露,非静态内部类构造方法参数是外部类,所以在Activity中要定义静态内部类并使用。
4) Handler引起的内存泄漏,使用静态类+弱引用。
5) 广播、发布/订阅框架等注册后未取消注册引起的内存泄漏
6) IO、数据库Cursor等需要关闭的资源使用完未关闭引起的内存泄漏
7) 集合中的对象未清理造成内存泄露,集合中的对象如果已经没用,集合中还存在,则造成内存泄漏,要及时清除,如果集合是static修饰更容易出现此问题
8) 属性动画造成内存泄露,比如关闭Activity时,页面中属性
动画依然会不断地播放下去,动画引用所在的控件,所在的控件引用Activity
,这就造成Activity
无法正常释放,所以
在Activity
销毁的时候cancel
掉属性动画,避免发生内存泄漏。
8) WebView造成内存泄露,在销毁WebView
之前需要先将WebView从
父容器中移除,然后在销毁WebView。
3.内存优化
1) bitmap尽量指定大小、尺寸去使用,能够复用的bitmap考虑复用
2) 尽可能少地创建对象,比如创建统一的线程池去处理异步操作、自定义 view 时不要在 onDraw 方法里面频繁创建对象、尽量用 StringBuilder 或者 StringBuffer 来拼接字符串等
5.卡顿优化
1.卡顿
操作过程中卡顿了,一般就是 CPU 和 GPU 其中的一个或者多个无法短时间完成对应的任务,CPU 造成卡顿更常见,一般也是通过减少 CPU 的计算任务来优化卡顿。
2.检查卡顿
1) 利用UI线程Looper打印的日志
public static void loop() {
final Looper me = myLooper();
final MessageQueue queue = me.mQueue;
// ...
for (;;) {
Message msg = queue.next();
//dispatchMessage之前
Printer logging = me.mLogging;
if (logging != null) {
logging.println(">>>>> Dispatching to " + msg.target + " " +
msg.callback + ": " + msg.what);
}
//dispatchMessage
msg.target.dispatchMessage(msg);
//dispatchMessage之后
if (logging != null) {
logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
}
// ...
}
}
}
//设置Printer
Looper.getMainLooper().setMessageLogging(new Printer() {
@Override
public void println(String s) {
}
});
Android中界面的刷新需要在主线程或者说 UI 线程执行,界面的绘制起始点又利用了 Looper 消息循环机制,通过设置Printer,可打印dispatchMessage方法执行时长。
2) 利用Choreographer
Android系统每隔16ms发出VSYNC信号,触发对UI进行渲染,可以通过Choreographer设置相关回调,两次回调的时间周期应该在16ms,如果超过了16ms则认为发生了卡顿,这里暂不深入。
2.影响 CPU 的使用率一般有以下几个方面
-
读写文件,频繁多次读写文件操作,耗时且耗资源,要少做。比如SP尽量批量操作后apply 或者 commit.
-
解析大量图片,解码图片毫无疑问是一个计算量大的操作。
1.加载图片的时候最好根据实际显示的尺寸做压缩,并且保存压缩后的缩略图,方便下次直接加载。
2.列表滑动不加载图片
3.对不同的图片格式,用不同的解码格式。比如 png 格式的图片根据机器实际情况选择 8888 或者 4444 解码方式解码图片。如果是 jpg/jpeg 格式的图片,就用 565 的解码方式解码图片。这操作是减少内存,防止内存不足,系统进行GC操作从而耗费资源与CPU。
-
频繁请求网络,使用 okhttp 请求网络的话,尽量全局使用一个 httpclient ,这样做的好处是可以复用,提高网络请求效率。其他比如减少请求接口数量、自定义dns减少dns解析时间、传输采用gzip压缩数据等。
-
复杂的布局,CPU 需要大量的运算资源,特别是布局层级太深要进行不断递归运算与绘制,所以优化复杂的布局是很有必要的。
-
频繁创建对象,可能会短时间内消耗大量内存,然后内存不足的时候系统就会尝试 GC 来回收对象,而 GC 是很耗资源的操作。
参考文章
分享一波 Android 性能优化的总结!