Facebook开源项目:我们为什么要用Fresco框架?

简介: (Facebook开源项目)Fresco:一个新的Android图像处理类库    在Facebook的Android客户端上快速高效的显示图片是非常重要的。然而多年来,我们遇到了很多如何高效存储图片的问题。

(Facebook开源项目)Fresco:一个新的Android图像处理类库 

  在Facebook的Android客户端上快速高效的显示图片是非常重要的。然而多年来,我们遇到了很多如何高效存储图片的问题。图片太大,而设备太小。一个像素点就占据了4个字节数据(分别代表R G B和alpha)。如果在一个480*800尺寸的手机屏幕上,一张单独的全屏图片就会占据1.5MB的内存空间。通常手机的内存都非常小,而这些内存被多种多样的app划分占用。在一些设备上,Facebook app虽然只有16MB,但是仅仅一个图片就占用了1/10的空间。

  当你的app用完你的内存时会发生什么呢?会崩溃。我们着手通过创建一个类(Fresco)来解决这个问题,它会管理好图片和内存。崩溃走开!

内存区域

  为了了解Facebook所做的工作,我们必须理解应用于Android中的各种堆内存。

  Java heap是严格的限制每个应用的主题,由设备制作商设置的。所有由Java语言的new操作符创建的对象都会来到这里。这里是一个相对安全的内存区域。内存总会被回收的,所以当app已经用完内存时,系统将会自动回收它。

  不幸的是,内存回收阶段出现了问题。为了回收内存,Android必须停止app的运行,然后运行垃圾回收器。这是你正在使用中的app出现停止或变慢的原因之一。这令人沮丧,或许用户正尝试滚动或者按一个button,结果app没有响应,只有莫名的等待。

  相反,native heap是被C++ new操作符使用的一个堆。在这里有大量的可用内存。app被限制在设备的可用物理内存中,这里不存在垃圾回收,app也不会变慢。然而,我们需要在C++程序中释放内存空间,否则他们会内存溢出,app最终会崩溃。

  Android中海油另外一块内存区域,叫ashmem。这很像是本地heap,但是需要额外的系统调用。Android能够“unpin”内存而不是释放他们。这是一种懒释放,只有在系统真正需要更多内存空间的时候才会释放。当Android再次指向(pin)内存时,旧的数据还会在那里,只要没有被释放的话。

可清除的位图

  ashmem不是直接的访问java应用,因为存在一些异常,图像就是其中一个。当你创建一个解码的(未压缩的)图像时,比如一个bitmap,Android API会允许你指定这个图像为“可清除的”: 

BitmapFactory.Options = new BitmapFactory.Options();
options.inPurgeable = true;
Bitmap bitmap = BitmapFactory.decodeByteArray(jpeg, 0, jpeg.length, options);

 这些可清除图像存在于ashmem中,然而,垃圾回收器并不会自动的回收他们。当绘制系统渲染图像时,Android的系统库会标记这块内存,当渲染完毕后,便不再标记它。没有被标记的内存随时都能被系统回收。如果一个没有被标记的图像需要重新绘制,系统会重新解码图像。

  这听起来像是一个完美的解决方案,但问题是快速解码图像发生在UI线程。解码是一个CPU密集型操作,执行时UI会变慢。为此,Google反对使用特征,他们推荐使用一种不同的flag:inBitmap。然而这种flag在Android3.0才出现。直到现在,这种flag也不常用,除非app里面所有图像的尺寸相同,这完全不符合Facebook的情况。这种限制直到Android4.4才移除。然而我们需要一个能让所有Facebook用户满意的方案,包括使用Android2.3的那些用户。

拥有蛋糕并吃掉它

  我们发现一种能够兼顾快速UI和快速内存的解决方案。如果我们提前标记内存,在非UI线程,并且保证不被除掉标记,然后我们能够保证图片存在于ashmem中而不会导致UI变慢。运气好的话,NDK中有一个函数能准确的做到,叫“AndroidBitmap_lockPixels”。这个函数会在“unlockPixels”调用之后被调用,再次去掉内存标记。

  当我们意识到我们不必那样做时,我们取得了突破。如果我们没有匹配“unlockPixels”而调用“lockPixels”,我们创建了一种安全存在于Java heap之外的图像,还不会拖慢UI线程。几行C++代码即可办到的事情。

用Java写代码,但用C++思考

  正如我们从蜘蛛侠上所学到的,“巨大的力量来源于重大的责任”。标记的可清除图像既不是垃圾回收也不是ashmem中的内置清除工具来防止内存泄漏。我们相信我们自己。

  在C++中,通常的办法是创建漂亮的指针类来实现引用计数,这些利用C++中的一些工具,如copy constructors, assignment operators, and deterministic destructors。这些动态特性不存在于Java中,在Java中,垃圾回收器会处理一切。所以我们必须在Java中找到实现C++风格的方法。

  我们利用两个类来实现之,一个是SharedReference,它有两个方法,addReference和deleteReference。每当他们获取底层对象或超出作用域时调用者必须调用他们。一旦引用计数器归零时,资源清理(如Bitmap.recycle)发生。

  然而显而易见的是,对于Java开发者来说,调用这些方法及其容易出错。Java被选为一个避免出现此种情况的语言。所以在SharedReference的顶部,我们建立了CloseableReference。它不但实现了Java Closeable接口,同时也实现了Cloneable。构造器和clone()方法调用addReference(),而且close()方法调用deleteReference。所以Java开发者仅需要遵循两个简单的规则。

  1.分配一个CloseableReference给一个新对象时,调用.clone()方法。

  2.超出作用域之前,调用.close()方法,通常在一个finally块中。

  这些规则在防止内存泄漏方面是非常有效的,同时让我们享受到本地内存管理的乐趣,比如在Facebook for Android 或 Messenger for Android这样的比较大的Java应用中。

它不只是加载器--它是管道

当在移动设备上面显示一个图像时,会有很多步骤。技术分享

  有几个很棒的开源库会进行这些步骤,如Picasso,Universal Image Loader,和Volley等等,不一而足。这些都对Android发展做出了重要贡献。我们相信我们的开源库在几个重要的方面走的更远。

  考虑到这些步骤作为一个管道而不是一个加载器是有区别的,每一步都应该尽量独立于其它部分,输入一些参数同时输出结果。它应尽可能并行的进行一些操作。而其他部分则串行。一些执行仅在特定条件下。他们执行的一些线程具有特殊的需求。此外,如果我们看重创新的图像,整个图片会变得更复杂。很多人是在非常慢的网络连接中使用Facebook的,我们想让这些用户能够快速的看到他们的图片,甚至是在图片真正加载完毕之前。

别担心,爱上流吧

  传统上,Java中的异步代码是通过像Future这样的机制被执行的。代码上传后在另外一个线程执行,像一个Future对象能被检测到结果是否准备好。然而,假设仅有一个结果,当处理先进的图像时,我们想,会出现一个连续结果的整体序列。

  我们的解决方案是Future的一个大概版本,叫做DataSource。他提供一个订阅方法,调用者必须传递一个数据订阅者和一个执行器。数据订阅者收到来自中间的或最终结果的DataSource的通知,而且提供一个简单方法来区分他们。因为我们经常处理一些需要明确调用close的对象,DataSource本身也是一个Closeable。

  在幕后,上面方框内的每一个都被实现了,并且使用了一个新的框架,叫做Producer/Consumer。这里我们画出了来自ReactiveX框架的灵感。我们的系统有与RxJava相同的接口,而且更适合移动设备和支持Closeable的嵌入式。

  接口很简单,Producer有一个独立的方法,是produceResults,它负责得到一个Consumer对象。相反,Consumer有一个onNewResult方法。

  我们使用这样的一个系统来把生产者链接在一起。假设我们有一个producer,它的工作是将类型I转换成类型O,那么代码就是如下所示。

public class OutputProducer<I, O> implements Producer<O> {

  private final Producer<I> mInputProducer;

  public OutputProducer(Producer<I> inputProducer) {
    this.mInputProducer = inputProducer;
  }

  public void produceResults(Consumer<O> outputConsumer, ProducerContext context) {
    Consumer<I> inputConsumer = new InputConsumer(outputConsumer);
    mInputProducer.produceResults(inputConsumer, context);
  }

  private static class InputConsumer implements Consumer<I> {
    private final Consumer<O> mOutputConsumer;

    public InputConsumer(Consumer<O> outputConsumer) {
      mOutputConsumer = outputConsumer;
    }

    public void onNewResult(I newResult, boolean isLast) {
      O output = doActualWork(newResult);
      mOutputConsumer.onNewResult(output, isLast);      
    }
  }
}

  这让我们将一系列复杂的步骤链接在一起,而且仍然保持他们逻辑上是独立的。

动画--从一个到多个

  Stickers,是存储在GIF中的WebP格式的动画,深受Facebook用户喜欢。但支持Stickers出现了新的挑战。一个动画并不是一幅图像而是一系列的图像的组合,其中每一张图片都要被解码,并存入内存,展示出来。存储大动画中的每个单独帧到内存是不合理的。

  我们创建了AnimatedDrawable,一个具有渲染能力的类,有两个后端--一个是GIF,另一个是WebP。AnimatedDrawable实现了标准Android的Animatable接口,所以调用者能够随时开始和结束动画。为了优化内存存储,在内存足够时我们缓存了所有帧到内存中,但是帧太多时,我们会匆忙的解码。这种行为是完全可以被调用者调节的。

  两个后端都是用C++代码实现,我们拷贝已编码数据和已解析的元数据,如宽和高。我们引用计数数据,这样可以在Java端让多个Drawable同时访问一个单独WebP图像。

 我如何爱你?让我制定方法

  当图片正在从网络被下载时,我们想显示一个占位符。如果下载失败,我们显示一个错误指示器。当图片到达时,我们做出一个快速的淡入效果动画。我们经常缩放图片,或者应用一个显示矩阵,使用硬件加速器来渲染它。而且我们不总是缩放图片--有用的焦点可能是其他地方。有时我们想显示一个圆角图片,或者圆形图片。所有这样的操作都应该是快速而平滑的。

  我们以前的实现都是使用Android的View对象--当时间到时为一个ImageView交换占位符,这会非常缓慢的。切换视图会强迫Android去执行整个layout,并不是像用户滚动这样的事情。一个更明智的方法是使用Android的Drawables,它能被快速交换。

  所以我们创建了Drawee。这是一个为显示图像的类似于MVC的框架。此模型被称作DraweeHierarchy。它是一个Drawables的实现层次架构,其中每个应用一个特定的函数--成像、分层、淡入、或缩放--对于底层图像来说。

  DraweeControllers连接图像管道--或对于任何图像加载器--照顾到后台图像控制。他们接收来自管道的事件,并决定如何处理它们。他们控制DraweeHierarchy的实际显示--如占位符、错误符号、或完成图片。

  DraweeViews仅有几个功能,但都是起决定作用的。他们监听Android视图不再显示的系统事件。当离开屏幕,DraweeView能告诉DraweeController关闭图像使用的资源。这会避免内存泄漏。另外,如果还没有出去的话,控制器将会告诉图像管道取消网络请求。因此,滚动长列表图像(如在Facebook中经常这样),并不会中止网络。

  有了这些工具,显示图像就不那么困难了。调用程序只需要实例化一个DraweeView,指定一个URI,然后随意设置一些其他的参数。其他所有工作都是自动化的。开发者不必担心管理图像内存或者更新流到图像。所有事情都是靠类库实现。

 

Fresco

  已经建立了灵活的工具集来显示和操作图像,我们想分享它给Android开发者社区。我们很高兴的宣布,从今日开始,这个项目开放源代码。

  fresco(湿壁画)是一种绘画技术,它已经流行了几个世纪。我们很荣幸许多伟大的艺术家都曾使用过这种形式,从意大利的文艺复兴大师如拉斐尔,到斯里兰卡的锡吉里亚古宫艺术家们。我们不自称达到那样的水平。我们希望Android app开发者们能够享受到使用类库带来的乐趣。

相关文章
|
机器学习/深度学习 数据采集 缓存
探索Facebook NLP框架Fairseq的强大功能
探索Facebook NLP框架Fairseq的强大功能
|
Web App开发 安全 前端开发
利用本地HTTPS模拟环境为FastAPI框架集成FaceBook社交三方登录
提起社交,就不得不说马克·扎克伯格(Mark Zuckerberg)一手创办的社交网络(FaceBook)。进入2020年,FaceBook的全球用户数已经突破了30亿,这是什么概念?全球人口大约70亿,除开中国14亿,还有56亿。国外市场是四倍于中国的潜在市场,扣除短期内有上网限制的人群,那也是两倍以上。站在全球视角看问题,说微信、支付宝偏安一隅,其实也并不为过。所以为你的平台集成全球最大用户基数的社交登录系统,显然可以为你带来更多的潜在用户,本次我们使用当红炸子鸡FastAPI框架来集成FaceBook的三方登录。
利用本地HTTPS模拟环境为FastAPI框架集成FaceBook社交三方登录
|
前端开发 JavaScript 容器
【React高级技术】合成事件以及 Test Utilities测试框架在Facebook内部进行测试
【React高级技术】合成事件以及 Test Utilities测试框架在Facebook内部进行测试
|
机器学习/深度学习 自动驾驶 安全
AWS 和 Facebook 合作推出 PyTorch 模型服务框架 TorchServe
  近日,Facebook 和 AWS 合作推出了 PyTorch 模型服务库 TorchServe,强强联手剑指 Google TensorFlow。   PyTorch 是当下最火的深度学习开源库之一,可以帮助开发者和研究人员更轻松地构建和训练模型。即便有了 PyTorch,在生产中部署和管理模型仍然是机器学习的一大难题,包括构建定制的预测 API,对其进行缩放以及保证其安全等问题。   简化模型部署过程的一种方法是使用模型服务器,即专门设计用于在生产中提供机器学习预测的现成的 Web 应用程序。模型服务器可轻松加载一个或多个模型,并自动创建由可伸缩 Web 服务器支持的预测 API。
169 0
|
机器学习/深度学习 运维 算法
华裔女博士生一作:Facebook提出用于超参数调整的自我监督学习框架
Facebook的研究人员近日提出了一种用于超参数调整的自我监督学习框架。这个新模型实现了准确预测的结果,估计超参数的速度快了6到20倍。
136 0
华裔女博士生一作:Facebook提出用于超参数调整的自我监督学习框架
|
人工智能 自然语言处理 算法
Facebook&哥大等推出实验性AI框架,音视频信息可自由转换文本!
近日,来自Facebook、哥伦比亚大学等高校的研究人员开发了一种可以从视频、对话历史、音频以及语音文本中生成高层次语义信息的框架——Vx2Text,该模型可以用来概括信息内容,并准确地回答相关问题。
137 0
Facebook&哥大等推出实验性AI框架,音视频信息可自由转换文本!
|
机器学习/深度学习 人工智能 文字识别
图神经网络版本的PyTorch来了,Facebook开源GTN框架,还可对图自动微分
近日,Facebook的AI研究院发表了一篇论文「DIFFERENTIABLE WEIGHTED FINITE-STATE TRANSDUCERS」,开源了用于图网络建模的GTN框架,操作类似于PyTorch这种传统的框架,也可以进行自动微分等操作,大大提高了对图模型建模的效率。
279 0
图神经网络版本的PyTorch来了,Facebook开源GTN框架,还可对图自动微分
|
JavaScript 网络协议 Dubbo
Facebook分布式框架—Thrift介绍。
Thrift介绍 Thrift是一个分布式RPC框架,用来进行可扩展且跨语言的服务的开发。它结合了功能强大的软件堆栈和代码生成引擎,以构建在 C++, Java, Python, PHP, Ruby, Erlang, Perl, Haskell, C#, Cocoa, JavaScript, Node.js, Smalltalk, and OCaml这些编程语言间无缝结合的、高效的服务。
350 0
Facebook分布式框架—Thrift介绍。
|
缓存 数据挖掘 Android开发
Airlock:Facebook 的移动端 A/B 测试框架
两年前,我们重写了我们移动端(iOS,Android)的应用,使用了原生的开发栈(native development stacks)代替我们以前定制开发的 Web 栈(custom web-stack)。这给了我们在关于项目在那里/怎样下载、缓存、释放等等方面一个更好的控制。它分别深入地和操作系统整合在一起,提供在底层调整修改所有系统的一整套工具。
160 0
Airlock:Facebook 的移动端 A/B 测试框架
|
人工智能 PyTorch 算法框架/工具
High&NewTech—AI界消息:2019年3月,贾扬清(Caffe框架作者)被曝从Facebook离职,入职Alibaba硅谷研究院
High&NewTech—AI界消息:2019年3月,贾扬清(Caffe框架作者)被曝从Facebook离职,入职Alibaba硅谷研究院