一、簡(jiǎn)介使用
1.1 介紹
Fresco對(duì)于圖片的展示支持多種情況:backgroud image(背景圖)、placeholder image(占位圖)、actual image(加載的圖片)、progress bar image(進(jìn)度條)、retry image(重新加載的圖片)、failure image(失敗圖片)與overlay image(疊加圖)。
主頁(yè):Fresco官方文檔
1.2優(yōu)點(diǎn)
1、支持webp格式的圖片F(xiàn)resco是通過(guò)jni來(lái)實(shí)現(xiàn)支持WebP格式圖片。
2、5.0以下系統(tǒng):使用”ashmem”(匿名共享內(nèi)存)區(qū)域存儲(chǔ)Bitmap緩存,這樣Bitmap對(duì)象的創(chuàng)建、釋放將永遠(yuǎn)不會(huì)觸發(fā)GC,關(guān)于”ashmem”存儲(chǔ)區(qū)域,它是一個(gè)不在Java堆區(qū)的一片存儲(chǔ)內(nèi)存空間,它的管理由Linux內(nèi)核驅(qū)動(dòng)管理,不必深究,只要知道這塊存儲(chǔ)區(qū)域是別于堆內(nèi)存之外的一塊空間就行了,且這塊空間是可以多進(jìn)程共享的,GC的活動(dòng)不會(huì)影響到它。5.0以上系統(tǒng),由于內(nèi)存管理的優(yōu)化,所以對(duì)于5.0以上的系統(tǒng)Fresco將Bitmap緩存直接放到了堆內(nèi)存中。
3、使用了三級(jí)緩存:Bitmap緩存+未解碼圖片緩存+硬盤(pán)緩存。
其中前兩個(gè)就是內(nèi)存緩存,Bitmap緩存根據(jù)系統(tǒng)版本不同放在了不同內(nèi)存區(qū)域中,而未解碼圖片的緩存只在堆內(nèi)存中
1.3使用
Application中初始化
public class MyApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
Fresco.initialize(this);
}
}
xml
<com.facebook.drawee.view.SimpleDraweeView
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 負(fù)責(zé)的是跟顯示相關(guān)的,
DraweeController 負(fù)責(zé)的是后臺(tái)相關(guān)的,
DraweeHolder主要是統(tǒng)籌兩者的,F(xiàn)resco的初始化方法執(zhí)行后會(huì)初始化相關(guān)配置。
以SimpleDraweeView為例,它的controller是PipelineDraweeController,F(xiàn)resco在初始化時(shí)初始化一個(gè)PipelineDraweeControllerBuilderSupplier來(lái)提供這個(gè)controller,DraweeHolder主要通過(guò)controller啟動(dòng)后臺(tái)服務(wù)獲取數(shù)據(jù),setController方法執(zhí)行后后調(diào)用controller的onAttach()方法,這個(gè)方法會(huì)提交請(qǐng)求并獲得數(shù)據(jù),后將數(shù)據(jù)返回給ui線(xiàn)程,
Fresco數(shù)據(jù)是封裝成Supplier<DataSource<IMAGE>> 的形式返回,而真正將數(shù)據(jù)傳遞回來(lái)的是通過(guò)ImagePipeline,這個(gè)ImagePipeline會(huì)在PipelineDraweeControllerBuilderSupplier的構(gòu)造器初始化創(chuàng)建一個(gè),它會(huì)在Fresco初始化方法中進(jìn)行初始化,SimpleDraweeView初始化方法中會(huì)調(diào)用PipelineDraweeControllerBuilderSupplier的get的方法將ImagePipeline返回,ImagePipeline有一個(gè)RequestListsner的成員變量,它管理這個(gè)一個(gè)監(jiān)聽(tīng)列表,請(qǐng)求監(jiān)聽(tīng)通過(guò)這個(gè)RequestListsner返回,而正確去獲取數(shù)據(jù)的時(shí)候是啟動(dòng)一個(gè)后臺(tái)服務(wù)進(jìn)行數(shù)據(jù)獲取的,整個(gè)數(shù)據(jù)獲取有分多個(gè)階段,F(xiàn)resco通過(guò)producer劃分這些階段,比如緩存獲取階段、編碼階段、網(wǎng)絡(luò)獲取階段,它會(huì)一個(gè)一個(gè)階段去獲取數(shù)據(jù),如果producer1沒(méi)有獲取到數(shù)據(jù)就交給producer2進(jìn)行獲取數(shù)據(jù)以此類(lèi)推下去,producer之間通過(guò)consumer返回?cái)?shù)據(jù),最終將數(shù)據(jù)返回到ui線(xiàn)程去。
2.1SimpleDraweeView的繼承關(guān)系
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) 最簡(jiǎn)單的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);
}
可以從字面看出來(lái) 給當(dāng)前view 設(shè)置一個(gè)controller,一個(gè)DraweeView對(duì)應(yīng)一個(gè)DraweeController。而Controller是由build模式所創(chuàng)建,查看 mControllerBuilder 的賦值來(lái)源
private void init(Context context, @Nullable AttributeSet attrs) {
...
mControllerBuilder = sDraweecontrollerbuildersupplier.get();
...
}
這是在SimpleDraweeView的構(gòu)造方法中會(huì)調(diào)用init()方法,繼續(xù)查看sDraweecontrollerbuildersupplier是什么,
public static void initialize(Supplier<? extends AbstractDraweeControllerBuilder> draweeControllerBuilderSupplier) {
sDraweecontrollerbuildersupplier = draweeControllerBuilderSupplier;
}
initialize 是的調(diào)用位置, 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方法,F(xiàn)resco在Application中執(zhí)行Fresco.initialize(context)里 initializeDrawee(context, draweeConfig);
初始化Drawee相關(guān)配置信息。所以可以先搞清楚
sDraweeControllerBuilderSupplier 是 PipelineDraweeControllerBuilderSupplier
mControllerBuilder 是 PipelineDraweeControllerBuilder
controller 是 PipelineDraweeController
2.3setController(controller)
public void setController(@Nullable DraweeController draweeController) {
mDraweeHolder.setController(draweeController);
super.setImageDrawable(mDraweeHolder.getTopLevelDrawable());
}
這里將控制器傳進(jìn)了holder里面。
mDraweeHolder.setController(draweeController)里最終調(diào)用了controller的onAttach()方法,
@Override
public void onAttach() {
......
mEventTracker.recordEvent(Event.ON_ATTACH_CONTROLLER);
Preconditions.checkNotNull(mSettableDraweeHierarchy);
mDeferredReleaser.cancelDeferredRelease(this);
mIsAttached = true;
if (!mIsRequestSubmitted) {
submitRequest();
}
}
最終執(zhí)行到 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();//獲取數(shù)據(jù)
.......
final String id = mId;
final boolean wasImmediate = mDataSource.hasResult();
final DataSubscriber<T> dataSubscriber =
new BaseDataSubscriber<T>() {
@Override
public void onNewResultImpl(DataSource<T> 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<T> dataSource) {
onFailureInternal(id, dataSource, dataSource.getFailureCause(), /* isFinished */ true);
}
};
mDataSource.subscribe(dataSubscriber,mUiThreadImmediateExecutor);//這個(gè)將獲取的數(shù)據(jù)回調(diào)到ui線(xiàn)程
}
這里可以看出來(lái)是 獲取到數(shù)據(jù),然后將數(shù)據(jù)提交到ui線(xiàn)程去。
2.5 如何獲取數(shù)據(jù)getDataSource()
@Override
protected DataSource<CloseableReference<CloseableImage>> getDataSource() {
.....
return mDataSourceSupplier.get();
}
這里出現(xiàn)了一個(gè)mDataSourceSupplier,mDataSourceSupplier只有在兩個(gè)地方傳遞進(jìn)去,一個(gè)是構(gòu)造器,還有一個(gè)是initialize方法,而這個(gè)方法只有在PipelineDraweeControllerBuilder的obtainController()時(shí)調(diào)用到:
private void init(Supplier<DataSource<CloseableReference<CloseableImage>>> dataSourceSupplier) {
mDataSourceSupplier = dataSourceSupplier;
}
public void initialize( Supplier<DataSource<CloseableReference<CloseableImage>>> dataSourceSupplier, String id,
CacheKey cacheKey, Object callerContext, @Nullable ImmutableList<DrawableFactory> customDrawableFactories,
@Nullable ImageOriginListener imageOriginListener) {
...
init(dataSourceSupplier);
...
}
initialize() 方法是哪里調(diào)用的呢?所有的方法都是在PipelineDraweeControllerBuilder的 調(diào)用父類(lèi)方法Builder 里面處理的
protected PipelineDraweeController obtainController() {
try {
...
controller.initialize(obtainDataSourceSupplier(controller, controllerId),
controllerId,
getCacheKey(),
getCallerContext(),
mCustomDrawableFactories,
mImageOriginListener);
controller.initializePerformanceMonitoring(mImagePerfDataListener);
return controller;
} finally {
...
}
}
父類(lèi) 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 創(chuàng)建賦值的流程大概出來(lái)了
SimpleDraweeView.setImageURI() --> PipelineDraweeControllerBuilder.build() --> PipelineDraweeControllerBuilder.buildController() --> PipelineDraweeControllerBuilder.obtainController() --> PipelineDraweeController.initialize() --> 賦值完成
按照上述流程繼續(xù)走下
mDataSourceSupplier 取值方法 obtainDataSourceSupplier(controller, controllerId)
protected Supplier<DataSource<IMAGE>> 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<Supplier<DataSource<IMAGE>>> suppliers = new ArrayList<>(2);
suppliers.add(supplier);
suppliers.add(getDataSourceSupplierForRequest(controller, controllerId, mLowResImageRequest));
supplier = IncreasingQualityDataSourceSupplier.create(suppliers, false);
}
...
return supplier;
}
發(fā)現(xiàn)最終是走到getDataSourceSupplierForRequest方法。
protected Supplier<DataSource<IMAGE>> getDataSourceSupplierForRequest(
final DraweeController controller, String controllerId, REQUEST imageRequest) {
return getDataSourceSupplierForRequest(
controller, controllerId, imageRequest, CacheLevel.FULL_FETCH);
}
protected Supplier<DataSource<IMAGE>> getDataSourceSupplierForRequest(
final DraweeController controller,
final String controllerId,
final REQUEST imageRequest,
final CacheLevel cacheLevel) {
final Object callerContext = getCallerContext();
return new Supplier<DataSource<IMAGE>>() {
@Override
public DataSource<IMAGE> get() {
// 這里就是 getDataSource() 時(shí)候真正調(diào)用的方法
return getDataSourceForRequest(
controller, controllerId, imageRequest, callerContext, cacheLevel);
}
@Override
public String toString() {
...
}
};
}
現(xiàn)在流程最終到getDataSourceForRequest()
2.5getDataSourceForRequest
現(xiàn)在流程為
onAttach() --> submitRequest() --> getDataSource() -->mDataSourceSupplier.get() --> getDataSourceForRequest()
// PipelineDraweeControllerBuilder.java
protected DataSource<CloseableReference<CloseableImage>> getDataSourceForRequest(
DraweeController controller,
String controllerId,
ImageRequest imageRequest,
Object callerContext,
AbstractDraweeControllerBuilder.CacheLevel cacheLevel) {
return mImagePipeline.fetchDecodedImage(
imageRequest,
callerContext,
convertCacheLevelToRequestLevel(cacheLevel),
getRequestListener(controller));
}
可以看出來(lái) 最終把任務(wù)提交到mImagePipeline.fetchDecodedImage()去執(zhí)行。
2.6ImagePipeline是什么
ImagePipeline負(fù)責(zé)完成加載圖像,變成Android設(shè)備可呈現(xiàn)的形式所要做的每個(gè)事情。
pipelineSequence
大致流程如下:
1.檢查內(nèi)存緩存,如有,返回
2.后臺(tái)線(xiàn)程開(kāi)始后續(xù)工作
3.檢查是否在未解碼內(nèi)存緩存中。如有,解碼,變換,返回,然后緩存到內(nèi)存緩存中。
4.檢查是否在磁盤(pán)緩存中,如果有,變換,返回。緩存到未解碼緩存和內(nèi)存緩存中。
5.從網(wǎng)絡(luò)或者本地加載。加載完成后,解碼,變換,返回。存到各個(gè)緩存中。
至此流程基本結(jié)束
三、其他
3.1
Asynctask配合使用時(shí)候,注意阻塞問(wèn)題,Android4.0以后Asynctask就改成(先進(jìn)先出)誰(shuí)先來(lái)誰(shuí)先執(zhí)行并且只能一個(gè)線(xiàn)程執(zhí)行,這樣就導(dǎo)致了所有Http請(qǐng)求都阻塞了。
3.2從緩存中讀取文件
使用Fresco加載圖片,預(yù)覽保存圖片不在下載而是直接從緩存中讀取,
FileBinaryResource resource = (FileBinaryResource) Fresco.getImagePipelineFactory().getMainFileCache().getResource(new SimpleCacheKey(URI));
File file2 = resource.getFile();
說(shuō)明:
1.Fresco不同版本略不同,老版本是getMainFileCache()。
2.得到文件路徑是安裝包路徑,并且是.cnt未結(jié)尾。
/data/user/0/com.app.example/cache/image_cache/v2.ols100.1/67/MuA4Ubc_k3r3KFyVmpyoOw6RHU8.cnt
3.所以如果涉及到微信分享出去等,需要重新復(fù)制一個(gè).jpg或png的文件。