当前位置: 首页 > 工具软件 > Fresco > 使用案例 >

android fresco 流程,Android Fresco 笔记

孔永年
2023-12-01

一、简介使用

1.1 介绍

Fresco对于图片的展示支持多种情况:backgroud image(背景图)、placeholder image(占位图)、actual image(加载的图片)、progress bar image(进度条)、retry image(重新加载的图片)、failure image(失败图片)与overlay image(叠加图)。

1.2优点

1、支持webp格式的图片Fresco是通过jni来实现支持WebP格式图片。

2、5.0以下系统:使用”ashmem”(匿名共享内存)区域存储Bitmap缓存,这样Bitmap对象的创建、释放将永远不会触发GC,关于”ashmem”存储区域,它是一个不在Java堆区的一片存储内存空间,它的管理由Linux内核驱动管理,不必深究,只要知道这块存储区域是别于堆内存之外的一块空间就行了,且这块空间是可以多进程共享的,GC的活动不会影响到它。5.0以上系统,由于内存管理的优化,所以对于5.0以上的系统Fresco将Bitmap缓存直接放到了堆内存中。

3、使用了三级缓存:Bitmap缓存+未解码图片缓存+硬盘缓存。

其中前两个就是内存缓存,Bitmap缓存根据系统版本不同放在了不同内存区域中,而未解码图片的缓存只在堆内存中

1.3使用

Application中初始化

public class MyApplication extends Application {

@Override

public void onCreate() {

super.onCreate();

Fresco.initialize(this);

}

}

xml

android:id="@+id/my_image_view"

android:layout_width="130dp"

android:layout_height="130dp"

fresco:placeholderImage="@drawable/my_drawable"

/>

具体

Uri uri = Uri.parse("https://raw.githubusercontent.com/facebook/fresco/gh-pages/static/fresco-logo.png");

SimpleDraweeView draweeView = (SimpleDraweeView) findViewById(R.id.my_image_view);

draweeView.setImageURI(uri);

二、原理分析

DraweeView采用的 是典型的mvc模式,

DraweeHierarchy 负责的是跟显示相关的,

DraweeController 负责的是后台相关的,

DraweeHolder主要是统筹两者的,Fresco的初始化方法执行后会初始化相关配置。

以SimpleDraweeView为例,它的controller是PipelineDraweeController,Fresco在初始化时初始化一个PipelineDraweeControllerBuilderSupplier来提供这个controller,DraweeHolder主要通过controller启动后台服务获取数据,setController方法执行后后调用controller的onAttach()方法,这个方法会提交请求并获得数据,后将数据返回给ui线程,

Fresco数据是封装成Supplier> 的形式返回,而真正将数据传递回来的是通过ImagePipeline,这个ImagePipeline会在PipelineDraweeControllerBuilderSupplier的构造器初始化创建一个,它会在Fresco初始化方法中进行初始化,SimpleDraweeView初始化方法中会调用PipelineDraweeControllerBuilderSupplier的get的方法将ImagePipeline返回,ImagePipeline有一个RequestListsner的成员变量,它管理这个一个监听列表,请求监听通过这个RequestListsner返回,而正确去获取数据的时候是启动一个后台服务进行数据获取的,整个数据获取有分多个阶段,Fresco通过producer划分这些阶段,比如缓存获取阶段、编码阶段、网络获取阶段,它会一个一个阶段去获取数据,如果producer1没有获取到数据就交给producer2进行获取数据以此类推下去,producer之间通过consumer返回数据,最终将数据返回到ui线程去。

2.1SimpleDraweeView的继承关系

Object (java.lang)

View (android.view)

ImageView (android.widget)

DraweeView (com.facebook.drawee.view) 根部DraweeView

GenericDraweeView (com.facebook.drawee.view) 通用的DraweeView

SimpleDraweeView (com.facebook.drawee.view) 最简单的DraweeView

2.2从 SimpleDraweeView.setImageURI(uri);

public void setImageURI(Uri uri, @Nullable Object callerContext) {

DraweeController controller =mControllerBuilder

.setCallerContext(callerContext)

.setUri(uri)

.setOldController(getController())

.build();

setController(controller);

}

可以从字面看出来 给当前view 设置一个controller,一个DraweeView对应一个DraweeController。而Controller是由build模式所创建,查看 mControllerBuilder 的赋值来源

private void init(Context context, @Nullable AttributeSet attrs) {

...

mControllerBuilder = sDraweecontrollerbuildersupplier.get();

...

}

这是在SimpleDraweeView的构造方法中会调用init()方法,继续查看sDraweecontrollerbuildersupplier是什么,

public static void initialize(Supplier extends AbstractDraweeControllerBuilder> draweeControllerBuilderSupplier) {

sDraweecontrollerbuildersupplier = draweeControllerBuilderSupplier;

}

initialize 是的调用位置, Fresco中的initializeDrawee()

private static void initializeDrawee(Context context, @Nullable DraweeConfig draweeConfig) {

...

sDraweeControllerBuilderSupplier =new PipelineDraweeControllerBuilderSupplier(context, draweeConfig);

SimpleDraweeView.initialize(sDraweeControllerBuilderSupplier);

...

}

public static void initialize(Context context, @Nullable ImagePipelineConfig imagePipelineConfig, @Nullable DraweeConfig draweeConfig) {

...

initializeDrawee(context, draweeConfig);

...

}

这是static方法,Fresco在Application中执行Fresco.initialize(context)里 initializeDrawee(context, draweeConfig);

初始化Drawee相关配置信息。所以可以先搞清楚

sDraweeControllerBuilderSupplier 是 PipelineDraweeControllerBuilderSupplier

mControllerBuilder 是 PipelineDraweeControllerBuilder

controller 是 PipelineDraweeController

2.3setController(controller)

public void setController(@Nullable DraweeController draweeController) {

mDraweeHolder.setController(draweeController);

super.setImageDrawable(mDraweeHolder.getTopLevelDrawable());

}

这里将控制器传进了holder里面。

mDraweeHolder.setController(draweeController)里最终调用了controller的onAttach()方法,

@Override

public void onAttach() {

......

mEventTracker.recordEvent(Event.ON_ATTACH_CONTROLLER);

Preconditions.checkNotNull(mSettableDraweeHierarchy);

mDeferredReleaser.cancelDeferredRelease(this);

mIsAttached = true;

if (!mIsRequestSubmitted) {

submitRequest();

}

}

最终执行到 submitRequest();

2.4submitRequest();

protected void submitRequest() {

mEventTracker.recordEvent(Event.ON_DATASOURCE_SUBMIT);

getControllerListener().onSubmit(mId, mCallerContext);

mSettableDraweeHierarchy.setProgress(0, true);

mIsRequestSubmitted = true;

mHasFetchFailed = false;

mDataSource = getDataSource();//获取数据

.......

final String id = mId;

final boolean wasImmediate = mDataSource.hasResult();

final DataSubscriber dataSubscriber =

new BaseDataSubscriber() {

@Override

public void onNewResultImpl(DataSource dataSource) {

// isFinished must be obtained before image, otherwise we might set intermediate result

// as final image.

boolean isFinished = dataSource.isFinished();

T image = dataSource.getResult();

if (image != null) {

onNewResultInternal(id, dataSource, image, isFinished, wasImmediate);

} else if (isFinished) {

onFailureInternal(id, dataSource, new NullPointerException(), /* isFinished */ true);

}

}

@Override

public void onFailureImpl(DataSource dataSource) {

onFailureInternal(id, dataSource, dataSource.getFailureCause(), /* isFinished */ true);

}

};

mDataSource.subscribe(dataSubscriber,mUiThreadImmediateExecutor);//这个将获取的数据回调到ui线程

}

这里可以看出来是 获取到数据,然后将数据提交到ui线程去。

2.5 如何获取数据getDataSource()

@Override

protected DataSource> getDataSource() {

.....

return mDataSourceSupplier.get();

}

这里出现了一个mDataSourceSupplier,mDataSourceSupplier只有在两个地方传递进去,一个是构造器,还有一个是initialize方法,而这个方法只有在PipelineDraweeControllerBuilder的obtainController()时调用到:

private void init(Supplier>> dataSourceSupplier) {

mDataSourceSupplier = dataSourceSupplier;

}

public void initialize( Supplier>> dataSourceSupplier, String id,

CacheKey cacheKey, Object callerContext, @Nullable ImmutableList customDrawableFactories,

@Nullable ImageOriginListener imageOriginListener) {

...

init(dataSourceSupplier);

...

}

initialize() 方法是哪里调用的呢?所有的方法都是在PipelineDraweeControllerBuilder的 调用父类方法Builder 里面处理的

protected PipelineDraweeController obtainController() {

try {

...

controller.initialize(obtainDataSourceSupplier(controller, controllerId),

controllerId,

getCacheKey(),

getCallerContext(),

mCustomDrawableFactories,

mImageOriginListener);

controller.initializePerformanceMonitoring(mImagePerfDataListener);

return controller;

} finally {

...

}

}

父类 AbstractDraweeControllerBuilder.java

