Chapter 6 Pictures

Article Directory

Chapter 6 Pictures

(1) Android loading big picture

(1) Why do you need to deal with the loading of large images separately

The limited memory loads infinite pictures, which leads to OOM:
1. The pictures displayed in the picture library (mostly taken by the camera of the mobile phone) have a much higher resolution than the screen resolution of the mobile phone. The pictures should be compressed and the controls used for display The size is similar, otherwise it will occupy memory
2. Android decodes the picture: picture size = total number of pixels of the picture * size of each pixel of the picture

(2) Solution

1. Parse the bitmap according to the zoom ratio

First obtain the size of the screen, then obtain the size of the picture, and finally calculate the zoom ratio of the screen and the picture, and parse the bitmap according to the zoom ratio.

//1.获取手机的分辨率  获取windowmanager 实例
WindowManager wm  = (WindowManager) getSystemService(WINDOW_SERVICE);     screenWidth = wm.getDefaultDisplay().getWidth();
screenHeight = wm.getDefaultDisplay().getHeight();
//2.把xxxx.jpg 转换成bitmap
//创建bitmap工厂的配置参数
BitmapFactory.Options options = new Options();          
//=true返回一个null 没有bitmap:不去为bitmap分配内存 但是能返回图片的一些信息(宽和高)     options.inJustDecodeBounds = true;     BitmapFactory.decodeFile("/mnt/sdcard/xxxx.jpg",options);     
//3.获取图片的宽和高       
int imgWidth = options.outWidth;     
int imgHeight = options.outHeight;
 //4.计算缩放比          
int scalex =  imgWidth/screenWidth;     
int scaley = imgHeight /screenHeight;
scale =min(scalex, scaley,scale);            
//5.按照缩放比显示图片,inSampleSize给图片赋予缩放比,其值大于1时,会按缩放比返回一个小图片用来节省内存   
options.inSampleSize = scale;          
//6.=false开始真正的解析位图,可显示位图 
options.inJustDecodeBounds = false;     
Bitmap bitmap = BitmapFactory.decodeFile("/mnt/sdcard/dog.jpg",options);         
//7.把bitmap显示到控件上     
iv.setImageBitmap(bitmap);   } }

2. Image loading in blocks

The block loading of the picture is most obvious in the case of map drawing. When you want to get a small block area of ​​a large-size picture, the block loading of the picture is used. The function of the BitmapRegionDecoder class in Android is to load The designated area of ​​a picture.

3. LruCache caching mechanism

(2) Android picture three-level cache mechanism

The three-level cache mechanism of an image generally refers to an act of accessing memory, files, and the network to obtain image data when an application loads an image.

1. Flowchart of Level 3 Cache

Insert picture description here

2. Level 1: Memory LruCache

LruCache is a cache tool class provided by Android, and its algorithm is the least recently used algorithm. It stores the most recently used objects in LinkedHashMap with "strong references", and removes the least recently used objects from memory before the cached value reaches the preset value.
LinkedHashMap: The combination of HashMap and doubly linked list is LinkedHashMap. It guarantees the iteration order by maintaining an additional doubly linked list. In particular, the iteration order may be the insertion order or the access order.

(2.1) Source code analysis

/**
 * 它的主要算法原理是把最近使用的对象用强引用存储在 LinkedHashMap 中,并且把最近最少使用的对象在缓存值达到预设定值之前从内存中移除。(最近使用的数据在尾部,老数据在头部)
 */
public class LruCache<K, V> {
private final LinkedHashMap<K, V> map;
     public LruCache(int maxSize) {
         if (maxSize <= 0) {
             throw new IllegalArgumentException("maxSize <= 0");
         }
         this.maxSize = maxSize;         
// 注意第三个参数,是accessOrder=true,表示linkedHashMap为访问顺序,当对已存在LinkedHashMap的Entry进行get和put操作时,会把Entry移动到双向链表的表头
         this.map = new LinkedHashMap<K, V>(0, 0.75f, true);     
}
LruCache的put方法
public final V put(K key, V value) {       
	V previous;
         // 对map进行操作之前,先进行同步操作(HashMap是线程不安全的,应该先进行同步操作)
	   //synchronized加锁,表示一次只能有一个方法进入该线程
         synchronized (this) {
             putCount++;
             size += safeSizeOf(key, value);
//向map中加入缓存对象,若缓存中已存在,返回已有的值,否则执行插入新的数据,并返回null,并将缓存恢复为之前的值
             previous = map.put(key, value)
             if (previous != null) {
                 size -= safeSizeOf(key, previous);
             }         
}          
if (previous != null) {
             entryRemoved(false, key, previous, value);
         }
         // 根据缓存大小整理内存,看是否需要移除LinkedHashMap中的元素
         trimToSize(maxSize);
         return previous;     }

trimToSize(maxSize)

public void trimToSize(int maxSize) {
    while (true) {
// while循环,不断移除LinkedHashMap中双向链表表头表头元素(近期最少使用的数据),直到满足当前缓存大小小于或等于最大可缓存大小
//如果当前缓存大小已小于等于最大可缓存大小,则直接返回,不需要再移除LinkedHashMap数据
	if (size <= maxSize || map.isEmpty()) {
     	break; }
//得到双向链表表头header的下一个Entry(近期最少使用的数据=表头数据)
            Map.Entry<K, V> toEvict = map.entrySet().iterator().next();
            key = toEvict.getKey();
            value = toEvict.getValue();
//移除当前取出的Entry并重新计算当前缓存大小
            map.remove(key);
            size -= safeSizeOf(key, value);
            evictionCount++;
        }
        entryRemoved(true, key, value, null);
    }
}

LruCache get method

public final V get(K key) {
//如果该值在缓存中存在或可被创建便返回,当调用LruCache的get()方法获取集合中的缓存对象时,就代表访问了一次该元素,将会更新队列,移动到表尾,这个更新过程就是在LinkedHashMap中的get()方法中完成的。
    V mapValue;
    synchronized (this) {
        mapValue = map.get(key);
        if (mapValue != null) {
            return mapValue;
        }
    }
}

It can be seen from the reordering that put and get operations on LinkedHashMap will move the operated Entry to the end of the doubly linked list. The removal starts from map.entrySet (). Iterator (). Next (), which is after the header of the head of the doubly linked list, which also meets the requirements of the LRU algorithm.

(2.2) Use

1. Initialize the cache class, set the size and rewrite the sizeof () method
/* 内存缓存 */
    private LruCache<String, Bitmap> mMemoryCache = 4*1024*1024;
    //初始化这个cache前需要设定这个cache的大小,这里的大小官方推荐是用当前app可用内存的八分之一
    mMemoryCache = new LruCache<String, Bitmap>(memoryCache) {
            @Override
            protected int sizeOf(String key, Bitmap bitmap) {
                // 重写此方法来衡量每张图片的大小,默认返回图片数量。
                return bitmap.getByteCount() / 1024;
            }
        };
2. Rewrite add / remove cache
    //将bitmap添加到内存中去
    public void addBitmapToMemoryCache(String key, Bitmap bitmap) {
        if (getBitmapFromMemCache(key) == null) {
            mMemoryCache.put(key, bitmap);
        }
    }
    // 通过key来从内存缓存中获得bitmap对象
    private Bitmap getBitmapFromMemCache(String key) {
        return mMemoryCache.get(key);
    }
3. Simulate downloading pictures from the network and adding them to the cache
    private void loadBitmapToImageView(int resId, ImageView imageView) {
        final String imageKey = String.valueOf(resId);
        final Bitmap bitmap = getBitmapFromMemCache(imageKey); // 先看这个资源在不在内存中,如果在直接读取为bitmap,否则返回null
        if (bitmap != null) {
            imageView.setImageBitmap(bitmap);
        } else {
            imageView.setImageResource(R.drawable.ic_launcher); // 如果没有在内存中,先显示默认的图片,然后启动线程去下载图片
            BitmapWorkerTask task = new BitmapWorkerTask(imageView);
            task.execute(resId); // 启动线程,模拟从网络下载图片,下载后加入缓存
        }
}

If there is no cache, continue

3. Level 2: File cache

3.1) Path setting for cache file storage

The storage path must first consider the cache directory of the SD card. When the SD card does not exist, it can only be stored in the internal cache directory.

private File getCacheDir() {
    // 获取缓存路径目录
    File file;
    if (Environment.getExternalStorageState() == Environment.MEDIA_MOUNTED) {
        file = mContext.getExternalCacheDir();        // 有SD卡就保存到sd卡
    } else {
        file = mContext.getCacheDir();        // 没有就保存到内部储存
    }
    return file;
}

3.2) Parse the file to generate a Bitmap object and store it in the cache

private Bitmap getBitmapFromFile() {
    // 根据url中获取文件名字,存储文件的文件名截取URL中的名字,并且文件名用md5加密
    String fileName = url.substring(url.lastIndexOf("/") + 1);
    File file = new File(getCacheDir(),MD5Util.encodeMd5(fileName));
    if (file.exists() && file.length() > 0) { 
          diskBitmap  = BitmapFactory.decodeFile(file.getAbsolutePath());
 	mImageCache.put(url, new SoftReference<Bitmap>(diskBitmap)); 	// 保存到内存中去
	return diskBitmap;
    } else {
        return null;
    }
}

4. Level 3: Network loading

4.1) Simple thread pool handles time-consuming network requests

//构建出5条线程的线程池
private ExecutorService mExecutorService = Executors.newFixedThreadPool(5);

4.2) Network loading data HttpUrlConnection

//用HttpUrlConnection加载网络图片
URL loadUrl = new URL(url);
HttpURLConnection conn = (HttpURLConnection) loadUrl.openConnection();

4.3) Save data to memory and files

mImageCache.put(url, new SoftReference<>(bm));    //保存到内存
String fileName = url.substring(url.lastIndexOf("/") + 1);//从Url中获取文件名字,保存到磁盘
File file = new File(getCacheDir(), MD5Util.encodeMd5(fileName));//获取存储路径
FileOutputStream os = new FileOutputStream(file);
bm.compress(Bitmap.CompressFormat.JPEG, 100, os);//将图片转为文件存储

(3) Glide source code analysis

1. Basic usage

//with(Context/Activity/Fragment)决定Glide加载图片的生命周期
//load(url)url包括网络图片、本地图片、应用资源、二进制流、Uri对象等等(重载)
//into(imageView)
Glide.with(this).load(url).into(imageView);
//扩展功能
.placeholder(R.drawable.loading)//加载图片过程占位符,加载完成会替换占位符
.error(R.drawable.error)//加载图片错误占位符
.asGif()/.asBitmap()只显示动态图/只显示静态图(不设置时,Glide会自动判断图片格式)
.diskCacheStrategy(DiskCacheStrategy.NONE)//禁用Glide缓存机制
.override(100, 100)//指定图片大小(Glide会自动判断ImageView的大小,然后将对应的图片像素加载本地,节省内存开支)

2. Source code analysis of image loading process

Insert picture description here

(2.1)with(Context/Activity/Fragment)

Get a RequestManager object (to achieve the association of request and Activity / Fragment life cycle), Glide then determines the life cycle of the image load according to the parameters of the with () method passed in:
1. Application type parameters-application life cycle
2. Application type parameters-Activity / Fragment life cycle

(2.2)load(url)

Get a DrawableTypeRequest object (extends DrawableRequestBuilder)

(2.3)into(imageView)

Insert picture description here
Insert picture description here

1.DrawableRequestBuilder 父类 GenericRequestBuilder
return into(glide.buildImageViewTarget(view, transcodeClass));

其中glide.buildImageViewTarget(view,transcodeClass):

public interface Target<R> extends LifecycleListener {
//The typical lifecycle is onLoadStarted -> onResourceReady or onLoadFailed -> onLoadCleared.

The carrier of the request, the loading class corresponding to various resources, contains the callback method of the life cycle, which is convenient for the developer to make corresponding preparations and resource recovery during the image loading process.
In the buildTarget () method, different Target objects will be constructed according to the class parameters passed in. If you call the asBitmap () method when using Glide to load the image, then the BitmapImageViewTarget object will be constructed here, otherwise it will be built. Is a GlideDrawableImageViewTarget object.
Through the glide.buildImageViewTarget () method, we construct a GlideDrawableImageViewTarget object. Ie
into (GlideDrawableImageViewTarget)

2.into (target) Set the target of the resource, and create, bind, track, and initiate the request

Insert picture description here

public <Y extends Target<TranscodeType>> Y into(Y target) {
    Request request = buildRequest(target);
    target.setRequest(request);
    lifecycle.addListener(target);
    requestTracker.runRequest(request);
    return target;
}

(1) buildRequest(buildRequestRecursive)

//创建请求,如果配置了thumbnail(缩略图)请求,则构建一个ThumbnailRequestCoordinator(包含了FullRequest和ThumbnailRequest)请求, 
否则调用了obtainRequest()方法来获取一个Request对象,并传入相应的参数(target,sizeMultiplier,priority,requestCoordinator...)
return GenericRequest.obtain(target, sizeMultiplier, priority, parentCoordinator…);

(2) requestTracker.runRequest

public void runRequest(Request request) {
//处理请求
    requests.add(request);
    if (!isPaused) {
        request.begin();//调用GenericRequest的begin方法
    } else {
        pendingRequests.add(request);
    }
}

GenericRequest.begin()

public void begin() {
    if (model == null) { //model为Url,图片路径,Uri等等图片资源模型
        onException(null); //调用setErrorPlaceholder(),将error占位符填入imageView上
        return;
    }
    if (Util.isValidDimensions(overrideWidth, overrideHeight)) {
        onSizeReady(overrideWidth, overrideHeight);
    } else {//未指定宽高,则glide根据ImageView的layout_width和layout_height值做一系列的计算,来算出图片应该的宽高(防止OOM),再调用onSizeReady(width,height)
        target.getSize(this);
    }
    if (!isComplete() && !isFailed() && canNotifyStatusChanged()) {
        target.onLoadStarted(getPlaceholderDrawable());
    }
}
public void onSizeReady(int width, int height) {
    width = Math.round(sizeMultiplier * width);
    height = Math.round(sizeMultiplier * height);
//ModelLoader:各种资源model的ModelLoader,主要功能有:1、将任意复杂的model转换为可以被DataFetcher所decode的数据类型2、允许model结合View的尺寸获取特定大小的资源
    ModelLoader<A, T> modelLoader = loadProvider.getModelLoader();
//每次通过ModelLoader加载资源都会创建的实例
    final DataFetcher<T> dataFetcher = modelLoader.getResourceFetcher(model, width, height);
// ResourceTranscoder : 资源转换器,将给定的资源类型,转换为另一种资源类型,比如将Bitmap转换为Drawable,Bitmap转换为Bytes 
    ResourceTranscoder<Z, R> transcoder = loadProvider.getTranscoder();
    loadedFromMemoryCache = true;
    loadStatus = engine.load(signature, width, height, dataFetcher, loadProvider, transformation, transcoder, priority, isMemoryCacheable, diskCacheStrategy, this); //真正开始加载资源
    loadedFromMemoryCache = resource != null;
}
3.Engine is responsible for task creation, initiation, callback, resource management

Engine.load (...)
really starts to load resources, and the load call processing flowchart:
Insert picture description here

public <T, Z, R> LoadStatus load(Key signature, int width, int height, DataFetcher<T> fetcher,
        DataLoadProvider<T, Z> loadProvider, Transformation<Z> transformation, ResourceTranscoder<Z, R> transcoder,
        Priority priority, boolean isMemoryCacheable, DiskCacheStrategy diskCacheStrategy, ResourceCallback cb) {
//创建一个EngineJob:调度DecodeJob,添加,移除资源回调,并notify回调
    EngineJob engineJob = engineJobFactory.build(key, isMemoryCacheable);
//创建一个DecodeJob:调度任务的核心类,整个请求的繁重工作都在这里完成:处理来自缓存或者原始的资源,应用转换动画以及transcode。
负责根据缓存类型获取不同的Generator加载数据,数据加载成功后回调DecodeJob的onDataFetcherReady方法对资源进行处理
    DecodeJob<T, Z, R> decodeJob = new DecodeJob<T, Z, R>(key, width, height, fetcher, loadProvider, transformation, transcoder, diskCacheProvider, diskCacheStrategy, priority);
    EngineJob engineJob = engineJobFactory.build(key, isMemoryCacheable);
//创建一个EngineRunnable implements Runnable
    EngineRunnable runnable = new EngineRunnable(engineJob, decodeJob, priority);
    jobs.put(key, engineJob);
    engineJob.addCallback(cb);
    engineJob.start(runnable); //*
    return new LoadStatus(cb, engineJob);
}

* Run the Run () method of EngineRunnable in the sub-thread => DecodeJob.decodeFromSource () / decodeFromCache ()
(1) decodeSource () // Get a Resource object
1, fetcher.loadData () for network communication to get InputStream, and wrap it into ImageVideoWrapper object

private InputStream loadDataWithRedirects(URL url, int redirects, URL lastUrl, Map<String, String> headers)
        throws IOException {
       urlConnection = connectionFactory.build(url);
       urlConnection.connect();
       return getStreamForSuccessfulRequest(urlConnection);
}

2. decoded = decodeFromSourceData (data) to decode the ImageVideoWrapper object (the encapsulation class of the stream); including the logical processing of image compression, rotation, fillet, etc., to get the loaded image object Bitmap, and layer by layer packaging BitmapResource (Resource) -> GifBitmapWrapper-> GifBitmapWrapperResource (Resource) enables the loaded image to be returned in the form of Resource interface, and can handle both Bitmap and gif images

public Bitmap decode(InputStream is, BitmapPool pool, int outWidth, int outHeight, DecodeFormat decodeFormat) {
        final Bitmap downsampled =downsampleWithSize(invalidatingStream, bufferedStream, options, pool, inWidth, inHeight, sampleSize, decodeFormat);

}
4.transformEncodeAndTranscode(decoded)

Process this Resource object and convert it into a Resource object that can be displayed in ImageView

5. Get a displayable Resource object and call EngineJob.onResourceReady ()
public void onResourceReady(final Resource<?> resource) {
    this.resource = resource;
//使用Handler发出了一条MSG_COMPLETE消息
    MAIN_THREAD_HANDLER.obtainMessage(MSG_COMPLETE, this).sendToTarget();
}
private static class MainThreadCallback implements Handler.Callback {
//在MainThreadCallback的handleMessage()方法中就会收到MSG_COMPLETE消息。从这里开始,所有的逻辑又回到主线程当中进行了,因为很快就需要更新UI了
    @Override
    public boolean handleMessage(Message message) {
        if (MSG_COMPLETE == message.what || MSG_EXCEPTION == message.what) {
            EngineJob job = (EngineJob) message.obj;
            if (MSG_COMPLETE == message.what) {
                job.handleResultOnMainThread();
            } else {
                job.handleExceptionOnMainThread();
            }
            return true;
        }
        return false;
    }
}

Finally, the setResource () method is called in the onResourceReady () method of ImageViewTarget, that is, the ImageView.setImageDrawable () method is called, and the picture finally shows

3. Glide cache mechanism

(3.1) Related usage of Glide cache function

1. Set the memory cache switch

skipMemoryCache(true)

2. Set the disk cache mode

diskCacheStrategy(DiskCacheStrategy.NONE)

4 modes can be set:

DiskCacheStrategy.NONE: indicates that nothing is cached.
DiskCacheStrategy.SOURCE: indicates that only the original picture is cached.
DiskCacheStrategy.RESULT: indicates that only converted pictures are cached (default option).
DiskCacheStrategy.ALL: means to cache both the original image and the converted image.

(3.2) Glide cache source code analysis

3.2.1) Design ideas

