In a recent project development, I used fresco as the image loading engine framework. Fresco is very powerful, but I encountered many pits in the development process, so I wrote an article to record the problems I encountered in order to facilitate your future use.
The corresponding code in this article is github On the other hand, the code is written in kotlin, so you can experience the power of kotlin in the process of learning.
Environmental Construction
compile 'com.facebook.fresco:fresco:1.3.0' // Support webp compile 'com.facebook.fresco:webpsupport:1.3.0' // gif load usage compile 'com.facebook.fresco:animated-gif:1.3.0' // WebP (Static Graph + Motion Graph) Loading Use compile 'com.facebook.fresco:animated-webp:1.3.0'
Basic concepts
Let's start with a brief look at the important classes in fresco
DraweeView: Inherited from View, we usually use the SimpleDraweeView class for image loading. We'll talk about its custom attributes later.
Drawee Hierarchy: The class that renders the content of an image, through which we can set the properties of DraweeView in java code
DraweeController: Class responsible for image loading at the bottom of the framework
Image Pipeline: (Graphic Pipeline) Complete the acquisition of pictures. Whether through the network, local files, content provider s or local resources, it compresses and caches pictures to disk, and stores decoded pictures in memory as a second cache.
Configuration of attributes in XML
Fresco has the following layers
/* Occupancy Layer, which is displayed by default */ private final int mPlaceholderImageIndex; /* Schedule Bar Layer: Round or Linear Schedule Bar during Loading */ private final int mProgressBarImageIndex; /* Target Display Layer, the Layer Displayed after Loading */ private final int mActualImageIndex; /* Retry Layer, which is the layer displayed when the load fails to retry */ private final int mRetryImageIndex; /* Failure Layer, which is the layer displayed when loading fails */ private final int mFailureImageIndex; /* Control Overlay Layer, that is, Overlay Layer */ private final int mControllerOverlayIndex;
Around these six layers, our SimpleDraweeView layout generally contains the following main elements
<com.facebook.drawee.view.SimpleDraweeView xmlns:fresco="http://schemas.android.com/apk/res-auto" android:id="@+id/image_fresco2" android:layout_width="100dip" android:layout_height="wrap_content" fresco:actualImageScaleType="centerCrop" fresco:fadeDuration="3000" fresco:failureImage="@mipmap/ic_launcher" fresco:failureImageScaleType="centerCrop" fresco:placeholderImage="@mipmap/ic_launcher" fresco:placeholderImageScaleType="centerCrop" fresco:progressBarAutoRotateInterval="1000" fresco:progressBarImage="@drawable/ani_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="5dip" fresco:roundTopLeft="true" fresco:roundTopRight="true" fresco:roundBottomLeft="true" fresco:roundBottomRight="true" fresco:roundWithOverlayColor="@color/colorAccent" fresco:roundingBorderWidth="2dip" fresco:roundingBorderColor="@color/colorPrimary" fresco:viewAspectRatio="1"/>
attribute | Function description |
---|---|
actualImageScaleType | Zoom Styles for Loading Completed Pictures |
fadeDuration | Time interval used to transit from progress bar and placeholder image to loaded image |
failureImage | Pictures Used for Load Failure |
failureImageScaleType | Scaling Styles of Pictures Used in Load Failure |
placeholderImage | Placeholder picture |
placeholderImageScaleType | Scaling Style of Placeholder Pictures |
progressBarAutoRotateInterval | The time required to rotate the progress bar for one turn |
progressBarImage | Pictures used to rotate progress bars |
progressBarImageScaleType | Scaling Style of Pictures Used in Rotating Progress Bar |
retryImage | Retry the images used |
retryImageScaleType | Retry the zoom style of the image used |
backgroundImage | Background picture |
overlayImage | Overlay Overlay Pictures on Images After Loading |
pressedStateOverlayImage | Overlay Pictures under Pressure |
roundAsCircle | Whether to cut the picture into a circle |
roundedCornerRadius | The radius of the corner in a rounded picture |
roundTopLeft | Is the upper left corner rounded? |
roundTopRight | Is the upper right corner rounded? |
roundBottomLeft | Is the lower left corner rounded? |
roundBottomRight | Is the lower right corner rounded? |
roundWithOverlayColor | The color of an overlay of rounded corners or circles can only be a color. |
roundingBorderWidth | Width of the border of a rounded corner or circle |
roundingBorderColor | The color of the border of a rounded corner or circle |
viewAspectRatio | Setting aspect ratio |
Here I'll talk about scaleType. GenericDraweeView has an additional focusCrop property compared to ImageView. The difference between this attribute and centerCrop is that the former can freely define the focus while the latter has a fixed focus. DraweeView displays with this focus as much as possible. Focus is shown in relative coordinates, such as (0f, 0f) upper left corner alignment display, (1f, 1f) lower right corner alignment display. This makes the focus position independent of the specific size, which is very practical. If the focus is set to (0.5f, 0.5f), then it is equivalent to center Crop.
If you want to use this zoom mode, first specify the zoom mode in XML:
fresco:actualImageScaleType="focusCrop"
Then in Java code, specify the focus for your image:
image_fresco2!!.hierarchy.setActualImageFocusPoint(PointF(1f, 1f))
Refer to the Source Section for other attributes
Here are a few things to be noted.
- The width of SimpleDraweeView cannot be wrap_content at the same time. It requires match_parent or a fixed value. Unless you use viewAspectRatio to set proportions, one of the broader, Higher-school parties under these conditions can be wrap_content.
- When using SimpleDraweeView, facebook officially advises against using any attributes of ImageView, such as setImageResource, setBackground, setScaleType, etc., which are in ImageView but not in View.
Implementing attribute configuration in XML in JAVA code
If you are already familiar with custom attributes in xml, it should be easy for you to implement them again in java
Here's a brief look at the dynamic setup process from the source point of view
SimpleDraweeView is inherited from GenericDraweeView
public class SimpleDraweeView extends GenericDraweeView
There is an important method in GenericDraweeView, inflateHierarchy, which is implemented by every construction method of GenericDraweeView. This method is to convert the layer information defined in xml into GenericDrawee Hierarchy Builder, and finally set Hierarchy to GenericDraweeView.
protected void inflateHierarchy(Context context, @Nullable AttributeSet attrs) { GenericDraweeHierarchyBuilder builder = GenericDraweeHierarchyInflater.inflateBuilder(context, attrs); this.setAspectRatio(builder.getDesiredAspectRatio()); this.setHierarchy(builder.build()); }
Now it's obvious that we just need to set up this hierarchy.
/** * Setting the Hierarchy of Various Images */ private fun setNormalHierarchy(context: Context) : GenericDraweeHierarchy { val hierarchy: GenericDraweeHierarchy = this.hierarchy // System default horizontal linear progress bar Progress Bar Drawable // val drawable: Drawable = ContextCompat.getDrawable(context, R.drawable.ani_rotate) if (mySimpleDraweeViewParams!!.progressBarImage!=null) { hierarchy.setProgressBarImage(AutoRotateDrawable(mySimpleDraweeViewParams!!.progressBarImage, 1000)) } // Configure rounded corners var roundingParams: RoundingParams = RoundingParams.fromCornersRadius(30f) hierarchy.fadeDuration= mySimpleDraweeViewParams!!.fadeDuration hierarchy.roundingParams = roundingParams hierarchy.setOverlayImage(mySimpleDraweeViewParams!!.overlayImageDrawable) hierarchy.actualImageScaleType=mySimpleDraweeViewParams!!.scaleType hierarchy.setPlaceholderImage(mySimpleDraweeViewParams!!.placeHolderDrawable, mySimpleDraweeViewParams!!.scaleType) hierarchy.setFailureImage(mySimpleDraweeViewParams!!.placeHolderDrawable, mySimpleDraweeViewParams!!.scaleType) hierarchy.setRetryImage(mySimpleDraweeViewParams!!.placeHolderDrawable, mySimpleDraweeViewParams!!.scaleType) return hierarchy }
Ultimately, the same is true.
SimpleDraweeView.setHierarchy(setNormalHierarchy(context))
Configure Image Pipeline Config
As I said earlier, ImagePipeline plays a very important role in the image loading process. Let's take a look at its workflow.
- Check memory cache, if yes, return
- Background threads start follow-up work
- Check whether it is in the uncoded memory cache. If yes, decode, transform, return, and then cache in memory.
- Check whether it is in the file cache, if so, transform, return. Cached into uncoded and memory caches.
- Load from the network or locally. After loading, decode, transform and return. Store in each cache.
Completing these tasks requires a certain strategy, which requires us to complete through a custom ImagePipeline Config interface.
Just load the default configuration.
Fresco.initialize(this)
Let's look at how to customize the configuration
- Define constant parameters
companion object { // Maximum available memory val maxHeapSize: Long = Runtime.getRuntime().maxMemory() // Cache Disk Small Picture Folder Name val smallDiskCacheName: String = "smallDiskCacheConfig" // Cache Disk Ordinary Picture Folder Name val normalDiskCacheName: String = "normalDiskCacheConfig" // Cache Disk folder size val diskCacheSize: Int = 100 * ByteConstants.MB // Cache Disk Folder Size in Low Hard Disk Space val lowDiskCacheSize: Int = 50 * ByteConstants.MB // Cache Disk Folder Size in Very Low Hard Disk Space val veryLowDiskCacheSize: Int = 20 * ByteConstants.MB }
-
Implementing the ImagePipeline Config interface
fun getDefaultImagePipelineConfig(context: Context) : ImagePipelineConfig { }
Look at the specific configuration
var imagePipelineConfig: ImagePipelineConfig.Builder = ImagePipelineConfig.newBuilder(context) .setProgressiveJpegConfig(SimpleProgressiveJpegConfig()) // Progressive configuration .setBitmapsConfig(Bitmap.Config.RGB_565) // No transparent picture display requirements, set to RGB_565, reduce memory overhead .setBitmapMemoryCacheParamsSupplier(supplierMemoryCacheParams) // Configuring Caching Policy .setSmallImageDiskCacheConfig(smallDiskCacheConfig) // Disk Caching Strategy for Small Pictures .setMainDiskCacheConfig(normalDiskCacheConfig) // Disk Caching Strategy for Basic Pictures .setMemoryTrimmableRegistry(NoOpMemoryTrimmableRegistry.getInstance()) // Register memory callers to reclaim memory when it is necessary to reclaim it .setResizeAndRotateEnabledForNetwork(true) // resize network pictures to reduce memory consumption .setDownsampleEnabled(true) // It must be used with ReizeOptions of ImageRequest to decode the image according to the width and height of the pixels set by Resize Options so that a smaller Bitmap can be obtained.
At first glance, many configurations, let me explain a little:
Image Pipeline Config: Fresco supports the use of OKHTTP as a network loading engine. This article demonstrates the use of the system's own network loading engine. Additional imports are required if the OKhttp network library is used for loading.compile 'com.facebook.fresco:imagepipeline-okhttp3:1.3.0'
At the same time, OkHttpImagePipeline ConfigFactory is used to initialize ImagePipeline Config.
OkHttpImagePipelineConfigFactory.newBuilder(context, OkHttpClient())
setProgressiveJpegConfig: Used to set up incremental, let's go into detail later.
setBitmapsConfig: Filtering alpha channels under RGB_565 reduces image memory consumption and further reduces the risk of OOM
setBitmapMemoryCacheParamsSupplier: Configure the cache policy, which is mainly used to configure the maximum amount of memory available under fresco-related conditions and the number of files. We need to understand this through code.val memmoryCacheParams = MemoryCacheParams( maxHeapSize.toInt()/4, // Maximum available memory in bytes Int.MAX_VALUE, // Maximum number of pictures allowed in memory maxHeapSize.toInt()/4, // The maximum amount of memory available in bytes for the total picture ready to be cleaned up but not yet deleted Int.MAX_VALUE, // Maximum number of images ready to be cleared in memory Int.MAX_VALUE) // Maximum size of a single image in memory
Place the object in Supplier after setting it up
val supplierMemoryCacheParams: Supplier<MemoryCacheParams> = Supplier { return@Supplier memmoryCacheParams }
SetSmallImageDisk CacheConfig: Small picture Disk caching strategy, which mainly sets the location of disk storage and folder related information, available space size and other information.
val smallDiskCacheConfig = DiskCacheConfig.newBuilder(context) .setBaseDirectoryPath(context.applicationContext.cacheDir) // Setting the local root directory path for cached pictures .setBaseDirectoryName(smallDiskCacheName) // Setting Cache Disk Folder Name .setMaxCacheSize(diskCacheSize.toLong()) // Set the cache Disk folder size .setMaxCacheSizeOnLowDiskSpace(lowDiskCacheSize.toLong()) // Setting the size of cache Disk folder in low hard disk space .setMaxCacheSizeOnVeryLowDiskSpace(veryLowDiskCacheSize.toLong()) // Set the size of the cache Disk folder in very low hard disk space .build()
SetMainDisk CacheConfig: This configuration is used by default. Here we choose to use the same caching strategy as the small image Disk, but why do we need to configure two copies? We know that in the actual operation of the relevant folders read and write operations will be very frequent. Because of the capacity of available space, if there is only one DiskCache, once large files come in, most small files will be removed, so separate configuration is a good choice. As for what is a big picture and what is a small picture, Fresco offers two ways to let you load the picture freely.
public static enum CacheChoice { SMALL, DEFAULT; private CacheChoice() { } }
Set up when configuring ImageRequest
var imageRequestBuilder: ImageRequestBuilder = ImageRequestBuilder.newBuilderWithSource(url) imageRequestBuilder.cacheChoice = mySimpleDraweeViewParams!!.cacheChoice
SetMemory Trimmable Registry: This is the default configuration item. Register memory callers to reclaim memory when it is necessary to reclaim it
SetResize AndRotateEnabled ForNetwork: Allowing resize processing when setting ImageRequest reduces memory consumption and also reduces the risk of OOM
SetDownsample Enabled: Must be used with ReizeOptions of ImageRequest, also to reduce the risk of OOM
display
After setting up the basic information of the picture, we go back to the configuration Controller section. First, we configure the imageRequest.
private fun getImageRequest(url: Uri) : ImageRequest { var imageRequestBuilder: ImageRequestBuilder = ImageRequestBuilder.newBuilderWithSource(url) imageRequestBuilder.cacheChoice = mySimpleDraweeViewParams!!.cacheChoice // Picture Selection, Big Picture or Small Picture imageRequestBuilder.lowestPermittedRequestLevel = mySimpleDraweeViewParams!!.requestLevel // Which level of cache is the lowest allowable access to data? imageRequestBuilder.isLocalThumbnailPreviewsEnabled = true // The image request returns a thumbnail when accessing a local image imageRequestBuilder.isProgressiveRenderingEnabled = mySimpleDraweeViewParams!!.progressiveRenderingEnabled // Progressive imageRequestBuilder.resizeOptions = ResizeOptions(measuredWidth, measuredHeight) // The picture is decoded according to the set width and height. imageRequestBuilder.rotationOptions = RotationOptions.forceRotation(mySimpleDraweeViewParams!!.rotate) // Picture rotation angle return imageRequestBuilder.build() }
Then set up some listening events
var pipelineDraweeControllerBuilder: PipelineDraweeControllerBuilder=Fresco.newDraweeControllerBuilder() pipelineDraweeControllerBuilder.autoPlayAnimations = mySimpleDraweeViewParams!!.autoPlayAnimation pipelineDraweeControllerBuilder.controllerListener = mySimpleDraweeViewParams!!.controllerListener pipelineDraweeControllerBuilder.imageRequest=getImageRequest(url)
There's nothing wrong with using it.
image_fresco3!!.hierarchy = setNormalHierarchy(this) var pipelineDraweeControllerBuilder: PipelineDraweeControllerBuilder=Fresco.newDraweeControllerBuilder() pipelineDraweeControllerBuilder.autoPlayAnimations = mySimpleDraweeViewParams!!.autoPlayAnimation pipelineDraweeControllerBuilder.tapToRetryEnabled = true pipelineDraweeControllerBuilder.controllerListener = mySimpleDraweeViewParams!!.controllerListener pipelineDraweeControllerBuilder.imageRequest=getImageRequest(url) image_fresco3!!.setController(pipelineDraweeControllerBuilder.build())
Note that only when tapToRetryEnabled is set to true will a click-retry layer appear, and after more than four retries, the failed layer will be displayed.
Production of Progress Bar
There are currently three styles for progress bars
- System default linear progress bar
hierarchy.setProgressBarImage(ProgressBarDrawable())
- Any picture, using rotation animation
Use this xml directly<?xml version="1.0" encoding="utf-8"?> <animated-rotate xmlns:android="http://schemas.android.com/apk/res/android" android:drawable="@drawable/load_progress_8" android:pivotX="50%" android:pivotY="50%"> </animated-rotate>
val drawable: Drawable = ContextCompat.getDrawable(context, R.drawable.ani_rotate) hierarchy.setProgressBarImage(AutoRotateDrawable(drawable, 1000))
-
Customize Drawable. Modify the current canvas by onLevelChange callback method
class LoadingProgressDrawable() : Drawable() { val images: IntArray = intArrayOf(R.drawable.load_progress_0, R.drawable.load_progress_1, R.drawable.load_progress_2, R.drawable.load_progress_3, R.drawable.load_progress_4, R.drawable.load_progress_5, R.drawable.load_progress_6, R.drawable.load_progress_7, R.drawable.load_progress_8, R.drawable.load_progress_9, R.drawable.load_progress_10, R.drawable.load_progress_11, R.drawable.load_progress_12) var mLevel: Int = 0 var context: Context? = null var options: BitmapFactory.Options? = null var paint: Paint? = null constructor(context: Context) : this() { this.context = context options = BitmapFactory.Options() paint = Paint() paint!!.isAntiAlias = true } override fun draw(p0: Canvas?) { val bmp: Bitmap = BitmapFactory.decodeResource(context!!.resources, images[getIndex()], options) val imageWidth = bmp.width val imageHeight = bmp.height val drawableWidth = bounds.width() val drawableHeight = bounds.height() val left = (drawableWidth-imageWidth)/2 val top = (drawableHeight-imageHeight)/2 p0!!.drawBitmap(bmp, left.toFloat(), top.toFloat(), paint!!) } override fun setAlpha(p0: Int) { paint!!.alpha = p0 } override fun getOpacity(): Int { return PixelFormat.TRANSPARENT } override fun setColorFilter(p0: ColorFilter?) { paint!!.setColorFilter(p0) } override fun onLevelChange(level: Int): Boolean { this.mLevel = level invalidateSelf() return true } fun getIndex(): Int { var index: Int = mLevel/1000 if (index<0) { index=0 } if (index>images.size-1) { index=images.size-1 } return index } }
There's nothing to say about this.
Progressive Image Loading
Progressive graphics is a loading mode from blurred to clear. Fresco only supports network pictures with JPEG file type, because local pictures are decoded at one time, so local pictures do not need to be progressive. In fresco, you can set a definition standard so that it will be displayed on a placeholder map until it reaches that standard.
This standard is achieved by implementing the Progressive JpegConfig interface
class ProgressiveJpegConfigClass : ProgressiveJpegConfig { override fun getNextScanNumberToDecode(p0: Int): Int { return p0+2 } override fun getQualityInfo(p0: Int): QualityInfo { val isGoodEnough = p0 >= 5 return ImmutableQualityInfo.of(p0, isGoodEnough, false) } }
After the setup is complete, place it in Image Pineline Config and initialize it with fresco
var imagePipelineConfig: ImagePipelineConfig.Builder = ImagePipelineConfig.newBuilder(context) .setProgressiveJpegConfig(ProgressiveJpegConfigClass())
getNextScanNumberToDecode: Returns the next number of scans that need to be decoded
getQualityInfo: Determine how many times to scan before the image can be displayed
Of course, if you want to be lazy, fresco also provides the default progressive loading scheme class, SimpleProgressive JpegConfig, to avoid the trouble of your handwriting.
Finally, just set up an incremental approach to SimpleDraweeView.
imageRequestBuilder.isProgressiveRenderingEnabled = true
Look at the evolutionary process.
Picture Loading Event Monitor
private class DefaultBaseControllerListener : BaseControllerListener<ImageInfo>() { // Method of triggering when picture loading is successful override fun onFinalImageSet(id: String?, imageInfo: ImageInfo?, animatable: Animatable?) { super.onFinalImageSet(id, imageInfo, animatable) // Download the width of the successfully displayed picture println("width:${imageInfo!!.width} height:${imageInfo!!.height}") } // Callback Method for Loading Progressive Pictures override fun onIntermediateImageSet(id: String?, imageInfo: ImageInfo?) { super.onIntermediateImageSet(id, imageInfo) } // Method of Callback when Picture Loading Failed override fun onFailure(id: String?, throwable: Throwable?) { super.onFailure(id, throwable) } }
Place this listening event in the controller Listener of Pipeline Drawee Controller Builder
var pipelineDraweeControllerBuilder: PipelineDraweeControllerBuilder=Fresco.newDraweeControllerBuilder() pipelineDraweeControllerBuilder.controllerListener = mySimpleDraweeViewParams!!.controllerListener
Additional processing of pictures
Sometimes, we want to render the loaded image. Although bitmap can be obtained in onFinal Image Set just now, this method is certainly not suitable. Fortunately, fresco provides Postprocessor to implement this function. Here's an example of using a post-processor to achieve simple scaling-up and tailoring
class CutProcess(val mBeginXPercent: Float, val mBeginYPercent: Float, val mCutWidthPercent: Float, val mCutHeightPercent: Float) : BasePostprocessor() { override fun process(sourceBitmap: Bitmap?, bitmapFactory: PlatformBitmapFactory?): CloseableReference<Bitmap>? { val width = sourceBitmap!!.width val height = sourceBitmap!!.height val beginX = width*mBeginXPercent val beginY = height*mBeginYPercent val endX = width*mCutWidthPercent val endY = height*mCutHeightPercent val bmp: CloseableReference<Bitmap> = bitmapFactory!!.createBitmap(scale(sourceBitmap), beginX.toInt(), beginY.toInt(), endX.toInt(), endY.toInt()) return CloseableReference.cloneOrNull(bmp) } fun scale(sourceBitmap: Bitmap?): Bitmap { val matrix: Matrix = Matrix() matrix.postScale(1.1f, 1.1f, 0.5f, 0.5f) return Bitmap.createBitmap(sourceBitmap!!, 0, 0, sourceBitmap!!.width, sourceBitmap!!.height, matrix, true) } }
Add it directly to ImageRequest when you use it
val imageRequest: ImageRequest=ImageRequestBuilder .newBuilderWithSource(Uri.parse("res:///"+R.mipmap.rotate)) .setRotationOptions(RotationOptions.forceRotation(0)) .setPostprocessor(CutProcess(0f, 0f, 0.5f, 0.5f)).build()
Here's an extra tip. We just clipped the image through createBitmap, but android also provides a BitmapRegionDecoder class, which allows us to decode a rectangular area of a picture.
fun getBitmapArray(bitmap: Bitmap, quality: Int) : ByteArray { var os: ByteArrayOutputStream = ByteArrayOutputStream() bitmap.compress(Bitmap.CompressFormat.JPEG, quality, os) return os.toByteArray() } /** * Get Bitmap in rect range */ fun getDecodeRegion(bytes: ByteArray, width: Int, height: Int) : Bitmap { val decodeRegion: BitmapRegionDecoder = BitmapRegionDecoder.newInstance(bytes, 0, bytes.size, true) val rect = Rect(0, 0, width, height) return decodeRegion.decodeRegion(rect, null) }
This allows you to get a rect image directly
val bmp: CloseableReference<Bitmap> = bitmapFactory!!.createBitmap(CommonUtils.getDecodeRegion(CommonUtils.getBitmapArray(sourceBitmap, 100), sourceBitmap.width/2, sourceBitmap.height/2))
Supporting picture types
type | SCHEME |
---|---|
Remote Pictures | http://, https:// |
Local files | file:// |
Content provider | content:// |
Resources under asset directory | asset:// |
Resources under res directory | res:// |
Specify image data in Uri | data:mime/type;base64, data type must comply with rfc2397 (UTF-8 only) |
Control of Animation Events
Previously, we set up autoPlayAnimations in pipeline Drawee Controller Builder to allow fresco to automatically handle this broadcast event. So how can we control the start and stop of this animation by ourselves?
Look at the previous download listener. In the callback completed by the download, there is an Animatable attribute, which can be used to manipulate the animation.
private class DefaultBaseControllerListener : BaseControllerListener<ImageInfo>() { override fun onFinalImageSet(id: String?, imageInfo: ImageInfo?, animatable: Animatable?) }
But if this property is to be used in an external class, is it necessary to declare a global variable? fresco is certainly not such a fool. We can get the Animatable through pipeline Drawee Controller, so that we can control the animation flexibly.
image_fresco2!!.controller.animatable
Multi-graph Request and Image Multiplexing
First let's talk about the requirements according to the scenario.
- Sometimes, in order to load pictures faster, the background specially prepared different sizes of pictures for us. In this way, we will first display low-resolution images, and then display high-resolution images. How to deal with this in fresco? Simply configure the setLowRes ImageRequest property in the pipeline DraweeController Builder and add the image Request of the small graph as well.
val draweeController: DraweeController=Fresco.newDraweeControllerBuilder() .setLowResImageRequest(ImageRequest.fromUri(Uri.parse("res:///"+R.mipmap.rotate))) .setImageRequest(imageRequest).setAutoPlayAnimations(true) .setOldController(image_fresco3!!.controller).build() image_fresco3!!.controller=draweeController
- Thumbnail
If the local JPEG diagram has an EXIF thumbnail, pipeline can immediately return the thumbnail. DraweeView displays thumbnails first and then a full, clear large image. This feature is limited, supports only local URI s, and is in JPEG image format. This is also very simple, just configure setLocalThumbnail Preview Enabled (true) in the image Request.val imageRequest: ImageRequest=ImageRequestBuilder .newBuilderWithSource(Uri.parse("file:///storage/emulated/0/DCIM/Camera/20170529_163007.jpg")) .setLocalThumbnailPreviewsEnabled(true)
- If it is a social app, it usually has the function of mapping. In this way, local pictures and network pictures may coexist. The ideal process is, of course, to load the local image first, and then the network image if the local image does not exist. But if you add this judgment to every call, maybe the code is more cumbersome. Fortunately, fresco has helped us a lot. It's our invincible Pueline Drawee Controller Builder that provides the setFirstAvailable ImageRequests method, where ImageRequest is loaded first for anyone who is fast.
val requests: Array<ImageRequest> = arrayOf(ImageRequest.fromUri(Uri.parse("res:///"+R.mipmap.rotate)), imageRequest) val draweeController: DraweeController=Fresco.newDraweeControllerBuilder() .setFirstAvailableImageRequests(requests) .setOldController(image_fresco3!!.controller).build() image_fresco3!!.controller=draweeController
Get only pictures
Get the successfully loaded image through the BaseBitmapDataSubscriber callback
var dataSource: DataSource<CloseableReference<CloseableImage>> = Fresco.getImagePipeline().fetchDecodedImage(ImageRequest.fromUri("xxx.jpg"), this) dataSource.subscribe(CustomerBitmapDataSubscriber(), CallerThreadExecutor.getInstance())
class CustomerBitmapDataSubscriber : BaseBitmapDataSubscriber() { override fun onFailureImpl(p0: DataSource<CloseableReference<CloseableImage>>?) { } override fun onNewResultImpl(p0: Bitmap?) { println("Achieving Success") } }
One thing to note is that because this bitmap also exists in Fresco's cache, it may be quickly recycled by the system and needs to be copied and saved by itself.
Clear Cache
Consistent with previous loading logic, cache cleaning is also divided into two directions: memory and disk.
We haven't configured CacheKeyFactory before, so we can use evictFromXXX directly to delete the specified url. If you configure it yourself, you need to use Image Request to delete it.
Fresco.getImagePipeline().evictFromMemoryCache(url) Fresco.getImagePipeline().evictFromDiskCache(url) Fresco.getImagePipeline().evictFromCache(url)
Finally, all caches are cleared
Fresco.getImagePipeline().clearMemoryCaches() Fresco.getImagePipeline().clearDiskCaches() Fresco.getImagePipeline().clearCaches()
On OOM
The improper use of Fresco will frequently lead to the occurrence of OOM, at least I have encountered it. Here are two suggestions for you:
- Please configure the setResizeOptions(resizeOptions) attribute on the default configuration of ImageRequest, which has been explained many times in this article. For pages with a large number of pictures, such as photo walls, the size of pictures must be limited; network pictures can be processed by the server to the size, quality and type of pictures before returning to the client, but for local pictures of mobile phones, only setResize Options can effectively reduce the memory cache overhead.
- Manual release of memory caches in low memory or out of multi-graph pages: