中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

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

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

Android基于Glide v4.x如何實現圖片加載進度監聽

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

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

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,就這樣,有不合理的地方歡迎指出,又更好更優雅的實現方式請不吝指教。

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

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

临猗县| 北京市| 积石山| 玉溪市| 黄浦区| 康保县| 青川县| 深圳市| 将乐县| 疏勒县| 名山县| 昌乐县| 竹溪县| 宣武区| 临沭县| 建水县| 阿拉善盟| 石嘴山市| 图们市| 桓仁| 永吉县| 茶陵县| 民乐县| 育儿| 开鲁县| 正蓝旗| 靖江市| 白城市| 清河县| 寻甸| 甘洛县| 巧家县| 阳朔县| 湘阴县| 卫辉市| 司法| 独山县| 界首市| 北辰区| 沙田区| 岑巩县|