国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 系統 > Android > 正文

Android基于Glide v4.x的圖片加載進度監聽

2019-10-22 18:30:00
字體:
來源:轉載
供稿:網友

Glide是一款優秀的圖片加載框架,簡單的配置便可以使用起來,為開發者省下了很多的功夫。不過,它沒有提供其加載圖片進度的api,對于這樣的需求,實現起來還真頗費一番周折。

嘗試

遇到這個需求,第一反應是網上肯定有人實現過,不妨借鑒一下別人的經驗

Glide加載圖片實現進度條效果

可惜,這個實現是基于3.7版本的,4.0版本以上的glide改動比較大,using函數已經被移除了

using()
The using() API was removed in Glide 4 to encourage users to register their components once with a AppGlideModule to avoid object re-use. Rather than creating a new ModelLoader each time you load an image, you register it once in an AppGlideModule and let Glide inspect your model (the object you pass to load()) to figure out when to use your registered ModelLoader.
To make sure you only use your ModelLoader for certain models, implement handles() as shown above to inspect each model and return true only if your ModelLoader should be used.

思考

又要用最新的版本又希望給其增加功能,魚與熊掌不可兼得?“貪新厭舊”的我怎會輕易放棄。我對加載圖片進度的需求再理了一遍,發現可以用其他思路簡化一下:

  • 加載圖片分為加載本地圖片和網絡圖片
  • 加載本地圖片速度很快,主要耗時在圖片解碼的工作,如果圖片已經在內存中,解碼的步驟也省去了
  • 加載網絡圖片主要時間耗在下載圖片數據過程中

所以,能監聽到圖片的下載進度就大概是圖片的加載進度了。那難道要先下載圖片再交給glide去加載?不用,glide支持整合其他網絡模塊,例如OkHttp,并且如果我們愿意的話,也可以利用其接口實現自己的網絡加載模塊。

glide官方倉庫提供了OkHttp的整合模塊,于是乎我去這些代碼了尋找一下突破口。

突破

OkHttp模塊是非常簡單的,只有4個文件,并且文件都不長

首先,用過glide的都知道,繼承GlideModule的類是用于設置glide的配置信息和加載模塊的,在OkHttpGlideModule里,向系統注冊了一個用于加載GlideUrl類型的組件,簡單的可以理解為加載網絡圖片的組件。

public class OkHttpGlideModule implements GlideModule {  public OkHttpGlideModule() {  }  public void applyOptions(Context context, GlideBuilder builder) {  }  public void registerComponents(Context context, Glide glide, Registry registry) {    registry.replace(GlideUrl.class, InputStream.class, new OkHttpUrlLoader.Factory());  }}

OkHttpLoader文件也很簡單,實現了ModelLoader接口,這里ModelLoader是glide的抽象的資源loader的表示,例如這里,就是說加載GlideUrl的model,返回InputStream,即圖片的輸入流。關于Glide的loadData、model、fetch的詳細介紹,可以查看 官方文檔

OkHttpLoader的最終目的,也就是返回了一個LoadData對象。現在情況明確了,glide框架就是利用這個LoadData對象得到圖片的輸入流,從而下載圖片并經過一系列的解碼,裁剪,緩存等操作,最后加載出來的。LoadData的參數有一個OkHttpStreamFetcher,從名字看來,這里一定就是下載圖片的地方了,我們繼續看下去。

