溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

Android基于Glide v4.x如何實現(xiàn)圖片加載進度監(jiān)聽

發(fā)布時間:2021-08-05 14:11:06 來源:億速云 閱讀:223 作者:小新 欄目:移動開發(fā)

這篇文章主要介紹Android基于Glide v4.x如何實現(xiàn)圖片加載進度監(jiān)聽,文中介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們一定要看完!

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

嘗試

遇到這個需求,第一反應(yīng)是網(wǎng)上肯定有人實現(xiàn)過,不妨借鑒一下別人的經(jīng)驗。

Glide加載圖片實現(xiàn)進度條效果

可惜,這個實現(xiàn)是基于3.7版本的,4.0版本以上的glide改動比較大,using函數(shù)已經(jīng)被移除了

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.

思考

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

  • 加載圖片分為加載本地圖片和網(wǎng)絡(luò)圖片

  • 加載本地圖片速度很快,主要耗時在圖片解碼的工作,如果圖片已經(jīng)在內(nèi)存中,解碼的步驟也省去了

  • 加載網(wǎng)絡(luò)圖片主要時間耗在下載圖片數(shù)據(jù)過程中

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

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

突破

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

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

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文件也很簡單,實現(xiàn)了ModelLoader接口,這里ModelLoader是glide的抽象的資源loader的表示,例如這里,就是說加載GlideUrl的model,返回InputStream,即圖片的輸入流。關(guān)于Glide的loadData、model、fetch的詳細介紹,可以查看 官方文檔

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

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函數(shù)里面了,用過OkHttp的同學(xué),有沒有覺得好簡單?哈哈。

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

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

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;
  }
}

實現(xiàn)

在loadData函數(shù)里,有這樣一句代碼

復(fù)制代碼 代碼如下:


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

ContentLengthInputStream是glide的工具類,用來讀取輸入流的,點進去看看,發(fā)現(xiàn)有幾個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,并在這插入自己的下載進度邏輯就行了。

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

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

ok,就這樣,有不合理的地方歡迎指出,又更好更優(yōu)雅的實現(xiàn)方式請不吝指教。

以上是“Android基于Glide v4.x如何實現(xiàn)圖片加載進度監(jiān)聽”這篇文章的所有內(nèi)容,感謝各位的閱讀!希望分享的內(nèi)容對大家有幫助,更多相關(guān)知識,歡迎關(guān)注億速云行業(yè)資訊頻道!

向AI問一下細節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI