Android性能优化
android内存监测
-
LeakCanary 详情
一个可以检测程序在运行过程中发生的内存泄漏问题,通过简单的代码配置,可以方便的找出我们应用中的内存问题
-
Memory Monitor
android studio
自带的实时内存分析工具,我们可以通过实时的内存、CPU等的波动来分析问题,如果某个页面反复进入后内存持续增长,我们就要注意了。
内存优化方案
-
界面不可见时及时回收部分内存
当用户打开了另外一个程序,我们的程序界面已经不再可见的时候,我们应当将所有和界面相关的资源进行释放。在这种场景下释放资源可以让系统缓存后台进程的能力显著增加,因此也会让用户体验变得更好。
检测界面是否可见我们可以重写如下方法:
@Override public void onTrimMemory(int level) { super.onTrimMemory(level); switch (level) { case TRIM_MEMORY_UI_HIDDEN: // 进行资源释放操作 break; } }
onTrimMemory
方法只有当程序中的所有UI组件全部不可见的时候才会触发完全不可见时候才会调用,这和onStop()
方法还是有很大区别的,因为onStop()
方法只是当一个Activity
完全不可见的时候就会调用,比如说用户打开了我们程序中的另一个Activity
。 因此,我们可以在onStop()
方法中去释放一些Activity相关的资源,比如说取消网络连接或者注销广播接收器等,但是像UI相关的资源应该一直要等到onTrimMemory(TRIM_MEMORY_UI_HIDDEN)
这个回调之后才去释放,这样可以保证如果用户只是从我们程序的一个Activity
回到了另外一个Activity
,界面相关的资源都不需要重新加载,从而提升响应速度。 -
使用Handler时尽量弱引用
我们经常会在
handler
中进行一些延时任务,这些延时任务会导致Activity
被引用,从而发生内存泄漏,为了避免这类事情发生,我们可以对handler
使用弱引用。public class MainActivity extends AppCompatActivity { public static final String TAG = "carson:"; private Handler showhandler; // 主线程创建时便自动创建Looper & 对应的MessageQueue // 之后执行Loop()进入消息循环 @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); //1\. 实例化自定义的Handler类对象->>分析1 //注: // a. 此处并无指定Looper,故自动绑定当前线程(主线程)的Looper、MessageQueue; // b. 定义时需传入持有的Activity实例(弱引用) showhandler = new FHandler(this); // 2\. 启动子线程1 new Thread() { @Override public void run() { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } // a. 定义要发送的消息 Message msg = Message.obtain(); msg.what = 1;// 消息标识 msg.obj = "AA";// 消息存放 // b. 传入主线程的Handler & 向其MessageQueue发送消息 showhandler.sendMessage(msg); } }.start(); // 3\. 启动子线程2 new Thread() { @Override public void run() { try { Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } // a. 定义要发送的消息 Message msg = Message.obtain(); msg.what = 2;// 消息标识 msg.obj = "BB";// 消息存放 // b. 传入主线程的Handler & 向其MessageQueue发送消息 showhandler.sendMessage(msg); } }.start(); } // 分析1:自定义Handler子类 // 设置为:静态内部类 private static class FHandler extends Handler{ // 定义 弱引用实例 private WeakReference<Activity> reference; // 在构造方法中传入需持有的Activity实例 public FHandler(Activity activity) { // 使用WeakReference弱引用持有Activity实例 reference = new WeakReference<Activity>(activity); } // 通过复写handlerMessage() 从而确定更新UI的操作 @Override public void handleMessage(Message msg) { switch (msg.what) { case 1: Log.d(TAG, "收到线程1的消息"); break; case 2: Log.d(TAG, " 收到线程2的消息"); break; } } } }
这样一来,
handler
中发送延时消息便不会发生内存泄漏了。当然避免
handler
内存泄漏还可以采取当Activity
结束使用时候,清空消息队列的操作,如下:@Override protected void onDestroy() { super.onDestroy(); mHandler.removeCallbacksAndMessages(null); // 外部类Activity生命周期结束时,同时清空消息队列 & 结束Handler生命周期 }
-
加载图片的注意事项 详情
- 不在小控件上显示大图
- 列表类图片,仅加载当前页面可见的图片
- 有显示原图需求时,要判断可用内存,内存不够时要压缩图片
-
避免内存抖动 详情
-
尽量避免在循环体内创建对象,应该把对象创建移到循环体外。
-
注意自定义View的onDraw()方法会被频繁调用,所以在这里面不应该频繁的创建对象。
-
当需要大量使用Bitmap的时候,试着把它们缓存在数组中实现复用。
-
对于能够复用的对象,同理可以使用对象池将它们缓存起来。
-
布局优化 详情
避免过度绘制
-
什么是过度绘制
过度绘制就是在同一个位置,有多次的颜色绘制过程。常见的情况就是在同一个位置堆叠了许多控件,这会造成一些性能问题,严重的情况会造成卡顿。
-
如何检测过度绘制
开发者选项->调试GPU过度绘制->显示过度绘制区域
-
过度绘制优化
- 移除控件中不需要的背景
- 将layout层级扁平化
- 减少透明度的使用
- 自定义
View
中减少重复绘制区域
布局优化技巧
- 简单布局优先使用
LinearLayout
或FragmentLayout
- 复杂布局优先使用
constrainLayout
- 使用
include
标签提高复用性 - 使用
ViewStub
标签延迟加载 -
onDraw()
中不要创建新的局部变量以及不要做耗时操作
网络优化 详情
为什么要网络优化
- 过多的无用网络请求,会消耗用户的网络流量。流量消耗过大会流失用户
- 频繁的网络操作会导致设备用电量提升
- 网络弹框的频繁出现会降低用户体验
- 应用更新、大文件下载等场景,更优的网络传输速度可提升用户体验
网络优化的方式
- 使用
GZip
压缩,数据压缩后可以减少流量的消耗,减少传输的时间 - 使用IP直连,DNS域名解析是一个较为耗时操作,可以直连IP减少解析时间
- 图片加载
- 使用
WebP
格式可以大幅节省流量 - 图片按需加载,列表中图片只加载缩略图
- 大图上传时,采用分片传输,失败只传对应片段
- 用户体验影响不大时,手机原图压缩后再传输
- 使用
- 减少接口数量,同一个页面尽量只使用一个接口,数据可以放到后台去拼凑
- 利用缓存,对数据设定有效期,有效期内数据不重复请求
- 检测网络状态,不同网络转态执行不同策略,例如移动网络不加载图片,2G网络只加载标题等。
- 文件上传、下载采用断点续传,不浪费已传输完成部分流量
- 利用抓包工具模拟多种情况,在实践中调整不断优化用户体验
启动优化 详情
闪屏页优化
主流APP
是在应用启动时候会加载一个默认的主题,用来去掉应用启动时候的黑/白屏的情况
<style name="AppThemeWelcome" parent="Theme.AppCompat.NoActionBar">
...
<item name="android:windowBackground">@drawable/logo</item> <!-- 默认背景-->
</style>
应用主题到Application
或Activity
<activity android:name=".ui.activity.DemoSplashActivity"
android:configChanges="orientation|screenSize|keyboardHidden"
android:theme="@style/AppThemeWelcome"
android:screenOrientation="portrait">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
其实就是个障眼法而已,提前让你看到了假的界面。也算是一种不错的方法,但是治标不治本。
第三方库懒加载
在开发中会用到很多的三方库,如友盟、百度、bugly
、图片库、网络库等。
这些都是必须的,不能去掉,那么办法就是异步加载了,可以有以下几种思路
-
像友盟,bugly这样的业务非必要的可以的异步加载。
-
比如地图,推送等,非第一时间需要的可以在主线程做延时启动。当程序已经启动起来之后,在进行初始化。
-
对于图片,网络请求框架必须在主线程里初始化了。
-
我们一般会有闪屏页面,也可以把延时启动的地图、推送的启动放到这个页面
按照以上方式处理后,还可以进一步降低应用启动时间。
WebView
启动优化
- WebView第一次创建比较耗时,可以预先创建WebView,提前将其内核初始化。
- 使用WebView缓存池,用到WebView的地方都从缓存池取,缓存池中没有缓存再创建,注意内存泄漏问题。
- 本地预置html和css,WebView创建的时候先预加载本地html,之后通过js脚本填充内容部分。
数据项预加载
主页数据变化不大时候,可以再第一次启动后,缓存主页数据到本地,下次启动先读取本地数据,页面完全显示后再去请求新数据进行增量更新。
安装包体积优化
体积优化的必要性
安装包体积是用户搜索应用后能第一眼看到的数据,虽然现在的应用体积越来越大,但小体积的App
依旧是很多存储空间紧张用户的痛点。所以减少安装包体积是性能优化方面必不可少的一步。
减少应用体积的N种办法
-
使用
lint
工具删除无用的资源 -
简单的切图尽量替换为
shape
类型的xml
文件 -
形状一致的图片只使用一个切图,比如方向不同的箭头、图像相同着色不同的切图等
-
对图片进行压缩,优先使用
WebP
格式图像 -
使用矢量图(.9)图来实现大小可变的背景图
-
代码混淆,使用
proGuard
代码混淆器工具,它包括压缩、优化、混淆等功能。 -
插件化,不需要的部分可以存在服务器,当用到时候动态下载。
电量优化
电量优化我放到最后说,是因为这个优先级比较低,因为一般APP
在使用过程中,很难造成电量的明显下降,除非是游戏、相机或者视频类APP
电量优化相对来说比较简单,在开发中注意一下几点就可以了:
- 数据备份、日志报告等后台活动,可以放到电量充足或者正在充电时候执行
- 除视屏播放外,一直避免一直亮屏。
- 录音、GPS、相机等耗电操作,在执行完成后及时释放对应资源
- 后台不必要的
service
记得及时关闭
总结
Android
的性能优化是一个长期且漫长的过程。一般企业在开发中都是先实现功能再去管性能,这样做会导致后期优化起来麻烦且耗时。建议有可能的话尽量保持一个好的开发习惯,在项目初期就注意性能方面的事情,不要引入无用的内容、保持代码整洁、及时删除已废弃模块等,这样开发的项目才回高效且易维护。