为了账号安全,请及时绑定邮箱和手机立即绑定

【学习打卡】第12天 App 专项技术优化

标签:
Android

课程名称:Top团队大牛带你玩转Android性能分析与优化
课程章节:App性能概览与平台化实践
主讲老师:随风绽放

课程内容

1.列表专项优化

列表指的是开发中用到的 ListView 和 RecyclerView 控件,列表控件使用不当会出现滑动卡顿等问题。

优化列表的常规方案

  • 优化列表最常用的优化是 ConvertView 的复用,使用 ConvertView 时,当列表是第一次展示时,系统会创建 ConvertView,当列表 item 划出屏幕时,释放item 使用的 ConvertView,新数据进入屏幕显示,新数据会使用滑出 item 的 ConvertView,避免每次都重新创建 item。
  • 配合 ConvertView 还会使用 ViewHolder,ViewHolder就是维护View引用的类,每个布局的控件通过 findViewById 之后会被保存下来,下次需要刷新的时候也是直接使用,绕过了再次 findViewById 的过程。
  • 任务异步处理,如果在getView()等方法中有明显的耗时操作,可以采用异步的方式来处理。

优化列表的其它方案

在大型项目中或者复杂的列表页仅靠常规方案还是很难解决卡顿问题,还要考虑其它方案。

  1. 布局优化,由于布局文件的加载是一个 IO 的过程,同时还用到的反射,所以列表布局的加载是有可能导致滑动的卡顿。可以通过减少布局层级、避免过度绘制,使用异步 inflate 方式或者 X2C 框架。

  2. 图片相关的优化,过高的内存占用可能会导致GC频繁、内存抖动,如果发生的是阻塞式GC,主线程被影响的时间就会更长,因此在列表页一定要避免过大的图片尺寸,同时需要注意避免加载出的图片宽高大于控件的宽高。为了让列表页的滑动更加流畅,还可以监听列表页的事件,在滑动的过程中取消图片的加载以及显示,只有用户停止的时候才来加载需要显示的图片。

  3. 线程优化,错误的异步方式很可能会导致卡顿加剧,对于 Android App 来说,它所能创建出的线程数也不是无限多的,而且即使是创建出了很多的线程,对于Java 这种抢占式的调度模型,只会抢走原本属于主线程的 CPU 时间片,导致主线程执行的更慢,因此在列表页中要做耗时操作的话,可以考虑使用线程池,同时将线程池中的线程优先级设置成后台优先级,这样就不会出现原本属于主线程的 CPU 时间片被异步任务抢走了。

  4. TextView 的优化,对于复杂文本原生的 TextView 也会导致卡顿,在 TextView 内部用 BoringLayout 单行显示、StaticLayout 多行显示、DynamicLayout 用于可编辑的文本展示。可以使用 StaticLayout 代替 DynamicLayout,性能更优,此外还可以通过异步的方式创建 StaticLayout。

  5. 其它方面的优化,对于优化是无止境的,最好是根据 SysTrace 来跟踪具体的卡顿原因,来做针对性的优化。这里提一点我们开发中容易忽略的地方——字符串拼接,注意不要使用“+”这种字符串相加的拼接操作,而是应该使用 StringBuilder 通过 append() 方法拼接,尤其是针对字符串频繁操作的情况。

2.存储优化

通常对存储的优化就是将 IO 操作放到异步任务当中,这个思路实际上就是将耗时操作放到异步任务中避免卡顿主线程,其实只是一个粗粒度的优化而已。

SharedPreferences 相关优化

在实际项目中针对数据存储会用到SharedPreferences,它是 Android 中的一种非常简单易用的轻量级存储方式,可以用来存储一些简单的数据类型。但是它也有一些缺点。

  • 首先它在创建的时候它会将整个文件全部都加载到内存中,如果你的文件比较大那么加载一定会很耗时,虽然它的加载是在异步线程中做的,但是取值的方法会等待这个异步线程执行完成,这也就出现了UI线程等待异步线程的情况。

  • 第二个缺点是全量写入,就是不管你是使用 commit() 还是 apply() 方法去改动其中一个条目的数据,它会把全部内容都写入到文件中,即便是你多次写入同一个文件它也不会将多次修改合并成一次,每次写入都是全量写入,性能较差。

  • 第三个缺点就是卡顿,Android其实提供了对SharedPreferences 的一种异步的 apply() 机制,但是这种机制可能会在程序崩溃或者其它一些异常情况的时候导致数据丢失,所以当应用收到系统广播或者被调用到 onPause() 等一些时机系统会强制将所有 SharedPreferences 对象保存在硬盘中,如果一直没有保存完成那么主线程就会一直被阻塞,这样就很容易会造成卡顿更甚者ANR。对 SharedPreferences 来说它的设计初衷只是存储少量的数据,所以实际使用时不要用它来存储大量数据,并且它也不支持跨进程通信。

可以考虑使用 MMKV 替代 SharedPreferences,MMKV 使用了 mmap 和文件锁保证数据完整,支持增量写入,使用了 Protocol Buffer,还支持从 SharedPreferences 中迁移数据。

日志存储优化

对于日志存储的要求是不能影响 App 的性能,还能做到日志不丢失、而且要安全。

常规的实现方式是,每产生一个日志,写一遍到磁盘中,这种方案每次操作都去写一遍,如果操作非常频繁,肯定会因为频繁的 IO 操作影响程序性能,导致程序卡顿,但是这种方案的优点就是不会出现丢日志的情况。

可以考虑开辟一个内存 buffer,先将日志保存在 buffer 中,然后再写入文件。这种方案可以有效减少 IO 操作,保证程序的流畅性。但是这种方案存在一个致命的缺陷,就是内存中的日志不稳定,很可能会导致丢日志。

最优的解决方案是使用 mmap,mmap是一种内存映射文件的方法,即将一个文件或者其它对象映射到进程的地址空间,实现文件磁盘地址和进程虚拟地址空间中一段虚拟地址的一一对映关系。实现这样的映射关系后,进程就可以采用指针的方式读写操作这一段内存,而系统会自动回写脏页面到对应的文件磁盘上,即完成了对文件的操作而不必再调用 read , write 等系统调用函数。相反,内核空间对这段区域的修改也直接反映用户空间,从而可以实现不同进程间的文件共享。微信的 XLog 和美团的 Logan 就是基于 mmap 方式实现的。

其它优化

常用数据的缓存,避免多次读取,对于项目中比较大的数据可以在读取之后直接缓存起来,避免多次读取造成的浪费。

合理选择缓冲区 Buffer 大小,对于文件流的使用要合理的选择缓存区 buffer 的大小,一般设置 4-8KB 之间即可,buffer 太小会导致 IO 操作次数增加,太大会导致申请时间变长。

3.WebView 异常监控

WebView 是一个很重要的控件,因为它承载了 H5 页面的展示功能,但是在 Android 中WebView 出现的问题也是非常多的,尤其是性能和适配方面。对于用户来说,使用 WebView加载 H5 页面,出现的最严重的情况就是页面打开后是一个空白页,所以这里要做的就是监控用户的白屏率。解决的思路是监控屏幕是否白屏,出现白屏则可以断定是 WebView 出现了问题。

课程收获

这一章针对平时开发中常用的列表控件(ListView 和 RecyclerView)、常用的存储工具 SharedPreferences 和 H5 加载控件 WebView 的优化。给出了常规解决方案和更优的解决方案,通过对比,更能加深对优化思路的理解和应用。

点击查看更多内容
2人点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
移动开发工程师
手记
粉丝
12
获赞与收藏
17

关注作者,订阅最新文章

阅读免费教程

感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消