public class OkHttpUrlLoader implements ModelLoader<GlideUrl, InputStream> {  private final okhttp3.Call.Factory client;  public OkHttpUrlLoader(okhttp3.Call.Factory client) {    this.client = client;  }  public boolean handles(GlideUrl url) {    return true;  }  public LoadData<InputStream> buildLoadData(GlideUrl model, int width, int height, Options options) {    //返回LoadData對象,泛型為InputStream    return new LoadData(model, new OkHttpStreamFetcher(this.client, model));  }  public static class Factory implements ModelLoaderFactory<GlideUrl, InputStream> {    private static volatile okhttp3.Call.Factory internalClient;    private okhttp3.Call.Factory client;    private static okhttp3.Call.Factory getInternalClient() {      if(internalClient == null) {        Class var0 = OkHttpUrlLoader.Factory.class;        synchronized(OkHttpUrlLoader.Factory.class) {          if(internalClient == null) {            internalClient = new OkHttpClient();          }        }      }      return internalClient;    }    public Factory() {      this(getInternalClient());    }    public Factory(okhttp3.Call.Factory client) {      this.client = client;    }    public ModelLoader<GlideUrl, InputStream> build(MultiModelLoaderFactory multiFactory) {      return new OkHttpUrlLoader(this.client);    }    public void teardown() {    }  }}

可以看到,所有的重點都在loadData函數里面了,用過OkHttp的同學,有沒有覺得好簡單?哈哈。

這里直接得到圖片的InputStream然后通過回調函數callback.onDataReady()返回了

問題來了,callback的glide框架里傳過來的,我們并不能控制,我嘗試找了一下調用loadData的地方,結果沒有找到。怎么辦?看最終的實現吧。

public class OkHttpStreamFetcher implements DataFetcher<InputStream> {  private static final String TAG = "OkHttpFetcher";  private final Factory client;  private final GlideUrl url;  InputStream stream;  ResponseBody responseBody;  private volatile Call call;  public OkHttpStreamFetcher(Factory client, GlideUrl url) {    this.client = client;    this.url = url;  }  public void loadData(Priority priority, final DataCallback<? super InputStream> callback) {    Builder requestBuilder = (new Builder()).url(this.url.toStringUrl());    Iterator request = this.url.getHeaders().entrySet().iterator();    while(request.hasNext()) {      Entry headerEntry = (Entry)request.next();      String key = (String)headerEntry.getKey();      requestBuilder.addHeader(key, (String)headerEntry.getValue());    }    Request request1 = requestBuilder.build();    this.call = this.client.newCall(request1);    this.call.enqueue(new Callback() {      public void onFailure(Call call, IOException e) {        if(Log.isLoggable("OkHttpFetcher", 3)) {          Log.d("OkHttpFetcher", "OkHttp failed to obtain result", e);        }        callback.onLoadFailed(e);      }      public void onResponse(Call call, Response response) throws IOException {        OkHttpStreamFetcher.this.responseBody = response.body();        if(response.isSuccessful()) {          long contentLength = OkHttpStreamFetcher.this.responseBody.contentLength();          OkHttpStreamFetcher.this.stream = ContentLengthInputStream.obtain(OkHttpStreamFetcher.this.responseBody.byteStream(), contentLength);          callback.onDataReady(OkHttpStreamFetcher.this.stream);        } else {          callback.onLoadFailed(new HttpException(response.message(), response.code()));        }      }    });  }  public void cleanup() {    try {      if(this.stream != null) {        this.stream.close();      }    } catch (IOException var2) {      ;    }    if(this.responseBody != null) {      this.responseBody.close();    }  }  public void cancel() {    Call local = this.call;    if(local != null) {      local.cancel();    }  }  public Class<InputStream> getDataClass() {    return InputStream.class;  }  public DataSource getDataSource() {    return DataSource.REMOTE;  }}

實現

在loadData函數里,有這樣一句代碼

 

復制代碼 代碼如下:

OkHttpStreamFetcher.this.stream = ContentLengthInputStream.obtain(OkHttpStreamFetcher.this.responseBody.byteStream(), contentLength);

 

ContentLengthInputStream是glide的工具類,用來讀取輸入流的,點進去看看,發現有幾個read方法

  public synchronized int read() throws IOException {    int value = super.read();    this.checkReadSoFarOrThrow(value >= 0?1:-1);    return value;  }  public int read(byte[] buffer) throws IOException {    return this.read(buffer, 0, buffer.length);  }  public synchronized int read(byte[] buffer, int byteOffset, int byteCount) throws IOException {    return this.checkReadSoFarOrThrow(super.read(buffer, byteOffset, byteCount));  }

所以,我的解決方法就是在這里計算下載進度和進行進度的報告的,把這個類從源碼里拷貝出來,替換掉OkHttp模塊里面的ContentLengthInputStream,并在這插入自己的下載進度邏輯就行了。

具體的代碼實現就不貼出來了,寫個大概的思路:

下載圖片的時候傳入圖片進度監聽,用集合容器,例如map保存監聽的實例,key為url,下載過程中計算下載進度后通過url找到對應的回調返回進度,圖片加載完畢后將回調從集合了移除(glide提供了圖片加載完畢的回調)。

ok,就這樣,有不合理的地方歡迎指出,又更好更優雅的實現方式請不吝指教。也希望大家多多支持VEVB武林網。


注:相關教程知識閱讀請移步到Android開發頻道。
發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 揭阳市| 贺州市| 宁蒗| 西华县| 华池县| 观塘区| 罗源县| 义乌市| 潜山县| 阿拉善右旗| 勐海县| 上栗县| 武隆县| 永靖县| 昌图县| 固原市| 荣昌县| 利川市| 南部县| 梅州市| 大竹县| 利辛县| 莱西市| 长治县| 平山县| 平利县| 湛江市| 新郑市| 皋兰县| 左云县| 抚远县| 和硕县| 沂南县| 米林县| 房产| 罗城| 桂东县| 汾西县| 来凤县| 赤水市| 汤阴县|