一、简介使用
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的文件。