Glide源碼分析之?dāng)?shù)據(jù)拉取

同樣地,開(kāi)始之前先思考1個(gè)問(wèn)題:

  1. Glide是怎么實(shí)現(xiàn)那么多資源Model的加載的?比如可以從Url、Asset、FileDescriptor、Uri、File等來(lái)源加載數(shù)據(jù)

1.ModelLoader

Glide的所有數(shù)據(jù)加載都實(shí)現(xiàn)一個(gè)接口ModelLoader<Model, Data>,其中Model類(lèi)型就是來(lái)源的類(lèi)型,Data是加載得到的數(shù)據(jù)類(lèi)型,看下接口的具體源碼,包含一個(gè)內(nèi)部類(lèi)LoadData,和兩個(gè)接口方法,一個(gè)buildLoadData方法用來(lái)構(gòu)造返回一個(gè)LoadData,另外一個(gè)方法handles用來(lái)返回這個(gè)ModelLoader能否處理這個(gè)ModelLoadData這個(gè)類(lèi)里面有三個(gè)字段,一個(gè)sourceKey用來(lái)表示這次下載,一個(gè)DataFetcher用來(lái)獲取不在緩存中的數(shù)據(jù):

public interface ModelLoader<Model, Data> {

  /**
   * @param <Data> The type of data that well be loaded.
   */
  class LoadData<Data> {
    public final Key sourceKey;
    public final List<Key> alternateKeys;
    public final DataFetcher<Data> fetcher;

    public LoadData(@NonNull Key sourceKey, @NonNull DataFetcher<Data> fetcher) {
      this(sourceKey, Collections.<Key>emptyList(), fetcher);
    }

    public LoadData(@NonNull Key sourceKey, @NonNull List<Key> alternateKeys,
        @NonNull DataFetcher<Data> fetcher) {
      this.sourceKey = Preconditions.checkNotNull(sourceKey);
      this.alternateKeys = Preconditions.checkNotNull(alternateKeys);
      this.fetcher = Preconditions.checkNotNull(fetcher);
    }
  }

  @Nullable
  LoadData<Data> buildLoadData(@NonNull Model model, int width, int height,
      @NonNull Options options);

  boolean handles(@NonNull Model model);
}

看下Glide里面所有的實(shí)現(xiàn)類(lèi),這些種類(lèi)就代表了Glide支持從哪些數(shù)據(jù)類(lèi)型下載數(shù)據(jù):

ModelLoader.PNG

比如我們看一個(gè)最常用的從http/https uris下載數(shù)據(jù)的UrlUriLoader, 先看它的接口方法handles,如果scheme類(lèi)型是http/https類(lèi)型的就返回true;另外一個(gè)接口方法buildLoadData通過(guò)字段urlloader返回一個(gè)LoadData,還有一個(gè)內(nèi)部工廠類(lèi)StramFactory, UrlUriLoader不建議外面通過(guò)構(gòu)造函數(shù)實(shí)例化,而是通過(guò)這個(gè)工廠類(lèi)的build方法進(jìn)行實(shí)例化。基本上ModelLoader的實(shí)現(xiàn)類(lèi)都是這種設(shè)計(jì)模式。

// UrlUriLoader.java
public class UrlUriLoader<Data> implements ModelLoader<Uri, Data> {
  private static final Set<String> SCHEMES = Collections.unmodifiableSet(
      new HashSet<>(
          Arrays.asList(
              "http",
              "https"
          )
      )
  );
  private final ModelLoader<GlideUrl, Data> urlLoader;

  // Public API.
  @SuppressWarnings("WeakerAccess")
  public UrlUriLoader(ModelLoader<GlideUrl, Data> urlLoader) {
    this.urlLoader = urlLoader;
  }

  @Override
  public LoadData<Data> buildLoadData(@NonNull Uri uri, int width, int height,
      @NonNull Options options) {
    GlideUrl glideUrl = new GlideUrl(uri.toString());
    return urlLoader.buildLoadData(glideUrl, width, height, options);
  }

  @Override
  public boolean handles(@NonNull Uri uri) {
    return SCHEMES.contains(uri.getScheme());
  }

  /**
   * Loads {@link java.io.InputStream InputStreams} from {@link android.net.Uri Uris} with http
   * or https schemes.
   */
  public static class StreamFactory implements ModelLoaderFactory<Uri, InputStream> {

    @NonNull
    @Override
    public ModelLoader<Uri, InputStream> build(MultiModelLoaderFactory multiFactory) {
      return new UrlUriLoader<>(multiFactory.build(GlideUrl.class, InputStream.class));
    }

    @Override
    public void teardown() {
      // Do nothing.
    }
  }
}

StreamFactory也是實(shí)現(xiàn)一個(gè)工廠接口ModelLoaderFactory,具體看下這個(gè)接口代碼:

// ModelLoaderFactory.java
/**
 * An interface for creating a {@link ModelLoader} for a given model type.
 *
 * @param <T> The type of the model the {@link com.bumptech.glide.load.model.ModelLoader}s built by
 *            this factory can handle
 * @param <Y> The type of data the {@link com.bumptech.glide.load.model.ModelLoader}s built by this
 *            factory can load.
 */
public interface ModelLoaderFactory<T, Y> {

  /**
   * Build a concrete ModelLoader for this model type.
   *
   * @param multiFactory A map of classes to factories that can be used to construct additional
   *                     {@link ModelLoader}s that this factory's {@link ModelLoader} may depend on
   * @return A new {@link ModelLoader}
   */
  @NonNull
  ModelLoader<T, Y> build(@NonNull MultiModelLoaderFactory multiFactory);

  /**
   * A lifecycle method that will be called when this factory is about to replaced.
   */
  void teardown();
}

接口比較簡(jiǎn)單,比如特殊的是還有一個(gè)工廠MultiModelLoaderFactory,其實(shí)也可以看成一種代理模式,真正去buildLoadData的是通過(guò)工廠類(lèi)StreamFactory構(gòu)造返回的urlLoader,它能處理的Model類(lèi)型是GlideUrl,返回的類(lèi)型是InputStream,而urlloader本身是通過(guò)MultiModelLoaderFactory構(gòu)造:

public ModelLoader<Uri, InputStream> build(MultiModelLoaderFactory multiFactory) {
      return new UrlUriLoader<>(multiFactory.build(GlideUrl.class, InputStream.class));
}

所以我們總結(jié)下下ModelLoader,UrlUriLoader,StreamFactory,ModelLoaderFactory,MultiModelLoaderFactory的關(guān)系, 具體的ModelLoader里面有一個(gè)代理的ModelLoader,這個(gè)代理的ModelLoder由MultiModelLoaderFactory實(shí)例化,不管是外層的ModelLoader或者是代理ModelLoader都是由Factory這種方式生成,外層的ModelLoaderFactory在自己的類(lèi)名下面,而代理ModelLoader統(tǒng)一都由MultiModelLoaderFactory這個(gè)工廠生成,類(lèi)似門(mén)面模式。

ModelLoaderUML.png

StreamFactory是在Glide初始化的時(shí)候注冊(cè)到ModelLoaderRegistry中:

//Glide.java
registry.append(URL.class, InputStream.class, new UrlLoader.StreamFactory())

ModelLoaderRegistry其實(shí)只是MultiModelLoaderFactory的外殼,它會(huì)把Glide初始化注冊(cè)的所有ModelFactory保存到MultiModelLoaderFactory:

// ModelLoaderRegistry.java
private final MultiModelLoaderFactory multiModelLoaderFactory;
private final ModelLoaderCache cache = new ModelLoaderCache();
public synchronized <Model, Data> void append(
      @NonNull Class<Model> modelClass,
      @NonNull Class<Data> dataClass,
      @NonNull ModelLoaderFactory<? extends Model, ? extends Data> factory) {
    multiModelLoaderFactory.append(modelClass, dataClass, factory);
    cache.clear();
  }

按照上面的邏輯,再看一個(gè)具體的ModelLoader實(shí)現(xiàn)類(lèi)FileLoader<Data> implements ModelLoader<File, Data>, 實(shí)現(xiàn)從File加載Data,這里Data有兩個(gè)類(lèi)型java.io.InputStreamjava.io.FileDescriptor,看下具體實(shí)現(xiàn)。實(shí)際工作會(huì)通過(guò)內(nèi)部實(shí)例變量FileOpener完成,在FileFetcher loadData中通過(guò)FileOpener打開(kāi)文件,返回需要的數(shù)據(jù)類(lèi)型,所以關(guān)鍵就在這個(gè)FileOpener,在這里是對(duì)文件打開(kāi)關(guān)閉的一個(gè)封裝接口,根據(jù)具體的返回類(lèi)型構(gòu)造不同的FileOpener.

// FileLoader.java
public class FileLoader<Data> implements ModelLoader<File, Data> {
  private static final String TAG = "FileLoader";

  private final FileOpener<Data> fileOpener;

  // Public API.
  @SuppressWarnings("WeakerAccess")
  public FileLoader(FileOpener<Data> fileOpener) {
    this.fileOpener = fileOpener;
  }

  @Override
  public LoadData<Data> buildLoadData(@NonNull File model, int width, int height,
      @NonNull Options options) {
    return new LoadData<>(new ObjectKey(model), new FileFetcher<>(model, fileOpener));
  }

  @Override
  public boolean handles(@NonNull File model) {
    return true;
  }

  /**
   * Allows opening a specific type of data from a {@link java.io.File}.
   * @param <Data> The type of data that can be opened.
   */
  public interface FileOpener<Data> {
    Data open(File file) throws FileNotFoundException;
    void close(Data data) throws IOException;
    Class<Data> getDataClass();
  }

  private static final class FileFetcher<Data> implements DataFetcher<Data> {
    private final File file;
    private final FileOpener<Data> opener;
    private Data data;

    FileFetcher(File file, FileOpener<Data> opener) {
      this.file = file;
      this.opener = opener;
    }

    @Override
    public void loadData(@NonNull Priority priority, @NonNull DataCallback<? super Data> callback) {
      try {
        data = opener.open(file);
      } catch (FileNotFoundException e) {
        if (Log.isLoggable(TAG, Log.DEBUG)) {
          Log.d(TAG, "Failed to open file", e);
        }
        callback.onLoadFailed(e);
        return;
      }
      callback.onDataReady(data);
    }

    @Override
    public void cleanup() {
      if (data != null) {
        try {
          opener.close(data);
        } catch (IOException e) {
          // Ignored.
        }
      }
    }

    @Override
    public void cancel() {
      // Do nothing.
    }

    @NonNull
    @Override
    public Class<Data> getDataClass() {
      return opener.getDataClass();
    }

    @NonNull
    @Override
    public DataSource getDataSource() {
      return DataSource.LOCAL;
    }
  }

  /**
   * Base factory for loading data from {@link java.io.File files}.
   * @param <Data> The type of data that will be loaded for a given {@link java.io.File}.
   */
  public static class Factory<Data> implements ModelLoaderFactory<File, Data> {
    private final FileOpener<Data> opener;

    public Factory(FileOpener<Data> opener) {
      this.opener = opener;
    }

    @NonNull
    @Override
    public final ModelLoader<File, Data> build(@NonNull MultiModelLoaderFactory multiFactory) {
      return new FileLoader<>(opener);
    }

    @Override
    public final void teardown() {
      // Do nothing.
    }
  }

FileLoader內(nèi)部有兩個(gè)靜態(tài)內(nèi)部類(lèi)StreamFactoryFileDescriptorFactory分別返回兩個(gè)數(shù)據(jù)類(lèi)型java.io.InputStreamjava.io.FileDescriptor。分別構(gòu)造兩種類(lèi)型的FileOpener,一個(gè)返回InputStream類(lèi)型,另外一個(gè)返回ParcelFileDescriptor.

  /**
   * Factory for loading {@link InputStream}s from {@link File}s.
   */
  public static class StreamFactory extends Factory<InputStream> {
    public StreamFactory() {
      super(new FileOpener<InputStream>() {
        @Override
        public InputStream open(File file) throws FileNotFoundException {
          return new FileInputStream(file);
        }

        @Override
        public void close(InputStream inputStream) throws IOException {
          inputStream.close();
        }

        @Override
        public Class<InputStream> getDataClass() {
          return InputStream.class;
        }
      });
    }
  }

  /**
   * Factory for loading {@link ParcelFileDescriptor}s from {@link File}s.
   */
  public static class FileDescriptorFactory extends Factory<ParcelFileDescriptor> {

    public FileDescriptorFactory() {
      super(new FileOpener<ParcelFileDescriptor>() {
        @Override
        public ParcelFileDescriptor open(File file) throws FileNotFoundException {
          return ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY);
        }

        @Override
        public void close(ParcelFileDescriptor parcelFileDescriptor) throws IOException {
          parcelFileDescriptor.close();
        }

        @Override
        public Class<ParcelFileDescriptor> getDataClass() {
          return ParcelFileDescriptor.class;
        }
      });
    }
  }

上面兩個(gè)Factory也是Glide初始化的時(shí)候注冊(cè)到ModelLoaderRegistry中:

.append(File.class, InputStream.class, new FileLoader.StreamFactory())
.append(File.class, ParcelFileDescriptor.class, new FileLoader.FileDescriptorFactory())

2. DataFetcher

從前面ModelLoader<Model, Data>中有提到過(guò)DataFetcher<Data>這個(gè)類(lèi),這是真正做數(shù)據(jù)拉取的功能接口,基本上對(duì)應(yīng)不同的ModelLoader會(huì)有對(duì)應(yīng)的DataFetcher去實(shí)現(xiàn)拉取數(shù)據(jù)的工作,看下DataFetcher<Data>這個(gè)類(lèi)的接口定義。里面有個(gè)內(nèi)部接口DataCallback,在數(shù)據(jù)拉取成功后會(huì)通過(guò)回調(diào)接口返回。loadData就是真正發(fā)起數(shù)據(jù)請(qǐng)求的地方,getDataSource返回?cái)?shù)據(jù)來(lái)源,這是個(gè)枚舉。

// DataSource.java
public interface DataFetcher<T> {

  /**
   * Callback that must be called when data has been loaded and is available, or when the load
   * fails.
   *
   * @param <T> The type of data that will be loaded.
   */
  interface DataCallback<T> {

    /**
     * Called with the loaded data if the load succeeded, or with {@code null} if the load failed.
     */
    void onDataReady(@Nullable T data);

    /**
     * Called when the load fails.
     *
     * @param e a non-null {@link Exception} indicating why the load failed.
     */
    void onLoadFailed(@NonNull Exception e);
  }

  void loadData(@NonNull Priority priority, @NonNull DataCallback<? super T> callback);


  void cleanup();

  void cancel();

  /**
   * Returns the class of the data this fetcher will attempt to obtain.
   */
  @NonNull
  Class<T> getDataClass();

  /**
   * Returns the {@link com.bumptech.glide.load.DataSource} this fetcher will return data from.
   */
  @NonNull
  DataSource getDataSource();
}

看下在Glide中DataFetcher的繼承結(jié)構(gòu),實(shí)現(xiàn)類(lèi)還是很多,可以從AssetPath/Url/File/Uri等地方拉取數(shù)據(jù)。

DataFetcher.png

看下比較常用的HttpUrlFetcher,實(shí)現(xiàn)從Url加載返回一個(gè)InputStream:

/**
 * A DataFetcher that retrieves an {@link java.io.InputStream} for a Url.
 */
public class HttpUrlFetcher implements DataFetcher<InputStream>

loadData方法中,通過(guò)loadDataWithRedirects加載url返回result,如果成功就通過(guò)onDataReady返回?cái)?shù)據(jù):

// HttpUrlFetcher.java
InputStream result = loadDataWithRedirects(glideUrl.toURL(), 0, null, glideUrl.getHeaders());
callback.onDataReady(result);

loadDataWithRedirects中通過(guò)HttpURLConnection發(fā)起網(wǎng)絡(luò)連接:

// HttpUrlFetcher.java    
urlConnection = connectionFactory.build(url);
for (Map.Entry<String, String> headerEntry : headers.entrySet()) {
   urlConnection.addRequestProperty(headerEntry.getKey(),headerEntry.getValue());
}
urlConnection.setConnectTimeout(timeout);
urlConnection.setReadTimeout(timeout);
urlConnection.setUseCaches(false);
urlConnection.setDoInput(true);
urlConnection.setInstanceFollowRedirects(false);

// Connect explicitly to avoid errors in decoders if connection fails.
urlConnection.connect();
// Set the stream so that it's closed in cleanup to avoid resource leaks. See #2352.
stream = urlConnection.getInputStream();

看下另外幾個(gè)接口方法的實(shí)現(xiàn),這里DataSource明顯就是REMOTE,返回的數(shù)據(jù)類(lèi)型是InputStream.class,在cleanup中就是把sream和connection關(guān)閉:

  @Override
  public void cleanup() {
    if (stream != null) {
      try {
        stream.close();
      } catch (IOException e) {
        // Ignore
      }
    }
    if (urlConnection != null) {
      urlConnection.disconnect();
    }
    urlConnection = null;
  }

  @Override
  public void cancel() {
    isCancelled = true;
  }

  @NonNull
  @Override
  public Class<InputStream> getDataClass() {
    return InputStream.class;
  }

  @NonNull
  @Override
  public DataSource getDataSource() {
    return DataSource.REMOTE;
  }

3.總結(jié)

再回頭看前面提的問(wèn)題,Glide提供很多ModelLoader接口的實(shí)現(xiàn)類(lèi)來(lái)實(shí)現(xiàn)不同資源的數(shù)據(jù)加載,每個(gè)ModelLoader不是直接通過(guò)構(gòu)造函數(shù)實(shí)例化,而是每個(gè)Loader內(nèi)部提供的工廠類(lèi)進(jìn)行實(shí)例化,在Glide初始化的時(shí)候,會(huì)把這些工廠類(lèi)都注冊(cè)到ModelLoaderRegistry中,構(gòu)造的時(shí)候從里面取出對(duì)應(yīng)的工廠實(shí)例化ModelLoader。ModelLoader下載數(shù)據(jù)會(huì)通過(guò)DataFetcher,DataFetcher的實(shí)現(xiàn)類(lèi)基本和ModelLoader對(duì)應(yīng)。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容