App 性能优化
RecyclerView
1、onBindViewHolder 运行在 UI 线程,不宜进行逻辑等耗时操作,只适合把数据填入视图;
2、使用 support 包下面的 DiffUtil 局部刷新处理,DiffUtil(内部也是调用局部刷新方法)可以对比数据的差异,是否更改;
DiffUtil.DiffResult diffResult = DiffUtil.calculateDiff(new DiffCallBack(oldDatas, newDatas), true); diffResult.dispatchUpdatesTo(mAdapter);
计算在主线程,如果数据量大需要放在线程中,通过 Handler 更新。
局部中的部分刷新
需要通过重写 onBindViewHolder 中带有 payloads 集合参数的方法,如果 payloads 集合不为空,就可以直接进行值更新,否则就调用原本的 onBindViewHolder 方法;
payloads 集合中的值在调用刷新方法时传入,notifyItemChanged(position , "value");,可以是任意类型。
3、给可以复用 holder 的 RecyclerView 设置相同的 holder pool,在相同视图中,不会重新创建 holder;
RecyclerView recyclerView = new RecyclerView(context); //layoutManager.setRecycleChildrenOnDetach(true);//LinearLayoutManager 需要额外设置 recyclerView.setRecycledViewPool(mPool);
4、数据缓存,直接加载网络数据延迟比较重,加载缓存中的数据;
5、优化代码,尽量减少对象的创建,复用对象资源,比如监听;
6、getExtraLayoutSpace 预加载,RecyclerView 只缓存可见 view,滑动时初次加载会导致延迟卡顿,可以使用预加载处理,改变加载的滑动范围;
7、嵌套 RecyclerView 时,通过 setInitialPrefetchItemCount 设置首次显示个数,只有在嵌套且横向列表才生效;
四级缓存
mAttachedScrap : 缓存可见的 ViewHolder,用于执行 onLayout 的时候 ArrayList 集合
mCacheView:缓存将要隐藏 ViewHolder,下次将要显示的 ViewHolder 先从这个缓存里边获取 ArrayList 集合
mViewChcheExtension:需要用户自己实现的缓存
mRecyclerPool:缓存池,这个用户根据不同的 ViewType 保存缓存池
先从 mAttachedScrap 缓存查找 ViewHolder,然后从 mCacheView 查找,然后从 mViewCacheExtension,然后 mRecyclerPool,如果都没有找到就调用 onCreateViewHolder 新建。
黑白屏
系统加载 app 时,会先显示一个空白页,空白页背景色从应用主题中获取,一般默认是白色。
1、通过给启动页单独设置主题背景或颜色来避免,否则每个活动都会设置成一样的启动图片。
设置背景颜色透明,如果启动的活动耗时长,会导致启动卡顿,需要避免启动活动的耗时操作;
设置背景图片,需要引入多张图片来做屏幕适配,而且无法适配所有屏幕,所以一般使用 LayerDrawable,然后引入资源图片,通过设置偏移量等参数达到适配效果。
2、减少 Application 耗时任务。
把一些非必要的第三方等操作单独初始化,比如 IntentService。
app 启动页优化
apk 体积优化
布局优化
1、减少层次嵌套;
2、减少重复绘制,可以通过系统设置中打开 Show GPU Overdraw 设置查看,红色表示需要优化;
3、使用 include(复用布局)、merge(减少嵌套)、ViewStub(按需求加载,只能执行一次)。
包体积优化
1、使用 tint 避免多次导入资源;
2、资源配置,ndk、resConfigs、代码混淆等;
3、删除无引用资源。
ANR 处理
Activity 响应时间为5秒,广播接受者为10秒,服务为20秒。
发生 ANR 时, 系统会在 /data/anr/ 目录下生成一个 traces.txt 文件,可以通过 adb 命令将其导出到本地查看,一般的 ANR 都能定位到具体类。
1、iowait
日志中显示 CPU 占用情况,其中 io 占用大部分,表示有频繁的读写(数据库、文件)操作;
2、dalvik threads free
显示内存不够,一般会触发 oom 异常。
网络优化
1、减少调用次数跟发送和接收数据包的大小,Android Studio 内置 Monitor 工具,可以查看接收和发送速度;
2、网络缓存;
3、七牛资源图片等加载适合的资源,比如压缩图。
自定义 View
1、避免在 onDraw 绘制方法中做内存分配等耗时操作,因为频繁执行容易导致 oom;
2、尽量减少 onDraw 的调用次数。
webView 优化
1、webView 预加载,生成一个 webViewPool,通过 MutableContextWrapper 去替换不同上下文复用,减少创建时间;
webView 只能在主线程初始化,把 webView 放进 Handler 消息队列中去(Looper.myQueue().addIdleHandler()),虽然也是在 UI 线程,但是尽量避免造成卡顿。
2、通过 shouldInterceptRequest 监听请求,由客户端下载好在填充进 webResourceResponse 中;
@Override public WebResourceResponse shouldInterceptRequest(WebView view, String url) { if (checkImageRequest(url)) { File imageFile = Glide.with(view.getContext()) .asFile() .load(url) .submit() .get(); return WebResourceResponse( "image/png,*/*", "UTF-8", new FileInputStream(imageFile) ); } return super.shouldInterceptRequest(view, url); }
假设拦截为图片,可以通过本地 Glide 加载缓存,避免多次重复加载。
3、加载本地模板,通过 JS 方法注入数据,过程简单,但是比较耗费流量。
反射
频繁使用反射,调用方法,需要进行大量查找,影响效率。
android 系统 xml 布局就是通过解析标签,然后反射调用组件方法实现,比较具备扩展性,不过影响性能,所以一般代码创建对象速度会更快,不过可读性很差。
可以通过资源缓存,避免多次反射来优化速度。
Fragment 优化
创建方式
1、构造参数(异常销毁后,比如 ConfigChange 屏幕旋转,导致重建时会抛出异常,导致程序崩溃)
2、静态工厂(传入的参数保存在 arguments)
配置旋转属性
android:configChanges="orientation|keyboard|keyboardHidden"
在改变屏幕方向时,不执行onCreate(),而是直接执行 onConfigurationChanged(),避免重复初始化