protected AbstractDraweeController buildController() {

...

AbstractDraweeController controller = obtainController();

controller.setRetainImageOnFailure(getRetainImageOnFailure());

controller.setContentDescription(getContentDescription());

controller.setControllerViewportVisibilityListener(getControllerViewportVisibilityListener());

...

return controller;

}

@Override

public AbstractDraweeController build() {

...

return buildController();

}

mDataSourceSupplier 创建赋值的流程大概出来了

SimpleDraweeView.setImageURI() --> PipelineDraweeControllerBuilder.build() --> PipelineDraweeControllerBuilder.buildController() --> PipelineDraweeControllerBuilder.obtainController() --> PipelineDraweeController.initialize() --> 赋值完成

按照上述流程继续走下

mDataSourceSupplier 取值方法 obtainDataSourceSupplier(controller, controllerId)

protected Supplier> obtainDataSourceSupplier(

final DraweeController controller, final String controllerId) {

...

// final image supplier;

if (mImageRequest != null) {

supplier = getDataSourceSupplierForRequest(controller, controllerId, mImageRequest);

}

// increasing-quality supplier; highest-quality supplier goes first

if (supplier != null && mLowResImageRequest != null) {

List>> suppliers = new ArrayList<>(2);

suppliers.add(supplier);

suppliers.add(getDataSourceSupplierForRequest(controller, controllerId, mLowResImageRequest));

supplier = IncreasingQualityDataSourceSupplier.create(suppliers, false);

}

...

return supplier;

}

发现最终是走到getDataSourceSupplierForRequest方法。

protected Supplier> getDataSourceSupplierForRequest(

final DraweeController controller, String controllerId, REQUEST imageRequest) {

return getDataSourceSupplierForRequest(

controller, controllerId, imageRequest, CacheLevel.FULL_FETCH);

}

protected Supplier> getDataSourceSupplierForRequest(

final DraweeController controller,

final String controllerId,

final REQUEST imageRequest,

final CacheLevel cacheLevel) {

final Object callerContext = getCallerContext();

return new Supplier>() {

@Override

public DataSource get() {

// 这里就是 getDataSource() 时候真正调用的方法

return getDataSourceForRequest(

controller, controllerId, imageRequest, callerContext, cacheLevel);

}

@Override

public String toString() {

...

}

};

}

现在流程最终到getDataSourceForRequest()

2.5getDataSourceForRequest

现在流程为

onAttach() --> submitRequest() --> getDataSource() -->mDataSourceSupplier.get() --> getDataSourceForRequest()

// PipelineDraweeControllerBuilder.java

protected DataSource> getDataSourceForRequest(

DraweeController controller,

String controllerId,

ImageRequest imageRequest,

Object callerContext,

AbstractDraweeControllerBuilder.CacheLevel cacheLevel) {

return mImagePipeline.fetchDecodedImage(

imageRequest,

callerContext,

convertCacheLevelToRequestLevel(cacheLevel),

getRequestListener(controller));

}

可以看出来 最终把任务提交到mImagePipeline.fetchDecodedImage()去执行。

2.6ImagePipeline是什么

ImagePipeline负责完成加载图像,变成Android设备可呈现的形式所要做的每个事情。

pipelineSequence

大致流程如下:

1.检查内存缓存,如有,返回

2.后台线程开始后续工作

3.检查是否在未解码内存缓存中。如有,解码,变换,返回,然后缓存到内存缓存中。

4.检查是否在磁盘缓存中,如果有,变换,返回。缓存到未解码缓存和内存缓存中。

5.从网络或者本地加载。加载完成后,解码,变换,返回。存到各个缓存中。

至此流程基本结束

三、其他

3.1

Asynctask配合使用时候,注意阻塞问题,Android4.0以后Asynctask就改成(先进先出)谁先来谁先执行并且只能一个线程执行,这样就导致了所有Http请求都阻塞了。

3.2从缓存中读取文件

使用Fresco加载图片,预览保存图片不在下载而是直接从缓存中读取,

FileBinaryResource resource = (FileBinaryResource) Fresco.getImagePipelineFactory().getMainFileCache().getResource(new SimpleCacheKey(URI));

File file2 = resource.getFile();

说明:

1.Fresco不同版本略不同,老版本是getMainFileCache()。

2.得到文件路径是安装包路径,并且是.cnt未结尾。

/data/user/0/com.app.example/cache/image_cache/v2.ols100.1/67/MuA4Ubc_k3r3KFyVmpyoOw6RHU8.cnt

3.所以如果涉及到微信分享出去等,需要重新复制一个.jpg或png的文件。

 类似资料: