Android ImageLoader框架之圖片載入與載入策略
在Android ImageLoader框架之初始配置與請求排程中,我們已經講述了ImageLoader的請求配置與排程相關的設計與實現。今天我們就來深入瞭解圖片的具體載入過程以及載入的策略(包括按順序載入和逆序載入) ,在這其中我會分享我的一些設計決策,也歡迎大家給我提建議。
圖片的載入
Loader與LoaderManager的實現
在上一篇文章Android ImageLoader框架之初始配置與請求排程中,我們聊到了Loader與LoaderManager。 ImageLoader不斷地從佇列中獲取請求,然後解析到圖片uri的schema,從schema的格式就可以知道它是儲存在哪裡的圖片。例如網路圖片物件的schema是http或者https,sd卡儲存的圖片對應的schema為file,schemae與Loader有一個對應關係。根據schema我們從LoaderManager中獲取對應的Loader來載入圖片。這個設計保證了SimpleImageLoader可載入圖片型別的可擴充套件性,這就是為什麼會增加loader這個包的原因。使用者只需要根據uri的格式來構造圖片uri,並且實現自己的Loader類,然後將Loader物件注入到LoaderManager即可。RequestDispatcher中的run函式如下 :
@Override public void run() { try { while (!this.isInterrupted()) { final BitmapRequest request = mRequestQueue.take(); if (request.isCancel) { continue; } final String schema = parseSchema(request.imageUri); // 根據schema獲取loader Loader imageLoader = LoaderManager.getInstance().getLoader(schema); imageLoader.loadImage(request); } } catch (InterruptedException e) { Log.i("", "### 請求分發器退出"); } }
Loader只定義了一個介面,只用一個載入圖片的方法。
public interface Loader { public void loadImage(BitmapRequest result); }
抽象是為了可擴充套件,定義這個介面,我們就可以注入自己的圖片載入實現類。例如從資源、assets中載入。不管從網路還是本地載入圖片,我們載入圖片的過程有如下幾個步驟:
- 判斷快取中是否含有該圖片;
- 如果有則將圖片直接投遞到UI執行緒,並且更新UI;
- 如果沒有快取,則從對應的地方獲取到圖片,並且將圖片快取起來,然後再將結果投遞給UI執行緒,更新UI;
我們可以發現,不管從哪裡載入圖片,這些邏輯都是通用的,因此我抽象了一個AbsLoader類。它將這幾個過程抽象起來,只將變化的部分交給子類處理,就相當於AbsLoader封裝了一個邏輯框架( 可以思考用了什麼設計模式),大致程式碼如下 :
/** * @author mrsimple */ public abstract class AbsLoader implements Loader { /** * 圖片快取 */ private static BitmapCache mCache = SimpleImageLoader.getInstance().getConfig().bitmapCache; @Override public final void loadImage(BitmapRequest request) { // 1、從快取中獲取 Bitmap resultBitmap = mCache.get(request); Log.e("", "### 是否有快取 : " + resultBitmap + ", uri = " + request.imageUri); if (resultBitmap == null) { showLoading(request); // 2、沒有快取,呼叫onLoaderImage載入圖片 resultBitmap = onLoadImage(request); // 3、快取圖片 cacheBitmap(request, resultBitmap); } else { request.justCacheInMem = true; } // 4、將結果投遞到UI執行緒 deliveryToUIThread(request, resultBitmap); } /** 載入圖片的hook方法,留給子類處理 * @param request * @return */ protected abstract Bitmap onLoadImage(BitmapRequest request); // 程式碼省略 }
程式碼邏輯如上所述實現了一個模板函式,變化的部分就是onLoadImage,子類在這裡實現真正的載入圖片的方法。比如從網路上載入圖片。
/** * @author mrsimple */ public class UrlLoader extends AbsLoader { @Override public Bitmap onLoadImage(BitmapRequest request) { final String imageUrl = request.imageUri; FileOutputStream fos = null; InputStream is = null; try { URL url = new URL(imageUrl); final HttpURLConnection conn = (HttpURLConnection) url.openConnection(); is = new BufferedInputStream(conn.getInputStream()); is.mark(is.available()); final InputStream inputStream = is; BitmapDecoder bitmapDecoder = new BitmapDecoder() { @Override public Bitmap decodeBitmapWithOption(Options options) { Bitmap bitmap = BitmapFactory.decodeStream(inputStream, null, options); // if (options.inJustDecodeBounds) { try { inputStream.reset(); } catch (IOException e) { e.printStackTrace(); } } else { // 關閉流 conn.disconnect(); } return bitmap; } }; return bitmapDecoder.decodeBitmap(request.getImageViewWidth(), request.getImageViewHeight()); } catch (Exception e) { } finally { IOUtil.closeQuietly(is); IOUtil.closeQuietly(fos); } return null; } }
在初始化ImageLoader時我們會預設將幾個Loader注入到LoaderManager中,然後在載入圖片時ImageLoader會根據圖片的schema來獲取對應Loader來完成載入功能。
/** * */ private LoaderManager() { register(HTTP, new UrlLoader()); register(HTTPS, new UrlLoader()); register(FILE, new LocalLoader()); }
載入策略
載入策略就是你的圖片載入請求提交以後ImageLoader按照一個什麼規則來載入你的請求。預設就是SerialPolicy策略(FIFO),誰在佇列前面就是誰優先被執行。但是事情往往沒有那麼簡單,我們在ListView滾動時,我們希望最後新增到請求佇列的圖片優先得了載入,因此此時它們就在手機螢幕上,所以我們又新增了一個ReversePolicy策略。咦,對於這種存在各種可能性的部分,我們最不能具體化,還是要抽象!於是我定義了LoadPolicy介面,它的作用是compare兩個請求,以此來規定排序原則。
public interface LoadPolicy { public int compare(BitmapRequest request1, BitmapRequest request2); }
因為我們的請求佇列使用的是優先順序佇列PriorityBlockingQueue,因此我們的BitmapRequest都實現了 Comparable 介面,我們在BitmapRequest的函式中將compareTo委託給LoadPolicy物件的compare。
@Override public int compareTo(BitmapRequest another) { return mLoadPolicy.compare(this, another); }
我們看看預設的載入策略,即按順序載入,先新增到佇列的請求先被執行。
/** * 順序載入策略 * * @author mrsimple */ public class SerialPolicy implements LoadPolicy { @Override public int compare(BitmapRequest request1, BitmapRequest request2) { // 那麼按照新增到佇列的序列號順序來執行 return request1.serialNum - request2.serialNum; } }
逆序載入則為 :
/** * 逆序載入策略,即從最後加入佇列的請求進行載入 * * @author mrsimple */ public class ReversePolicy implements LoadPolicy { @Override public int compare(BitmapRequest request1, BitmapRequest request2) { // 注意Bitmap請求要先執行最晚加入佇列的請求,ImageLoader的策略 return request2.serialNum - request1.serialNum; } }
呵,想想這不是策略模式麼!原來模式無處不在,當你習慣之後你就會發現模式在無形之中已經運用到你的程式碼了。如上所示,策略都是簡單的實現,這個策略只需要在配置ImageLoader時指定就行了,使用者也可以根據自己的需求來實現策略類,並且注入給ImageLoader。這樣就保證了靈活性、可擴充套件性。
總結
通過Loader和LoaderManager保證了可載入圖片來源的擴充套件性,即圖片可以儲存在網路上、sd卡中、res資料夾中等等,實現一個從特定位置載入圖片的Loader,然後給這個Loader註冊一個schema,在載入圖片的時候根據圖片的路徑獲取schema,再通過schema獲取Loader,通過Loader載入圖片。
而圖片的載入策略又通過LoadPolicy這個抽象來定製,使用者可以自行實現載入策略。這樣就保證了靈活性,當然還有後期的圖片快取也是需要同樣的靈活性。和我在公共技術點之物件導向六大原則所說,物件導向的幾大原則最終化為幾個簡單的關鍵字: : 抽象、單一職責、最小化。領悟到了這些思想,我想你的程式碼質量應該會有一個質的提升。
ImageLoader庫,圖片快取肯定必不可少。關於圖片的快取設計,還是那句老話,待我下回講解~
Github地址
相關文章
- Android 圖片載入框架Android框架
- 頁面圖片預載入與懶載入策略
- Android圖片載入框架Fresco使用詳解Android框架
- Android 基礎之圖片載入(二)Android
- Android 高效安全載入圖片Android
- Android 圖片載入庫Glide知其然知其所以然之載入AndroidIDE
- Flutter載入圖片與GlideFlutterIDE
- 圖片載入框架Picasso - 原始碼分析框架原始碼
- 圖片載入框架Picasso原始碼分析框架原始碼
- Android偽圖片載入進度效果Android
- 圖片預載入和懶載入
- Android圖片載入框架Picasso原始碼分析(基於Picasso 2.71828)Android框架原始碼
- Flutter 圖片載入Flutter
- 圖片懶載入
- 圖片載入事件事件
- 預載入圖片
- 如何實現一個圖片載入框架框架
- TestFlight下載App,載入圖片失效。Xcode安裝App,圖片載入正常。APPXCode
- 前端優化之圖片懶載入前端優化
- ECMAScript擴充套件 -12 【圖片的預載入與懶載入】套件
- 載入本地圖片模糊,Glide載入網路圖片卻很清晰地圖IDE
- Android9.0使用Glide載入圖片問題AndroidIDE
- 載入遠端圖片
- 圖片懶載入原理
- 圖片懶載入(IntersectionObserver)Server
- glide圖片載入原理IDE
- Android 載入網路圖片 以及實現圓角圖片效果Android
- 滾動載入圖片(懶載入)實現原理
- ListView 之非同步載入圖片亂序View非同步
- 圖片預載入,圖片懶載入,和jsonp中的一個疑問JSON
- Android常用圖片載入庫介紹及對比Android
- 要優雅!Android中這樣載入大圖片和長圖片Android
- 單張圖片懶載入
- 圖片懶載入實現
- 圖片懶載入踩坑
- 解耦圖片載入庫解耦
- 圖片懶載入大白話
- 通用圖片載入元件UniversalImageLoader元件
- Js圖片懶載入(lazyload)JS