The operation of the memory cache should be before asynchronous processing, and the disk cache is time-consuming operation should be completed in asynchronous processing. The reading and storing of Glide's memory and cache are done in the Engine class.
The memory cache uses weak references (ActiveCache) and LruCache (Cache). The weak references cache the pictures in use. There is a counter inside the picture packaging resources to determine whether the picture is in use.

3.2.2) Source code analysis
(1) Glide memory cache source code analysis

Process:
read: first fetch from lruCache, then fetch from weak reference;
store:
fetch from memory cache, pull from network and put in weak reference first, render picture, picture object Resources usage count plus one , Count> 0, in use; after
rendering the picture, the picture object Resources usage count is decremented by one; if the count is 0, the picture cache is deleted from the weak reference and placed in the lruCache cache.
1. Read the picture from the memory cache
The entry method of the Engine in the loading process is the load method

public class Engine implements EngineJobListener,
        MemoryCache.ResourceRemovedListener,
        EngineResource.ResourceListener {
…
public <T, Z, R> LoadStatus load(Key signature, int width, int height, DataFetcher<T> fetcher,
        DataLoadProvider<T, Z> loadProvider, Transformation<Z> transformation, ResourceTranscoder<Z, R> transcoder,
        Priority priority, boolean isMemoryCacheable, DiskCacheStrategy diskCacheStrategy, ResourceCallback cb) {
    Util.assertMainThread();
    long startTime = LogTime.getLogTime();

    final String id = fetcher.getId();
//生成缓存的key    
EngineKey key = keyFactory.buildKey(id, signature, width, height, loadProvider.getCacheDecoder(),
            loadProvider.getSourceDecoder(), transformation, loadProvider.getEncoder(),
            transcoder, loadProvider.getSourceEncoder());
//从LruCache获取缓存图片  
    EngineResource<?> cached = loadFromCache(key, isMemoryCacheable);
    if (cached != null) {
        cb.onResourceReady(cached);
        if (Log.isLoggable(TAG, Log.VERBOSE)) {
            logWithTimeAndKey("Loaded resource from cache", startTime, key);
        }
        return null;
    }
//从弱引用获取缓存图片
    EngineResource<?> active = loadFromActiveResources(key, isMemoryCacheable);
    if (active != null) {
        cb.onResourceReady(active);
        if (Log.isLoggable(TAG, Log.VERBOSE)) {
            logWithTimeAndKey("Loaded resource from active resources", startTime, key);
        }
        return null;
    }
…//进行异步处理
}

The main process of reading pictures from the memory cache:
generate the cache key-> get the cache picture from LruCache-> LruCache did not get it, get the picture from the weak reference-> memory cache can not get it, enter asynchronous processing.
Two methods loadFromCache () and loadFromActiveResources () for mixed accessing pictures from memory. loadFromCache uses the LruCache algorithm, and loadFromActiveResources uses weak references.

public class Engine implements EngineJobListener,
        MemoryCache.ResourceRemovedListener,
        EngineResource.ResourceListener {
private final MemoryCache cache;
private final Map<Key, WeakReference<EngineResource<?>>> activeResources;

private EngineResource<?> loadFromCache(Key key, boolean isMemoryCacheable) {
    if (!isMemoryCacheable) {//skipMemoryCache()方法设置是否内存缓存被禁用
        return null;
    }
    EngineResource<?> cached = getEngineResourceFromCache(key); //从LruCache获取图片缓存
if (cached != null) {
//从LruResourceCache中获取到缓存图片之后会将它从缓存中移除,将缓存图片存储到activeResources当中。activeResources就是弱引用的HashMap,用来缓存正在使用中的图片。
        cached.acquire();
        activeResources.put(key, new ResourceWeakReference(key, cached, getReferenceQueue()));
    }
    return cached;
}
private EngineResource<?> loadFromActiveResources(Key key, boolean isMemoryCacheable) {
    if (!isMemoryCacheable) {
        return null;
    }
    EngineResource<?> active = null;
    WeakReference<EngineResource<?>> activeRef = activeResources.get(key);
    if (activeRef != null) {
        active = activeRef.get();
        if (active != null) {
            active.acquire();
        } else {
            activeResources.remove(key);
        }
    }
    return active;
}

2. Store the picture in the memory cache
EngineJob will call the Engine's onEngineJobComplete () to get the picture being loaded into the weak reference cache after getting the picture

public void onEngineJobComplete(Key key, EngineResource<?> resource) {
    if (resource != null) {
        resource.setResourceListener(key, this);
        if (resource.isCacheable()) {
            activeResources.put(key, new ResourceWeakReference(key, resource, getReferenceQueue()));//将正在加载的图片放到弱引用缓存
        }
    }
    jobs.remove(key);
}

EngineResource uses an acquired variable to record the number of times the picture is referenced. Calling the acquire () method will increase the variable by 1 and calling the release () method will decrease the variable by 1. When the reference count acquired variable is 0, it means that the picture is used up and should be placed in LruCache. Here listener.onResourceReleased (key, this) is called; this listener is the Engine object.

public void onResourceReleased(Key cacheKey, EngineResource resource) {
    activeResources.remove(cacheKey); //从弱引用中删除图片缓存
    if (resource.isCacheable()) {
        cache.put(cacheKey, resource); //如果支持缓存,则缓存到LruCache
    } else {
        resourceRecycler.recycle(resource); //不支持缓存直接调用垃圾回收,回收图片
    }
}
(2) Source analysis of Glide disk cache

Read: Find the result picture first, then find the original picture if not.
Save: Save the original image first, and then save the processed image.
1. Read pictures from disk cache
EngineRunnable's run () method-> decode () method

private Resource<?> decode() throws Exception {
    if (isDecodingFromCache()) {//从磁盘缓存读取图片
        return decodeFromCache();
    } else {//从原始位置读取图片
        return decodeFromSource();
    }
}

2. Store the picture in the disk cache
and store the picture after getting the picture

public Resource<Z> decodeFromSource() throws Exception {
    Resource<T> decoded = decodeSource();
    return transformEncodeAndTranscode(decoded);
}

(1) Save the original picture

private Resource<T> decodeSource() throws Exception {
        decoded = decodeFromSourceData(data);
}
private Resource<T> decodeFromSourceData(A data) throws IOException {
    final Resource<T> decoded;
    if (diskCacheStrategy.cacheSource()) {//设置是否缓存原图
        decoded = cacheAndDecodeSourceData(data);
    } else {
        decoded = loadProvider.getSourceDecoder().decode(data, width, height);
    }
    return decoded;
}
private Resource<T> cacheAndDecodeSourceData(A data) throws IOException {
    SourceWriter<A> writer = new SourceWriter<A>(loadProvider.getSourceEncoder(), data);
//获取DiskCache工具类并写入缓存
    diskCacheProvider.getDiskCache().put(resultKey.getOriginalKey(), writer);
    Resource<T> result = loadFromCache(resultKey.getOriginalKey());
    return result;
}

(2) Save the processed
picture After the picture is processed, save the processed picture in the transformEncodeAndTranscode () method

private Resource<Z> transformEncodeAndTranscode(Resource<T> decoded) {
    Resource<T> transformed = transform(decoded);
    writeTransformedToCache(transformed);
    Resource<Z> result = transcode(transformed);
    return result;
}
private void writeTransformedToCache(Resource<T> transformed) {
    SourceWriter<Resource<T>> writer = new SourceWriter<Resource<T>>(loadProvider.getEncoder(), transformed);
    diskCacheProvider.getDiskCache().put(resultKey, writer); //获取DiskCache实例并写入缓存
}

4. Glide framework summary

(1) Main flow chart

Insert picture description here

(2) Three major events

1. Prepare the data

(1.1) Construction of GenericRequest object-Glide configuration
(1.2) Construction of decodeJob object-asynchronous processing of core objects

2. Asynchronous processing

(1) Initiate a network request and get the data stream
(2) Decode the data stream into a bitmap object
(3) Transcode the bitmap into a drawable object

3. Switch to the main thread and display Drawable in ImageView

(3) Refinement of main process-timing chart

Published 74 original articles · won 15 · views 6255

Guess you like

Origin blog.csdn.net/qq_29966203/article/details/90473451