Google官方 详解 Android 性能优化【史诗巨著之内存篇】

前言:首先感谢这两天大家的投票,SuperVideo将不再拉票,可以去魅族应用市场下载最新2.0版本私下体验。有想帮投的,上一篇文章中,有链接。尤其感谢鸿洋基友和明云基友,两位都有维护自己的公众号和博客,没有关注鸿洋公众号和明云的open dev公众号的,文章下面有二维码,可关注。鸿洋,一直更新着公众号,不管刮风还是下雪,工作日每天7:30准时推送一篇干货。csdn博客早已突破千万:blog地址为:http://blog.csdn.net/lmj623565791,做Android的,不用我说,应该都知道。明云(张明云),知乎专栏上写了一系列的性能优化,都相当实用,open dev也会定期送上干货,不定期汇总推荐当下优秀文章)今天来看下

杨超凡授权本公众号的Google官方的性能优化一些优化建议:点击阅读原文,可查看

杨超凡的原文:http://blog.csdn.net/chivalrousman/article/details/51553114,话不多说,看下正文。

1使用AsyncTask加载Bitmap

当图片资源来自网络或者硬盘的时候,最好不要直接在主线程中加载它,例如IO资源或者数据库资源都会占用CPU,CPU 要做的事情过多,Android手机会造成卡顿得现象,好在Google 提供了解决办法–AsyncTask异步加载工具

接着我们在主线程中执行它即可

2

使用Lrucache缓存图片

1. 为什么要缓存图片?

对于如何高效的加载一张图片 ,我们似乎已经得心应手了,这里要泼一盆凉水给大家,因为我们的应用不仅只是加载一张图片这么简单,比如ListView, GridView or ViewPager,RecyclerView,需要立刻加载出大量的bitmap,滑动的过程不断加载bitmap,还要求不卡顿,内存够用,这似乎又是一件棘手的事情。

Google 又提供了一种解决思路:对于ListView,RecyclerView,有可见的item和不可见的item,回收不可见的item 内存,分配给可见的,这样内存得到了重复利用,避免重复创建对象,不断申请并分配新的内存空间,触发最低内存限制的危险。

所以我们要管理 这些 已经创建好的内存。

2. 使用内存缓存

  1. 为什么优先使用内存缓存? 答:相比硬盘缓存的读取速度,读取内存中的数据更快
  2. Google官方有什么建议? 答:Google推荐Lrucache类,底层使用用强引用封装的LinkedHashMap,来存储最近使用的对象,它自动会回收最近使用的对象当中,使用的最少的那一个对象的内存,这一点毋庸置疑值得推荐!

一种过时的做法,是用虚引用或者弱引用来标记bitmap,这种方法在Android 3.0以后已经不提倡了,因为类似JDK1.8那样,bitmap的内存是放在本地内存中的,它的回收是不确定的,有可能导致APP挂掉,切勿使用。

  1. Lrucache这么棒,我们该如何用? Lrucache就可以当作一种存储数据的结构,类似list,set,可以存储对象,获取对象,对应的有add() 和get()方法,它与数组一样,初始化的时候需要指定一个初始的大小。 那么Lrucache实例 初始的大小该如何确定? 在计算大小之前,我们需要明确几件事情:
    • 我们的Activity和Application还有多少可用内存?
    • 第一次加载的时候,需要为多少图片分配内存?可见的item数量,决定了图片的数量,图片的数量*每张图片的内存大小就是初始化需要分配的内存。
    • 用户当前手机的屏幕尺寸和屏幕密度(为什么要这俩参数?大屏幕手机 ,初始化的时候会加载更多的item,需要的内存更大)
    • 这张图片我们要怎样配置?图片的尺寸如何设置,颜色模式如何配置?根据不同的需求,比如是做用户头像,还是信息展示?都有各自的应用场景的要求。我们需要分类判断.
    • cache大小是由内存大小决定的,而不是 它存储数据的个数决定 这让我想起一个在项目开发中常见的bug,use a bitmap which has bean recycler

3. Lrucache使用实例

loadBitmap的过程很简洁:如果内存缓存中有这张bitmap,则直接刷新imageview,如果bitmap为空,则启动后台线程去加载bitmap,接着刷新imageview

异步线程 BitmapWorkerTask

4. 磁盘缓存

与内存缓存相结合的还有磁盘缓存,虽然磁盘读取速度较慢,但是持久存储的,不像内存缓存那样,在内存极限情况下仍然会被清理,比如后台正在执行数据加载,突然打进来一个电话,内存不足系统可能会进行垃圾回收。缓存就没有了

Google官方提供直接DiskLruCache 类

为什么要使用DiskLruCache 很明了:就是解决当内存缓存不可用的情形

内当需要频繁访问缓存的图片资源时,比如APP的画廊功能,可以考虑使用ContentProvider解决更为妥当。

5.处理运行时变更的缓存

当屏幕旋转,或者其他原因导致Activity restart,这个时候难道又让我们重新创建大量的图像资源? 回答是否定的,Google提供了一种解决方案:

通过在Activity中使用fragment,构造Fragment时,通过设置 setRetainInstance(true))来设置缓存, 话不多说,直接上代码:

3RecyclerView GridView ListView

下面可能是大家经常的做法:

看上去非常棒,对吗?但是我们如何才能做的更好?

如果你能发现主线程加载图片的问题,恭喜你,有一点点进步。但是重复提醒你使用AsyncTask,显然不是我写这一节的目的。

我们还需要警惕GridView 中的并发问题,因为iGridView 会回收子View的内存空间。

并发问题

像ListView和GridView,与RecyclerView使用AsyncTask的时候,不能忽视一个问题:Android系统为了高效分配内存,这些组件都会在上下滑动的时候回收子view的内存。在滑动的时候,并不能保证AsyncTask可以完成当前任务。此外,也不能保证异步任务可以按照顺序完成。

Google在 Multithreading for Performance 提供了建议:在AsyncTask中,用弱引用来存储ImageView,通过使用弱引用来被检查ImageView是否加载完成

好吧,理论上是这样,我们开始行动:

1. 自定义Drawable去存储异步任务重的弱引用,只有这样,才能保证task执行完毕之后,ImageView才会显示图片

2. 在执行BitmapWorkerTask之前,你需要创建 AsyncDrawable 并且绑定到ImageView上,通过如下代码:

3. 仅仅这些代码还是不够的,Google 还引入 cancelPotentialWork()来检查是否有其他的task任务在使用当前ImageView,如果有,就会取消当前任务,取消这个做法是不是很让人眼前一亮呢!

这是cancelPotentialWork()的实现

4. 我们还需要get方法获得相关的ImageView:

5. 最后就是更新task中的onPostExecute()检查任务是否取消:

至此,我们已经了解Google 对于并发加载的解决方案,只需要在getView()实现它们就可以啦!

在GridView解决并发

这样解决并发的办法,同理适用于ListView, Recycler 这也是热门加载框架ImageLoader,Volley 的加载原理。原理还是Google提出的!

这样就可以流畅的展示图片。

4View Holder设计模式

我们的代码可能会调用findViewById(),尤其是当滑动ListView,RecyclerView,GridView的时候,会使得app性能变得糟糕。甚至Adapter会返回一个已经被Android系统回收的View,可是你仍然需要初始化加载这个view并刷新它。

Google提出了使用 “View Holder” 设计模式

1. 首先创建 ViewHolder类,存储子View内部所有需要展示的布局

2. 接着填充ViewHolder并且存储到布局中

‘view holder’ 设计模式同样也适用于ListView,RecyclerView,GridView

原文发布于微信公众号 - 何俊林(DriodDeveloper)

原文发表时间:2016-11-03

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏程序员互动联盟

【专业技术第十讲】嵌入式系统的中断流程剖析

存在问题: 搞嵌入式特别是底层,常常提到中断,中断时干什么的呢? 解决方案: 做嵌入式肯定要了解中断。本文根据实例详细介绍中断过程,包括软件和硬件方面。 示例:...

4466
来自专栏FreeBuf

ADB配置提权漏洞(CVE-2017-13212)原理与利用分析

0x01 背景 adb由于拥有shell权限,因此仅在授权PC端后才可使用shell权限,而通过该漏洞,可以实现在移动端获取shell权限,以致于可随意删除应用...

4379
来自专栏游戏开发那些事

【Unity游戏开发】AssetBundle杂记--AssetBundle的二三事

  马三在公司大部分时间做的都是游戏业务逻辑和编辑器工具等相关工作,因此对Unity AssetBundle这块的知识点并不是很熟悉,自己也是有打算想了解并熟悉...

3422
来自专栏非著名程序员

Android将应用程序的崩溃信息如何保存到本地文件,并上传至服务器

导语:最近实在是太忙了,没有怎么更新公众号,也没有怎么认真去写一些内容,在这里先给关注我的朋友说一声抱歉,可能在接下来的一段时间,还是很忙,但是我会争取抽空多分...

2409
来自专栏携程技术中心

干货 | Spring探秘,妙用BeanPostProcessor

作者简介 杨宗良,携程深圳机票研发部资深开发工程师,现参与国际机票Online项目的维护及JAVA重构工作。 最近,在给项目组使用Spring搭建Java项目...

4346
来自专栏逸鹏说道

bootstrap + requireJS+ director+ knockout + web API = 一个时髦的单页程序

bootstrap + requireJS+ director+ knockout + web API = 一个时髦的单页程序 也许单页程序(Single Pa...

3685
来自专栏求索之路

从零开始仿写一个抖音App——app架构更新与网络层定制

讨论1:zsh 对 bash 的支持并不是完全的,如果运行纯 bash 有时候会出问题建议不要在服务器上用。

4081
来自专栏Flutter入门到实战

Android M Launcher3主流程源码浅析

关于Launcher是啥的问题我想这里就没必要再强调了。由于一些原因迫使最近开始需要研究一下Launcher3源码,为了不再像以前那么傻逼(研究Settings...

1962
来自专栏移动开发

仿googleMVP设计模式示例demo,结合rxjava,retrofit2.0

这是模仿Google官方mvp示例的小demo,结合Retrofit2.0,rxjava加上自己的一点理解所写. 感谢干货免费开放的API.

1423
来自专栏顶级程序员

MVC, MVP, MVVM比较以及区别

作者:JustRun 原文:cnblogs.com/JustRun1983/p/3679827.html MVC, MVP和MVVM都是用来解决界面呈现和逻辑...

89710

扫码关注云+社区

领取腾讯云代金券