2.Fresco在RN页面中使用的问题,
通过看代码可以知道,RN页面销毁的时候,连带着Fresco的内存缓存都会被清空,
直接上代码图:
代码看到这里,似乎Fresco不用担心了,既然会清空Fresco的内存缓存,何愁会引起内存峰值过高,如果读者看到这里,也有这个想法,那就大错特错了。话不多说,直接上图。
Fresco相关源码的逻辑这篇文章就不分析了,主要讲思路,具体的源码分析后面我会用单独的篇幅去讲~
为什么我会对Fresco的动图缓存这么敏感,那还是Profiler的功劳,我在用Profiler查看内存中bitmap的分配的时候,发现有上百张的Loading图没有销毁(我们Loading图是动图,大概每帧的Bitmap对象在360K左右), 且打开的页面越多,Loading的bitmap就会越多。(这是因为我们每一个RN页面都会带一个Loading动画)
0.3M * 100 = 30M,不少了。。。,说实话有点恐怖
于是乎,干掉他们,这里用了反射,正常情况下不需要反射。直接拿ImagePipelineFactory中的对象来clear就好
public static void clearAnimationCache() {
if (frescoAnimationCache == null) {
//采用反射的方法,如果native、rn同时初始化Fresco,会造成Fresco内部存储动图的CountingMemoryCache不是Fresco.getImagePipelineFactory().getBitmapCountingMemoryCache()了
//暂时用反射的方法,拿到存储动图缓存的cache,并清空
try {
Class imagePipelineFactoryClz = Class.forName(“com.facebook.imagepipeline.core.ImagePipelineFactory”);
Field mAnimatedFactoryField = imagePipelineFactoryClz.getDeclaredField(“mAnimatedFactory”);
mAnimatedFactoryField.setAccessible(true);
AnimatedFactoryV2Impl animatedFactoryV2 = (AnimatedFactoryV2Impl) mAnimatedFactoryField.get(Fresco.getImagePipelineFactory());
Class animatedFactoryV2ImplClz = Class.forName(“com.facebook.fresco.animation.factory.AnimatedFactoryV2Impl”);
Field mBackingCacheField = animatedFactoryV2ImplClz.getDeclaredField(“mBackingCache”);
mBackingCacheField.setAccessible(true);
frescoAnimationCache = (CountingMemoryCache) mBackingCacheField.get(animatedFactoryV2);
} catch (Exception e) {
Log.e(“FrescoUtil”, e.getMessage(), e);
}
}
if (frescoAnimationCache != null) {
frescoAnimationCache.clear();
}
Fresco.getImagePipelineFactory().getBitmapCountingMemoryCache().clear();
Fresco.getImagePipelineFactory().getEncodedCountingMemoryCache().clear();
}
又一个兜底方案
为了防止峰值过高,我们还起了一个线程,定时的去监控实时的内存使用情况,如果内存紧急了,直接清空UIL/Fresco的内存缓存救急
private static Handler lowMemoryMonitorHandler;
private static final int MEMORY_MONITOR_INTERVAL = 1000 * 60;
/**
- 开启低内存监测,如果低内存了,作出相应的反应
*/
public static void startMonitorLowMemory() {
HandlerThread thread = new HandlerThread(“thread_monitor_low_memory”);
thread.start();
lowMemoryMonitorHandler = new Handler(thread.getLooper());
lowMemoryMonitorHandler.postDelayed(releaseMemoryCacheRunner, MEMORY_MONITOR_INTERVAL);
}
/**
- 低内存时清空Fresco、UIL的内存缓存
- 如果已用内存达到了总的 80%时,就清空缓存
*/
private static Runnable releaseMemoryCacheRunner = new Runnable() {
@Override
public void run() {
long alreadyUsedSize = Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory();
long maxSize = Runtime.getRuntime().maxMemory();
if (Double.compare(alreadyUsedSize, maxSize * 0.8) == 1) {
BitmapUtil.clearMemoryCaches();
}
lowMemoryMonitorHandler.postDelayed(releaseMemoryCacheRunner, MEMORY_MONITOR_INTERVAL);
}
};
五、特大图排查优化
我想大家都不会想到,在我们app的登录注册页,会有一个图片轮播控件,它轮播着五六张单张6M+的Bitmap。。。当然,特大图不仅限于此,还有其他地方会有相同情况,我们通过Profiler找出那些大的bitmap对象,然后预览之后确定是哪里在用的。
直接优化掉。最不济 8888 -> 565就少一半内存占用
怎么讲呢,,OOM这个东西,还没咋僵持呢,就没了。。
六、总结
深夜一时兴起想分享和记录一些什么,就随便写了这一篇博客,写的不详细,没有排版和良好的语言组织,单纯的就是想分享
总结一下吧,我们为了fix OOM所做的事情:
- 检查内存泄漏,包括常见的Context泄漏、单例泄漏、EditText的TextWatcher泄漏等等,找到并fix他们,最简单的例子,能传application的地方就不要硬传个activity过去
- 兜底方案:
- 在Activity onDestory的时候,遍历View树,清空backGround、Drawable、EditText的TextWatcher等
- 内存峰值的优化。内存泄漏会导致内存峰值,内存峰值是OOM的大锅,举个例子当可用内存不够分配一个Bitmap对象时,就会OOM,Android上大多数的内存峰值都是图片的加载带来的。现在许多的app中都有信息流的展现,可能会有许多的九宫格展示图片,且Bitmap对象本身就可以非常大。
-
优化UIL的使用
-
memoryCache选用,不是所有的图片加载都需要UIL去塞一份内存缓存的,比如闪屏图
-
ImageLoader.getInstance().displayImage()的时候,传进去的Option不要无脑ARGB_8888,讲道理来说,无脑RGB_565都是没啥问题的。。
-
调用displayImage的时候,最好传一个ImageSize作为targetSize,这个size可以是你的ImageView的尺寸,当View尺寸本身不确定的时候,可以传一个大概值,比如我们app中有好些个的头像标准尺寸,为了偷懒,直接传MaxAvatarSize就ok
-
Fresco的优化
-
RN中使用Fresco加载图片,在RN Activity销毁的时候,会将Fresco默认的memory cache清空,但是动图的缓存没有清。手动清一下。我们项目中每个RN页面都会带一个Loading动图,所以吃了大亏。。
- 持续的后台监控内存,起一个HandlerThread,一直在后台拿内存使用的状态,达到了危险警戒线就清空一把UIL、Fresco的memory cache,先让世界安静一下
- 需要对内存泄漏、OOM、Crash、ANR进行监控
一些其他的细节暂时想不起来了,凌晨四点脑子不清醒了
后续关于这里面涉及到的Fresco的部分源码分析、Profiler的最佳使用姿势(经过这一次的折腾,总结出来一句话,Profiler真香)、以及前段时间在做的App的启动速度优化等等等等等都会单独拎文章去分享,后续也会带来更多,涉及的内容包括但不限于:
- 主流框架的一些设计思想的分享
- 工作项目中遇到的麻烦和坑
- 工作中蹚坑的一些经验
- 好代码
- 坏代码
- 坏的设计
- 程序员从头发浓密到成为下雨天报警员的心路历程
- 。。。
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新
如果你觉得这些内容对你有帮助,可以添加V获取:vip204888 (备注Android)
结语
- 现在随着短视频,抖音,快手的流行NDK模块开发也显得越发重要,需要这块人才的企业也越来越多,随之学习这块的人也变多了,音视频的开发,往往是比较难的,而这个比较难的技术就是NDK里面的技术。
- 音视频/高清大图片/人工智能/直播/抖音等等这年与用户最紧密,与我们生活最相关的技术一直都在寻找最终的技术落地平台,以前是windows系统,而现在则是移动系统了,移动系统中又是以Android占比绝大部分为前提,所以AndroidNDK技术已经是我们必备技能了。
- 要学习好NDK,其中的关于C/C++,jni,Linux基础都是需要学习的,除此之外,音视频的编解码技术,流媒体协议,ffmpeg这些都是音视频开发必备技能,而且
- OpenCV/OpenGl/这些又是图像处理必备知识,下面这些我都是当年自己搜集的资料和做的一些图,因为当年我就感觉视频这块会是一个大的趋势。所以提前做了一些准备。现在拿出来分享给大家。
一个人可以走的很快,但一群人才能走的更远。如果你从事以下工作或对以下感兴趣,欢迎戳这里加入程序员的圈子,让我们一起学习成长!
AI人工智能、Android移动开发、AIGC大模型、C C#、Go语言、Java、Linux运维、云计算、MySQL、PMP、网络安全、Python爬虫、UE5、UI设计、Unity3D、Web前端开发、产品经理、车载开发、大数据、鸿蒙、计算机网络、嵌入式物联网、软件测试、数据结构与算法、音视频开发、Flutter、IOS开发、PHP开发、.NET、安卓逆向、云计算
Android移动开发、AIGC大模型、C C#、Go语言、Java、Linux运维、云计算、MySQL、PMP、网络安全、Python爬虫、UE5、UI设计、Unity3D、Web前端开发、产品经理、车载开发、大数据、鸿蒙、计算机网络、嵌入式物联网、软件测试、数据结构与算法、音视频开发、Flutter、IOS开发、PHP开发、.NET、安卓逆向、云计算**