Official documentation: www.fresco-cn.org/docs/gettin…
preface
Fresco is a powerful image loading library from Facebook
The advantages and disadvantages
Advantages:
1) Automatic memory reclamation. If the image is not visible, it will automatically release the memory occupied in time, avoiding OOM 2) level 3 cache mechanism as much as possible. Two levels of memory cache (decoded and undecoded) + one level of disk cache to improve loading speed and save memory footprint 3) Support various loading scenarios. Such as GIF loading, Gaussian blur and other common image loading scenarios. In addition, it provides unique incremental loading, loading small image before loading large image, loading schedule, etc. (very powerful).
Disadvantages:
1) Big (very fat). Larger than other mainstream photo libraries 2) more intrusive. Use SimpleDraweeView instead of ImageView to load and display images. Generally, if your application has high requirements for image display, loading, etc., then Fresco is recommended. But if it’s not that demanding use Glide or another library.
introduce
Configuration, SimpleDraweeView, loading images, obfuscating, and more.
1. The configuration
1.1 Adding a Dependency
compile 'com. Facebook. Fresco ": the fresco" : 1.5.0'
compile 'com. Facebook. Fresco ": animated - GIF: 1.5.0'// Add this library compile to load gifs'com. Facebook. Fresco ": animated - webp: 1.5.0'// Add this library compile to load webp GIF'com. Facebook. Fresco ": webpsupport: 1.5.0'// add this library compile to support webp'com. Facebook. Fresco ": imagepipeline - okhttp3:1.5.0'// The network implementation layer uses okhttp3 to add this library compile'jp. Wasabeef: fresco "- processors: 2.1.0 @ aar'// Used to provide fresco image transformationsCopy the code
1.2 Configuring Disk Caching
ImagePipelineConfig.Builder imagePipelineConfigBuilder = ImagePipelineConfig.newBuilder(context); imagePipelineConfigBuilder.setMainDiskCacheConfig(DiskCacheConfig.newBuilder(context) SetBaseDirectoryPath (context. GetExternalCacheDir ()) / / set the path of the disk cache SetBaseDirectoryName (baseconstants.app_image)// Set the name of the disk cache folder. SetMaxCacheSize (MAX_DISK_CACHE_SIZE)// Set the size of the disk cache.build());Copy the code
1.3 Setting Memory Cache
Set up the decoded memory cache (Bitmap cache)
ImagePipelineConfig.Builder imagePipelineConfigBuilder = ImagePipelineConfig.newBuilder(context);
imagePipelineConfigBuilder.setBitmapMemoryCacheParamsSupplier(new Supplier<MemoryCacheParams>() {
public MemoryCacheParams get() { int MAX_HEAP_SIZE = (int) Runtime.getRuntime().maxMemory(); int MAX_MEMORY_CACHE_SIZE = MAX_HEAP_SIZE / 5; MemoryCacheParams bitmapCacheParams = new MemoryCacheParams(// // Maximum memory available, MAX_MEMORY_CACHE_SIZE, in bytes, // Maximum number of images allowed in memory integer. MAX_VALUE, // Maximum amount of memory available for total images that are ready to be cleaned up but not deleted, MAX_MEMORY_CACHE_SIZE, in bytes, // Maximum number of images in memory to be cleared integer. MAX_VALUE, // Maximum size of a single image in memory integer. MAX_VALUE);returnbitmapCacheParams; }});Copy the code
Set undecoded memory cache
ImagePipelineConfig.Builder imagePipelineConfigBuilder = ImagePipelineConfig.newBuilder(context);
imagePipelineConfigBuilder.setEncodedMemoryCacheParamsSupplier(new Supplier<MemoryCacheParams>() {
public MemoryCacheParams get() { MemoryCacheParams bitmapCacheParams; // Set the size, refer to the decoded memory cache abovereturnbitmapCacheParams; }});Copy the code
1.4 Setting The Solution for Memory Shortage
MemoryTrimmableRegistry memoryTrimmableRegistry = NoOpMemoryTrimmableRegistry.getInstance();
memoryTrimmableRegistry.registerMemoryTrimmable(new MemoryTrimmable() {
@Override
public void trim(MemoryTrimType trimType) {
final double suggestedTrimRatio = trimType.getSuggestedTrimRatio();
if(MemoryTrimType.OnCloseToDalvikHeapLimit.getSuggestedTrimRatio() == suggestedTrimRatio || MemoryTrimType.OnSystemLowMemoryWhileAppInBackground.getSuggestedTrimRatio() == suggestedTrimRatio || MemoryTrimType. OnSystemLowMemoryWhileAppInForeground. GetSuggestedTrimRatio () = = suggestedTrimRatio) {/ / clear the memory cache ImagePipelineFactory.getInstance().getImagePipeline().clearMemoryCaches(); }}}); ImagePipelineConfig.Builder imagePipelineConfigBuilder = ImagePipelineConfig.newBuilder(context); imagePipelineConfigBuilder.setMemoryTrimmableRegistry(memoryTrimmableRegistry);Copy the code
1.5 Set progressive display effect
ProgressiveJpegConfig progressiveJpegConfig = new ProgressiveJpegConfig() {@ Override public int getNextScanNumberToDecode (int scanNumber) {/ / return the next need to scan the decodingreturnscanNumber + 2; } public QualityInfo getQualityInfo(int scanNumber) { boolean isGoodEnough = (scanNumber >= 5); // Determine the number of scans before the image is displayed.return ImmutableQualityInfo.of(scanNumber, isGoodEnough, false); }}; / / ImagePipelineConfig specific meaning may refer to http://wiki.jikexueyuan.com/project/fresco/progressive-jpegs.html. The Builder imagePipelineConfigBuilder = ImagePipelineConfig.newBuilder(context); imagePipelineConfigBuilder.setProgressiveJpegConfig(progressiveJpegConfig); / / or the effect of using the default / / imagePipelineConfigBuilder setProgressiveJpegConfig (new SimpleProgressiveJpegConfig ());Copy the code
After setting up the effects, you also need to enable progressive loading in ImageRequest, described below.
1.6 Allows resizing images while decoding
If allowed, the size of the decoded image can be adjusted in combination with ResizeOptions in ImageRequest introduced later, so as to optimize the image size. Only JPEG images are supported by default, so set this property to support PNG, JPG, webP.
ImagePipelineConfig.Builder imagePipelineConfigBuilder = ImagePipelineConfig.newBuilder(context);
imagePipelineConfigBuilder.setDownsampleEnabled(true);
Copy the code
1.7 open the Log
FLog.setMinimumLoggingLevel(FLog.VERBOSE);
Set<RequestListener> requestListeners = new HashSet<>();
requestListeners.add(new RequestLoggingListener());
ImagePipelineConfig.Builder imagePipelineConfigBuilder = ImagePipelineConfig.newBuilder(context);
imagePipelineConfigBuilder.setRequestListeners(requestListeners);
Copy the code
1.8 the initialization
All of the above configuration is done via ImagePipelineConfig and then needs to be initialized in the Application
ImagePipelineConfig.Builder imagePipelineConfigBuilder = ImagePipelineConfig.newBuilder(context); / /... In various Settings ImagePipelineConfig config = imagePipelineConfigBuilder. The build (); Fresco.initialize(context, config);Copy the code
If you want to use the default configuration, you can
Fresco.initialize(context);
Copy the code
2. SimpleDraweeView
Fresco requires that images be loaded and displayed using SimpleDraweeView instead of ImageView, which is why some people don’t want to use Fresco.
The following describes the various attributes of SimpleDraweeView in XML
/ / in the outermost layout attribute to join XMLNS: fresco “=” schemas.android.com/apk/res-aut…”
<com.facebook.drawee.view.SimpleDraweeView
android:id="@+id/sdv"
android:layout_width="150dp"
android:layout_height="150dp"
fresco:actualImageScaleType="centerCrop"
fresco:fadeDuration="2000"
fresco:failureImage="@mipmap/ic_launcher"
fresco:failureImageScaleType="centerCrop"
fresco:placeholderImage="@mipmap/ic_launcher"
fresco:placeholderImageScaleType="centerCrop"
fresco:progressBarImage="@drawable/rotate"
fresco:progressBarImageScaleType="centerCrop"
fresco:retryImage="@mipmap/ic_launcher"
fresco:retryImageScaleType="centerCrop"
fresco:backgroundImage="@mipmap/ic_launcher"
fresco:overlayImage="@mipmap/ic_launcher"
fresco:pressedStateOverlayImage="@mipmap/ic_launcher"
fresco:roundAsCircle="false"
fresco:roundedCornerRadius="7dp"
fresco:roundTopLeft="true"
fresco:roundTopRight="false"
fresco:roundBottomLeft="false"
fresco:roundBottomRight="true"
fresco:roundWithOverlayColor="@color/colorAccent"
fresco:roundingBorderWidth="2dp"
fresco:roundingBorderColor="@color/colorPrimary"
fresco:viewAspectRatio="1"/>
Copy the code
Attribute Description
ActualImageScaleType Loads the scale style of the finished image
FadeDuration The time interval used to transition from the progress bar and placeholder images to the finished loaded images
FailureImage Image used by the loading failure
FailureImageScaleType Scale style of the image used when loading failed
PlaceholderImage placeholderImage
PlaceholderImageScaleType placeholder image scaling style
Rotate the progress bar progressBarAutoRotateInterval 1 lap time
ProgressBarImage Image used to rotate the progress bar
ProgressBarImageScaleType used by rotating the progress bar image scaling style
RetryImage Specifies the image used for retry
RetryImageScaleType Retries the scaling style of the image used
BackgroundImage backgroundImage
OverlayImage Overlays an image over a loaded image
PressedStateOverlayImage Overlay image in the pressed state
RoundAsCircle whether to cut the image to a circle
RoundedCornerRadius Specifies the radius of the rounded corner when the image is rounded
Whether the upper left corner of the roundTopLeft is rounded
Whether the upper right corner of roundTopRight is rounded
Whether the lower left corner of roundBottomLeft is rounded
Whether the lower right corner of roundBottomRight is rounded
RoundWithOverlayColor Round corners or circles overlay color, can only be color
RoundingBorderWidth Width of the border of a rounded corner or circle
RoundingBorderColor The color of the rounded corners or borders of a circular drawing
ViewAspectRatio Sets the aspect ratio
* note:
1) The android: SRC attribute is invalid for SimpleDraweeView, use fresco:placeholderImage if necessary. 2) SimpleDraweeView does not support android:layout_width and Android :layout_height both set to wrap_content.
3. Load the image
Loading images with Fresco roughly follows this process. Hierarchay 1. Set Hierarchay (attributes in the XML above and display load progress bar, etc.) 2. Build ImageRequest (loading path, enable progressive loading, image transform, resize decoded image, etc., can be set here) 3. Build DraweeController (GIF load, click reload after failure, etc., can be set here) 4. Loading images
3.1 set Hierarchay
Although all the properties in the XML can be set in this step through code, there are generally only fixed properties set here, such as load placeholder, load failure graph, and so on. In addition, the loading progress of images is also set here.
Resources res = MyApplication.getInstance().getResources(); Drawable retryImage = ResourcesCompat.getDrawable(res, R.mipmap.ic_image_load, null); Drawable failureImage = ResourcesCompat.getDrawable(res, R.mipmap.ic_image_load, null); Drawable placeholderImage = ResourcesCompat.getDrawable(res, R.mipmap.ic_image_load, null); Hierarchy is set, for example, images displayed in various states public voidsetHierarchay(GenericDraweeHierarchy hierarchy) {
if(hierarchy ! = null) {// Reload the displayed image hierarchy. SetRetryImage (retryImage); / / load failure display picture hierarchy. The setFailureImage (failureImage, ScalingUtils. ScaleType. CENTER_CROP); / / before completion of loading shows placeholder figure hierarchy. SetPlaceholderImage (placeholderImage, ScalingUtils. ScaleType. CENTER_CROP); / / set to load after a successful picture zoom model hierarchy. The setActualImageScaleType (ScalingUtils. ScaleType. CENTER_CROP); New ProgressBarDrawable() is displayed at the bottom of the image by default. You can set the color of the progress bar. hierarchy.setProgressBarImage(new ProgressBarDrawable()); / / set the picture loaded on circular hierarchy. The setRoundingParams (RoundingParams. AsCircle ()); / / set the images added to rounded corners, and can set the fillet size hierarchy. SetRoundingParams (RoundingParams. FromCornersRadius (radius)); // For other Settings, please check the specific API. }}Copy the code
3.2 build ImageRequest
/** * Build ImageRequest * @param URI load path * @param simpleDraweeView load image control * @return ImageRequest
*/
public ImageRequest getImageRequest(Uri uri, SimpleDraweeView simpleDraweeView) {
int width;
int height;
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {
width = simpleDraweeView.getWidth();
height = simpleDraweeView.getHeight();
} else{ width = simpleDraweeView.getMaxWidth(); height = simpleDraweeView.getMaxHeight(); } / / according to the request path generates ImageRequest initializer ImageRequestBuilder builder = ImageRequestBuilder. NewBuilderWithSource (uri). // Resize the decoded imageif(width > 0 && height > 0) { builder.setResizeOptions(new ResizeOptions(width, height)); } / / set whether to open the incremental load, only supports JPEG image builder. SetProgressiveRenderingEnabled (true); / / picture transformation processing CombinePostProcessors. The Builder processorBuilder = new CombinePostProcessors. Builder (); ProcessorBuilder. Add (new BlurPostprocessor(context, RADIUS)); Processorbuilder.add (new GrayscalePostprocessor()); Builder.setpostprocessor (processorBuilder.build())); / / transform to see https://github.com/wasabeef/fresco-processors for more picturesreturn builder.build();
}
Copy the code
3.3 build DraweeController
/** * Build and get Controller * @param request * @param oldController * @return*/ public DraweeController getController(ImageRequest request, @Nullable DraweeController oldController) { PipelineDraweeControllerBuilder builder = Fresco.newDraweeControllerBuilder(); builder.setImageRequest(request); / / set the picture request builder. SetTapToRetryEnabled (false); Click when loading failure / / Settings are allowed to load builder. SetAutoPlayAnimations (true); // Set whether to allow the animation to autoplay builder.setoldController (oldController);return builder.build();
}
Copy the code
3.4 Loading images
Create a loadImage method to string the Hierarchy, ImageRequest, and DraweeController together for specific loading scenarios
/** @param simpleDraweeView @param uri @param public void loadImage(simpleDraweeView SimpleDraweeView, Uri Uri) {// Set HierarchysetHierarchay(simpleDraweeView.getHierarchy()); ImageRequest ImageRequest = getImageRequest(URI, simpleDraweeView); / / build and obtain the Controller DraweeController DraweeController = getController (imageRequest, simpleDraweeView. The getController ()); / / load simpleDraweeView. SetController (draweeController); }Copy the code
Specific loading scenarios:
- Load web images, including GIFs/Webp giFs
public void loadNetImage(SimpleDraweeView simpleDraweeView, String url) {
Uri uri = Uri.parse(url);
loadImage(simpleDraweeView, uri);
}
Copy the code
Load local file images
public void loadLocalImage(SimpleDraweeView simpleDraweeView, String fileName) {
Uri uri = Uri.parse("file://" + fileName);
loadImage(simpleDraweeView, uri);
}
Copy the code
Load resource images under RES
public void loadResourceImage(SimpleDraweeView simpleDraweeView, @DrawableRes int resId) {
Uri uri = Uri.parse("res:///" + resId);
loadImage(simpleDraweeView, uri);
}
Copy the code
Load the image under the ContentProvider
public void loadContentProviderImage(SimpleDraweeView simpleDraweeView, int resId) {
Uri uri = Uri.parse("content:///" + resId);
loadImage(simpleDraweeView, uri);
}
Copy the code
Load the image under asset
public void loadAssetImage(SimpleDraweeView simpleDraweeView, int resId) {
Uri uri = Uri.parse("asset:///" + resId);
}
Copy the code
To load network images, load the small image first and replace the small image after the large image is loaded
This requires modifying the DraweeController build to add the small graph request via setLowResImageRequest
public DraweeController getSmallToBigController(ImageRequest smallRequest, ImageRequest bigRequest, @Nullable DraweeController oldController) { PipelineDraweeControllerBuilder builder = Fresco.newDraweeControllerBuilder(); builder.setLowResImageRequest(smallRequest); Builder.setimagerequest (bigRequest); / / a larger picture request builder. The setTapToRetryEnabled (false); Click when loading failure / / Settings are allowed to load builder. SetAutoPlayAnimations (true); // Set whether to allow the animation to autoplay builder.setoldController (oldController);returnbuilder.build(); } public void loadImageSmallToBig(SimpleDraweeView SimpleDraweeView, Uri smallUri, Uri bigUri) {// Set HierarchysetHierarchay(simpleDraweeView.getHierarchy()); ImageRequest smallRequest = getImageRequest(smallUri, simpleDraweeView); ImageRequest bigRequest = getImageRequest(bigUri, simpleDraweeView); DraweeController = getSmallToBigController(smallRequest, bigRequest, simpleDraweeView.getController()); / / load simpleDraweeView. SetController (draweeController); } // Load the network image. Public void loadNetImageSmallToBig(SimpleDraweeView SimpleDraweeView, String smallUrl, String bigUrl) { Uri smallUri = Uri.parse(smallUrl); Uri bigUri = Uri.parse(bigUrl); loadImageSmallToBig(simpleDraweeView, smallUri, bigUri); }Copy the code
4. Confused
Add the following to the proGuard-rules. pro file for obfuscation configuration
-keep class com.facebook.fresco.** { *; } -keep,allowobfuscation @interface com.facebook.common.internal.DoNotStrip -keep @com.facebook.common.internal.DoNotStrip class * -keepclassmembers class * { @com.facebook.common.internal.DoNotStrip *; } -keep class com.facebook.imagepipeline.gif.** { *; } -keep class com.facebook.imagepipeline.webp.* { *; } -keepclassmembers class * { native <methods>; } -dontwarn okio.** -dontwarn com.squareup.okhttp.** -dontwarn okhttp3.** -dontwarn javax.annotation.** -dontwarn com.android.volley.toolbox.** -keep class com.facebook.imagepipeline.animated.factory.AnimatedFactoryImpl { public AnimatedFactoryImpl(com.facebook.imagepipeline.bitmaps.PlatformBitmapFactory,com.facebook.imagepipeline.core.ExecutorSup plier); }Copy the code
5. Other
5.1 Cache Policy Fresco uses a three-level cache mechanism, consisting of two levels of memory cache and one level of disk cache. The two levels of memory cache are divided into Bitmap cache and undecoded image cache. Let’s look at the caching strategy through the load process. 1. Check whether a corresponding Bitmap exists in the decoded image cache according to the Uri. If present, return Bitmap display; If not, look it up in the undecoded image cache. 2. If the corresponding data exists in the undecoded image cache, decode, return the Bitmap display and add it to the decoded image cache; If not, look it up in the disk cache. 3. If the corresponding data exists in the disk cache, the data is added to the undecoded image cache, and then decoded, returning the Bitmap display and adding it to the decoded image cache; If not, make a network request or load to a local file. 4. After the request or load is successful, add the data to the disk cache and the undecoded image cache, then decode, return the Bitmap display and add it to the decoded image cache. Simple whole schematic diagram, to help understand:
5.2 compatible with shared animation android5.0 added shared animation, if directly combined with Fresco and shared animation page transition effect, will find invalid or abnormal. Fresco has a note from www.fresco-cn.org/docs/shared… 1. Overwrite the XML file that shared the animation transformation effect, comment out the changeImageTransform, and place the file in the RES/Transition folder
<? xml version="1.0" encoding="utf-8"? > <transitionSet xmlns:android="http://schemas.android.com/apk/res/android"> <explode/> <changeBounds/> <changeTransform/> <changeClipBounds/> <! --<changeImageTransform/>--> <! -- The Fresco image framework does not support changeImageTransform. By default, all five transformations are used, so you need to rewrite the XML and comment out changeImageTransform --> </transitionSet>Copy the code
2. Use the XML file overridden in the previous step in the style file
<? xml version="1.0" encoding="utf-8"? > <resources> <style name="AppTheme" parent="AppTheme.Base"> <! -- Transitions -> <item name= is allowed"android:windowContentTransitions">true</item> <! -- Specify shared Element transitions --> <item name="android:windowSharedElementEnterTransition">
@transition/share_element_transition</item>
<item name="android:windowSharedElementExitTransition">
@transition/share_element_transition</item>
</style>
</resources>
Copy the code
5.3 Viewing the Larger Image “Click on the smaller image to view the larger image, and the larger image supports zooming.” This requirement is common; the SimpleDraweeView mentioned above doesn’t support zooming and so on, so you need to customize a control to display it. A ZoomableDraweeView is provided to support this scene, and you can also refer to the PhotoDraweeView
Sometimes, we need to get the Bitmap from the network request, so we can do this:
// Load the image, Public void getBitmap(Context Context, String URL, final ImageListener<Bitmap> imageListener) { Uri uri = Uri.parse(url); ImagePipeline imagePipeline = Fresco.getImagePipeline(); ImageRequestBuilder builder = ImageRequestBuilder.newBuilderWithSource(uri); ImageRequest imageRequest = builder.build(); DataSource<CloseableReference<CloseableImage>> dataSource = imagePipeline.fetchDecodedImage(imageRequest, context); dataSource.subscribe(new BaseDataSubscriber<CloseableReference<CloseableImage>>() { @Override public void onNewResultImpl(DataSource<CloseableReference<CloseableImage>> dataSource) {if(! dataSource.isFinished()) {return;
}
CloseableReference<CloseableImage> imageReference = dataSource.getResult();
if(imageReference ! = null) { final CloseableReference<CloseableImage> closeableReference = imageReference.clone(); try { CloseableImage closeableImage = closeableReference.get(); // GIF processingif(closeableImage instanceof CloseableAnimatedImage) { AnimatedImageResult animatedImageResult = ((CloseableAnimatedImage) closeableImage).getImageResult();if(animatedImageResult ! = null && animatedImageResult.getImage() ! = null) { int imageWidth = animatedImageResult.getImage().getWidth(); int imageHeight = animatedImageResult.getImage().getHeight(); Bitmap.Config bitmapConfig = Bitmap.Config.ARGB_8888; Bitmap bitmap = Bitmap.createBitmap(imageWidth, imageHeight, bitmapConfig); animatedImageResult.getImage().getFrame(0).renderFrame(imageWidth, imageHeight, bitmap);if(imageListener ! = null) { imageListener.onSuccess(bitmap); }}} // Non-gif processingelse if (closeableImage instanceof CloseableBitmap) {
CloseableBitmap closeableBitmap = (CloseableBitmap) closeableImage;
Bitmap bitmap = closeableBitmap.getUnderlyingBitmap();
if(bitmap ! = null && ! bitmap.isRecycled()) { final Bitmap tempBitmap = bitmap.copy(bitmap.getConfig(),false);
if(imageListener ! = null) { imageListener.onSuccess(tempBitmap); } } } } finally { imageReference.close(); closeableReference.close(); } } } @Override public void onFailureImpl(DataSource dataSource) { Throwable throwable = dataSource.getFailureCause();if(imageListener ! = null) { imageListener.onFail(throwable); } } }, UiThreadImmediateExecutorService.getInstance()); }Copy the code
Or if there is data in the cache, it can be fetched from the cache and converted into a Bitmap
FileBinaryResource resource = (FileBinaryResource) Fresco.getImagePipelineFactory().getMainFileCache().getResource(new SimpleCacheKey(url));
if(resource ! = null && resource.getFile() ! = null) { Bitmap bitmap = BitmapFactory.decodeFile(resource.getFile().getAbsolutePath()); }Copy the code
5.5 Downloading Images
Download the image to the specified location and get the download result in the ImageListener callback
public void downLoadImage(Context context, String url, final File saveFile, final ImageListener<File> imageListener) { Uri uri = Uri.parse(url); ImagePipeline imagePipeline = Fresco.getImagePipeline(); ImageRequestBuilder builder = ImageRequestBuilder.newBuilderWithSource(uri); ImageRequest imageRequest = builder.build(); // CloseableReference<PooledByteBuffer>> DataSource = imagePipeline.fetchEncodedImage(imageRequest, context); dataSource.subscribe(new BaseDataSubscriber<CloseableReference<PooledByteBuffer>>() { @Override public void onNewResultImpl(DataSource<CloseableReference<PooledByteBuffer>> dataSource) {if(! dataSource.isFinished()) {return;
}
CloseableReference<PooledByteBuffer> imageReference = dataSource.getResult();
if(imageReference ! = null) { final CloseableReference<PooledByteBuffer> closeableReference = imageReference.clone(); try { PooledByteBuffer pooledByteBuffer = closeableReference.get(); InputStream inputStream = new PooledByteBufferInputStream(pooledByteBuffer); OutputStream outputStream = new FileOutputStream(saveFile);if(FileUtil.saveFile(inputStream, outputStream) && imageListener ! = null) { imageListener.onSuccess(saveFile); } } catch (Exception e) {if(imageListener ! = null) { imageListener.onFail(e); } e.printStackTrace(); } finally { imageReference.close(); closeableReference.close(); } } } @Override public void onProgressUpdate(DataSource<CloseableReference<PooledByteBuffer>> dataSource) { int progress = (int) (dataSource.getProgress() * 100); RingLog.d("Fresco download progress:" + progress);
}
@Override
public void onFailureImpl(DataSource dataSource) {
Throwable throwable = dataSource.getFailureCause();
if(imageListener ! = null) { imageListener.onFail(throwable); } } }, Executors.newSingleThreadExecutor()); }Copy the code