对于Fresco大家在网上搜索看到的大多数是这篇翻译的文章,英语好的同学,可以直接看原版,因为我觉得翻译的不是很好(

http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2015/0402/2683.html  ),自己根据自己的理解补充了一下,如果有错误,希望大家指正,谢谢。

首先我们来了解一下Android在各个版本上处理Bitmap的不同,在Android 3.0以后,android的bitmap的数据也存储在java heap中,所以你不用太关心内存,内存会由java虚拟机回收。Android 3.0以前,由于google觉得虚拟机性能还不够,所以bitmap的内存数据是放在Ashmen里的,需要开发者显示调用Bitmap.recycle()来回收内存(其实不调用也没事,系统也能给你处理好,比如进程死了)。

由于Bitmap是内存大户,所以Google是意识到这个问题的,为了减低内存的分配,减少GC,Google在Android3.0 以后也提供了一个特别有用的特性BitmapFactory.Options.inBitamp,这个特性呢,能让图片在同一块内存区域上解码而达到重复利用这块内存,Android 4.4之前呢要求两张图片的大小必须一样,4.4 之后呢只要保证内存比需要解码的图片大就可以了。所以我们的应用只在4.4上跑,是可以做到高效的内存管理的。

那么,Facebook为什么又要搞什么Fresco呢?很明显,他们为了寻找一个通用的解决方案,一个能在Android 各个版本中都通用的方案。

他们又有了什么发现呢?他们的发现就是NDK里提供的一个函数AndroidBitmap_lockPixel,本来Google是推荐调用AndroidBitmap_lockPixels后要调用AndroidBitmap_unlockPixels,这两个函数应该是成对出现的,但Facebook的工程师们发现其实不调用AndroidBitmap_unlockPixels也没事,对,就这么简单。

看一下fresco/imagepipeline/src/jni/Bitmaps.c,pinBitmap这个函数,并没有unlockPixels。

/**
 * Pins bitmap's pixels.
 *
 * <p> Throws RuntimeException ifunable to pin.
 */
static void Bitmaps_pinBitmap(
    JNIEnv* env,
    jclass clazz,
    jobject bitmap) {
  UNUSED(clazz);
  int rc =AndroidBitmap_lockPixels(env, bitmap,0);
  if (rc !=ANDROID_BITMAP_RESULT_SUCCESS) {
    safe_throw_exception(env, "Failed to pin Bitmap");
  }
}

在来看一下AndroidBitmap_lockPixels的代码(/frameworks/base/native/graphics/jni/bitmap.cpp),在往里面的代码,大家可以继续跟了,其实我也搞不懂,反正就这样把内存给hold住了,大体上如果调用AndroidPixelRef() 的话是在Java heap上分配内存,调用SkMallocPixelRef()的话是在Native上分配内存。

  int AndroidBitmap_lockPixels(JNIEnv* env, jobject jbitmap, void** addrPtr) {if (NULL == env || NULL == jbitmap) {return ANDROID_BITMAP_RESULT_BAD_PARAMETER;}SkBitmap* bm = GraphicsJNI::getNativeBitmap(env, jbitmap);if (NULL == bm) {return ANDROID_BITMAP_RESULT_JNI_EXCEPTION;}bm->lockPixels();void* addr = bm->getPixels();if (NULL == addr) {bm->unlockPixels();return ANDROID_BITMAP_RESULT_ALLOCATION_FAILED;}if (addrPtr) {*addrPtr = addr;}return ANDROID_BITMAP_RESULT_SUCCESS;
}

Fresco就是基于这个原理去开发的,其实也就是回归到Android 2.3的处理方式上来,想来也简单,要兼容老的版本,就只能拿老版本的方案来解决问题了。

链接的文章里有一句话,“用C++的思想写Java代码”,大家都知道C++是要自己管理内存的,那我们写Java的时候也有这个意识,可能写出来的代码会更高。前面讲到不调用AndroidBitmap_unlockPixels也没事,真的没事吗?等着系统帮你回收内存?Facebook的工程师们当然不会这么水了,被hold住的Bitmap不用了还是需要调用一下Bitmap.recycle()的,那么问题就来了,什么时候调用呢?

为了解决这个问题,Fresco里有一个SharedReference这样的类,类似于C++的智能指针了,其实就是基于引用计数的,有使用到呢就加一,不用了呢就减一,引用计数为零了呢就调用Bitmap.recycle()。可是对于Java程序员去做这样的事情是比较别扭,所以又来了一个CloseableReference类继承Cloneable和Closeable这两个接口,通过clone和close这样Java化的方式来管理SharedReference,比如clone的时候就addReference(),close的时候就deleteReference(),这样我们用起来就舒服多了。

前面讲了一些原理,现在来看看上层是怎么使用Fresco的,Fresco提供了DraweeView这个View。我们在上层可以直接使用。

先来看看DraweeView这个类的继承关系,目前DraweeView是继承与ImageView的,但注释里写了,未来可能会改为继承View,所以在使用的时候尽可能不要使用到ImageView的方法。

我们在来看看DraweeView的主要代码,里面有一个重要的变量DraweeHolder,DraweeHolder里又包含DraweeHierarchy和DraweeController这两个接口变量。

通过上面的分析,很容易发现DraweeView,DraweeHierarchy,DraweeController三者的关系就是典型的MVC结构了。

这三者之间的关系是,DraweeView把事件派发给DraweeController,由DraweeController来决定DraweeHierarchy中存储的那种图片来显示(就是getTopLevelDrawable() 返回的Drawable)。

至于为什么要使用DraweeHolder,注释说了,这个就是为了解耦了,如果你不想使用DraweeView,通过DraweeHolder还是很方便使用另外两个组件的。

比如我们要使用自定义的View,我们就要处理好下面这几个函数,这样才能保证引用计数的正确性,否则可能就会引起内存泄露。其实就是要在View移除屏幕或进入屏幕去维护好引用计数了。

@Override
protected void onAttachedToWindow() {super.onAttachedToWindow();mDraweeHolder.onAttach();
}@Override
protected void onDetachedFromWindow() {super.onDetachedFromWindow();mDraweeHolder.onDetach();
}@Override
public void onStartTemporaryDetach() {super.onStartTemporaryDetach();mDraweeHolder.onDetach();
}@Override
public void onFinishTemporaryDetach() {super.onFinishTemporaryDetach();mDraweeHolder.onAttach();
}@Override
public boolean onTouchEvent(MotionEvent event) {if (mDraweeHolder.onTouchEvent(event)) {return true;}return super.onTouchEvent(event);
}

当然Fresco还有很多功能,大家可以慢慢去研究,比如开头帖子说的,“不仅仅是加载程序,它是一个管道”,有时间我在把管道的处理流程给画出来。

总的来说,Fresco的性能还是不错的,而且还兼容Android各个版本,但是也就是这个兼容性给Fresco带来了复杂性,如果我们的项目只考虑在4.4上运行,那还是有可能做到更简单和高效的。

Fresco原理分析相关推荐

  1. 可能是最详细的Android图片压缩原理分析(一)—— Android图片压缩必备基础知识

    本篇文章已授权微信公众号guolin_blog(郭霖)独家发布 稀土掘金链接 前言: 最近在研究图片压缩原理,看了大量资料,从上层尺寸压缩.质量压缩原理到下层的哈夫曼压缩,走成华大道,然后去二仙桥,全 ...

  2. java signature 性能_Java常见bean mapper的性能及原理分析

    背景 在分层的代码架构中,层与层之间的对象避免不了要做很多转换.赋值等操作,这些操作重复且繁琐,于是乎催生出很多工具来优雅,高效地完成这个操作,有BeanUtils.BeanCopier.Dozer. ...

  3. Select函数实现原理分析

    转载自 http://blog.chinaunix.net/uid-20643761-id-1594860.html select需要驱动程序的支持,驱动程序实现fops内的poll函数.select ...

  4. spring ioc原理分析

    spring ioc原理分析 spring ioc 的概念 简单工厂方法 spirng ioc实现原理 spring ioc的概念 ioc: 控制反转 将对象的创建由spring管理.比如,我们以前用 ...

  5. 一次 SQL 查询优化原理分析(900W+ 数据,从 17s 到 300ms)

    点击上方"方志朋",选择"设为星标" 回复"666"获取新整理的面试文章 来源:Muscleape jianshu.com/p/0768eb ...

  6. 原理分析_变色近视眼镜原理分析

    随着眼镜的发展,眼镜的外型变得越来越好看,并且眼镜的颜色也变得多姿多彩,让佩戴眼镜的你变得越来越时尚.变色近视眼镜就是由此产生的新型眼镜.变色镜可以随着阳光的强弱变换不同的色彩. 变色眼镜的原理分析 ...

  7. jieba分词_从语言模型原理分析如何jieba更细粒度的分词

    jieba分词是作中文分词常用的一种工具,之前也记录过源码及原理学习.但有的时候发现分词的结果并不是自己最想要的.比如分词"重庆邮电大学",使用精确模式+HMM分词结果是[&quo ...

  8. EJB调用原理分析 (飞茂EJB)

    EJB调用原理分析 EJB调用原理分析 作者:robbin (MSN:robbin_fan AT hotmail DOT com) 版权声明:本文严禁转载,如有转载请求,请和作者联系 一个远程对象至少 ...

  9. 深入掌握Java技术 EJB调用原理分析

      深入掌握Java技术 EJB调用原理分析     一个远程对象至少要包括4个class文件:远程对象:远程对象的接口:实现远程接口的对象的stub:对象的skeleton这4个class文件. 在 ...

最新文章

  1. sdut 2805(最小生成树)
  2. linux 中root用户与普通用户的切换
  3. 单例-初始化动作只执行一次
  4. MFC中OnCtlColor的用法(改变控件颜色)
  5. kali linux wifi监听模式,无线渗透教程1:监听无线网络
  6. [密码学基础][每个信息安全博士生应该知道的52件事][Bristol52]45.描述一些对抗RSA侧信道攻击的防御方法
  7. rabbitmq rpc
  8. 用 js判断 一个数是否是素数(质数)_小学五年级下册数学公式打印版,孩子寒假预习用的上!...
  9. SQL Server整合–在单个SQL Server实例上托管多个数据库
  10. CCF NOI1028 判断互质
  11. Spring pom配置详解(转)
  12. 设置全屏代码android,Android实现全屏显示的方法
  13. Spring实战4:面向切面编程
  14. keil4 破解心得
  15. 飞入菜花无处寻的上一句是什么,飞入菜花无处寻是什么意思
  16. 缺失值处理,你真的会了吗?
  17. 安装程序无法打开注册表项 UNKNOWN\Components\…解决办法
  18. java 动态线程池_线程池的参数动态调整
  19. 移动MAS短信技术错误代码和CMPP3.0错误代码表
  20. word文档保存的时候,就会出现“文件许可权错误,word 无法完成保存文件”的提示

热门文章

  1. 怎样用ps去掉gif图片的背景颜色
  2. 云计算hcie贴吧_云计算HCIE题库
  3. android高级应用课程大纲
  4. Redis面试经典问题
  5. 数据可视化,散点图代码和分析
  6. 《成本会计》---标准成本法
  7. Android的三种动画
  8. 二进制安装K8S(四):部署flannel网络
  9. 美术基础怎么自学?素描应该如何入门
  10. 苹果Facebook介入:移动AR市场2021年或达600亿美元