【Glide】按照数据模型获取数据

一个String类型的数据,可能指向的是一个文件,也可能指向一个网址,Glide是如何判断的呢?如何获取这些数据的?

DataFetcher

拉取数据的接口

public interface DataFetcher{
	
	void loadData(Priority priority, DataCallback<? super T> callback);
	
	void cleanup();

	void cancel();

	Class<T> getDataClass();
	
	DataSource getDataSource();
	
}

懒惰地检索可用于加载资源的数据。 ModelLoader每次加载资源都会创建一个新实例。loadData对于任何给定的负载可能会或可能不会被调用,具体取决于相应的资源是否被缓存。取消也可能会或可能不会被调用。如果 loadData 被调用,那么cleanup将被调用。

  • loadData

从可解码资源中获取数据。 始终在后台线程上调用,因此在这里执行长时间运行的任务是安全的。

  • cleanup

清理或回收DataFetcher使用的任何资源。此方法将在 loadData 提供的数据被 ResourceDecoder解码后在 finally 块中调用。 此方法将在后台线程上运行。

  • cancel

当加载不再应用并已被取消时将调用此方法。它可以在加载开始之前或完成之后调用。 使用此方法的最佳方法是取消任何尚未开始的加载,但允许正在进行的加载完成。注意 - 此方法将在主线程上运行,因此它不应该执行阻塞操作并且应该快速完成。

  • getDataClass

返回此 fetcher 将尝试获取的数据的类。

  • getDataSource

返回此 fetcher 将从中返回数据的 DataSource 。DataSource是一个枚举类,枚举了数据的多种来源,如内存缓存,磁盘缓存,网络等。

DataFetcher的继承关系

DataFetcher的继承关系

子类实现:HttpUrlFetcher

DataFetcher的子类,实现从网络地址拉取数据

public class HttpUrlFetcher implements DataFetcher<InputStream>{
  
  @Override
  public void loadData(Priority priority,DataCallback<? super InputStream> callback) {
    try {
      InputStream result = loadDataWithRedirects(glideUrl.toURL(), 0, null, glideUrl.getHeaders());
      callback.onDataReady(result);
    } catch (IOException e) {
      callback.onLoadFailed(e);
    } 
  }

  private InputStream loadDataWithRedirects(URL url, int redirects, URL lastUrl,
      Map<String, String> headers) throws IOException {

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

    urlConnection.connect();
    stream = urlConnection.getInputStream();

    final int statusCode = urlConnection.getResponseCode();
    if (isHttpOk(statusCode)) {
      return getStreamForSuccessfulRequest(urlConnection);
    } 
  }
}

ModelLoader

public interface ModelLoader<Model, Data> {

  class LoadData<Data> {
  
    public final Key sourceKey;
    public final List<Key> alternateKeys;
    public final DataFetcher<Data> fetcher;

  }

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

  boolean handles(@NonNull Model model);
  
}

一个工厂接口,用于将任意复杂的数据模型转换为具体的数据类型,DataFetcher 可以使用该数据类型来获取模型表示的资源的数据。
该接口有两个目标:

  1. 将特定模型转换为可解码为资源的数据类型。
  2. 允许模型与视图的维度相结合以获取特定大小的资源。这不仅避免了在 xml 和代码中复制尺寸以确定具有不同密度的设备上的视图大小,而且还允许您使用布局权重或以其他方式以编程方式放置视图的尺寸,而无需强迫您获取通用资源大小。您获取的资源越小,您使用的带宽和电池寿命就越短,每个资源的内存占用也就越少。

转换后得到的DataFetcher和相关数据,被封装到ModelLoader的内部类LoadData中。

ModelLoader的继承关系

ModelLoader继承关系

子类实现:HttpGlideUrlLoader

ModelLoader的子类,处理数据类型GlideUrl,把HttpUrlFetcher封装到LoadData

public class HttpGlideUrlLoader implements ModelLoader<GlideUrl, InputStream> {
	
  @Override
  public LoadData<InputStream> buildLoadData(GlideUrl model, int width, int height,    Options options) {

    GlideUrl url = model;
    int timeout = options.get(TIMEOUT);
    return new LoadData<>(url, new HttpUrlFetcher(url, timeout));
    
  }	

}

Registry

分析执行流程之前,必须再介绍几个类。这几个类缓存了数据模型对应的ModelLoader。
Registry中ModelLoaderRegistry

public class Registry {

  private final ModelLoaderRegistry modelLoaderRegistry;

}

ModelLoaderRegistry中MultiModelLoaderFactory

class ModelLoaderRegistry{

  private final MultiModelLoaderFactory multiModelLoaderFactory;

}

MultiModelLoaderFactory的entries缓存了数据模型和ModelLoader的对应关系。
build方法的逻辑是根据数据模型搜索ModelLoader。

class MultiModelLoaderFactory{

    List<Entry<?, ?>> entries = new ArrayList<>();

	synchronized <Model> List<ModelLoader<Model, ?>> build(Class<Model> modelClass) {

	    List<ModelLoader<Model, ?>> loaders = new ArrayList<>();
	    
	    for (Entry<?, ?> entry : entries) {
	      if (entry.handles(modelClass)) {
	        loaders.add(this.<Model, Object>build(entry));
	      }
	    }
	    
	    return loaders;

  }

}

Entry类

private static class Entry<Model, Data> {

  Class<Model> modelClass;
  Class<Data> dataClass;
  ModelLoaderFactory<? extends Model, ? extends Data> factory;

}

数据模型和ModelLoader的对应关系是在Glide创建时初始化的。

public class Glide{
	
	registry
        .append(File.class, ByteBuffer.class, new ByteBufferFileLoader.Factory())
        .append(File.class, InputStream.class, new FileLoader.StreamFactory())
        .register(new InputStreamRewinder.Factory(arrayPool))
        .append(int.class, InputStream.class, resourceLoaderStreamFactory)
        .append(Integer.class, Uri.class, resourceLoaderUriFactory)
        .append(String.class, InputStream.class, new DataUrlLoader.StreamFactory<String>())
        .append(Uri.class, InputStream.class, new DataUrlLoader.StreamFactory<Uri>())
        .append(String.class, InputStream.class, new StringLoader.StreamFactory())
        .append(String.class, ParcelFileDescriptor.class, new StringLoader.FileDescriptorFactory())
        .append(
            String.class, AssetFileDescriptor.class, new StringLoader.AssetFileDescriptorFactory())
        .append(Uri.class, InputStream.class, new HttpUriLoader.Factory())
        .append(Uri.class, InputStream.class, new AssetUriLoader.StreamFactory(context.getAssets()))
        .append(Uri.class, InputStream.class, new UrlUriLoader.StreamFactory())
        .append(URL.class, InputStream.class, new UrlLoader.StreamFactory())
        .append(Uri.class, File.class, new MediaStoreFileLoader.Factory(context))
        .append(GlideUrl.class, InputStream.class, new HttpGlideUrlLoader.Factory())
        .append(byte[].class, ByteBuffer.class, new ByteArrayLoader.ByteBufferFactory())
        .append(byte[].class, InputStream.class, new ByteArrayLoader.StreamFactory())
        .append(Uri.class, Uri.class, UnitModelLoader.Factory.<Uri>getInstance())
        .append(Drawable.class, Drawable.class, UnitModelLoader.Factory.<Drawable>getInstance())
        .append(Drawable.class, Drawable.class, new UnitDrawableDecoder());
	
}

从http/https拉取数据流程

通常使用网址加载数据时,我们传入的是String,那么从Glide初始化的代码中,找到处理String的ModelLoader

.append(String.class, InputStream.class, new DataUrlLoader.StreamFactory<String>())
.append(String.class, InputStream.class, new StringLoader.StreamFactory())
.append(String.class, ParcelFileDescriptor.class, new StringLoader.FileDescriptorFactory())
.append(String.class, AssetFileDescriptor.class, new StringLoader.AssetFileDescriptorFactory())

第一步会得到四个工厂实例:

  • DataUrlLoader.StreamFactory
  • StringLoader.StreamFactory
  • StringLoader.FileDescriptorFactory
  • StringLoader.AssetFileDescriptorFactory

然后依次分析这些工厂:

DataUrlLoader.StreamFactory

public static final class StreamFactory implements ModelLoaderFactory {
  public ModelLoader<Model, InputStream> build(MultiModelLoaderFactory multiFactory) {
    return new DataUrlLoader<>(opener);
  }
}

工厂方法返回的DataUrlLoader,我们只关注handles方法,如果返回true,表示可以这个数据类型,否则返回false。

public final class DataUrlLoader<Model, Data> implements ModelLoader<Model, Data> {
	
  private static final String DATA_SCHEME_IMAGE = "data:image";

  @Override
  public boolean handles(@NonNull Model model) {
    return model.toString().startsWith(DATA_SCHEME_IMAGE);
  }

}

很显然,我们传入的是http地址,这个方法返回false。http不是在这里处理的,这个工厂类跳过。

StringLoader.StreamFactory
StringLoader.FileDescriptorFactory
StringLoader.AssetFileDescriptorFactory

这三个工厂类内部都指向了另一个数据模型,因此放在一起看:

public static class StreamFactory implements ModelLoaderFactory<String, InputStream> {

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

public static class FileDescriptorFactory
    implements ModelLoaderFactory<String, ParcelFileDescriptor> {

  @Override
  public ModelLoader<String, ParcelFileDescriptor> build(MultiModelLoaderFactory multiFactory) {
    return new StringLoader<>(multiFactory.build(Uri.class, ParcelFileDescriptor.class));
  }
}

  public static final class AssetFileDescriptorFactory
      implements ModelLoaderFactory<String, AssetFileDescriptor> {

    @Override
    public ModelLoader<String, AssetFileDescriptor> build(MultiModelLoaderFactory multiFactory) {
      return new StringLoader<>(multiFactory.build(Uri.class, AssetFileDescriptor.class));
    }

  }

这个工厂类的build方法都调用了MultiModelLoaderFactory 的build,传入的类型参数变成Uri。

PS:此处的StringLoader使用了装饰者模式:

public class StringLoader<Data> implements ModelLoader<String, Data> {
  private final ModelLoader<Uri, Data> uriLoader;
  
  @SuppressWarnings("WeakerAccess")
  public StringLoader(ModelLoader<Uri, Data> uriLoader) {
    this.uriLoader = uriLoader;
  }
}

回到http流程,现在变成了获取Uri的ModelLoader,Glide对Uri的注册:

.append(Uri.class, InputStream.class, new DataUrlLoader.StreamFactory<Uri>())
.append(Uri.class, InputStream.class, new HttpUriLoader.Factory())
.append(Uri.class, InputStream.class, new AssetUriLoader.StreamFactory(context.getAssets()))
.append(Uri.class,ParcelFileDescriptor.class,new AssetUriLoader.FileDescriptorFactory(context.getAssets()))
.append(Uri.class, InputStream.class, new MediaStoreImageThumbLoader.Factory(context))
.append(Uri.class, InputStream.class, new MediaStoreVideoThumbLoader.Factory(context))
.append(Uri.class,InputStream.class,new UriLoader.StreamFactory(contentResolver))
.append(Uri.class,ParcelFileDescriptor.class,new UriLoader.FileDescriptorFactory(contentResolver))
.append(Uri.class,AssetFileDescriptor.class,new UriLoader.AssetFileDescriptorFactory(contentResolver))
.append(Uri.class, InputStream.class, new UrlUriLoader.StreamFactory())

接下来的分析逻辑和String的一致,看过各个工厂之后,最终锁定了如下两个类:

  • HttpUriLoader.Factory
  • UrlUriLoader.StreamFactory

HttpUriLoader和UrlUriLoader都可以处理http请求:


public class HttpUriLoader implements ModelLoader<Uri, InputStream> {
  private static final Set<String> SCHEMES =
      Collections.unmodifiableSet(new HashSet<>(Arrays.asList("http", "https")));

  private final ModelLoader<GlideUrl, InputStream> urlLoader;

  public HttpUriLoader(ModelLoader<GlideUrl, InputStream> urlLoader) {
    this.urlLoader = urlLoader;
  }

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

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


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 UrlUriLoader(ModelLoader<GlideUrl, Data> urlLoader) {
    this.urlLoader = urlLoader;
  }

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

对应的两个工厂类,都把Uri获取ModelLoder交给了GlideUrl代理:
HttpUriLoader.Factory

public static class Factory implements ModelLoaderFactory<Uri, InputStream> {

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

}

UrlUriLoader.StreamFactory

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

}

回到Glide,发现GlideUrl只注册了一个工厂类:

.append(GlideUrl.class, InputStream.class, new HttpGlideUrlLoader.Factory())

HttpGlideUrlLoader.Factory返回的ModelLoader是HttpGlideUrlLoader:

  public static class Factory implements ModelLoaderFactory<GlideUrl, InputStream> {

    @Override
    public ModelLoader<GlideUrl, InputStream> build(MultiModelLoaderFactory multiFactory) {
      return new HttpGlideUrlLoader(modelCache);
    }


}

HttpGlideUrlLoader内部创建了HttpUrlFetcher的实例,用于从http请求中拉取数据:

public class HttpGlideUrlLoader implements ModelLoader<GlideUrl, InputStream> {

  @Override
  public LoadData<InputStream> buildLoadData(@NonNull GlideUrl model, int width, int height,Options options) {

    GlideUrl url = model;
    int timeout = options.get(TIMEOUT);
    return new LoadData<>(url, new HttpUrlFetcher(url, timeout))
    
  }
}

到这一步会有一个疑问,正常来说,一个数据模型对应的应该是一个DataFetcher,Uri那一步,得到了两个factory:

  • HttpUriLoader.Factory
  • UrlUriLoader.StreamFactory

不会导致后面得到两个GlideUrl的ModelLoader吗?不会执行两次网络请求吗?
回到String转Uri这个方法:

public static class StreamFactory implements ModelLoaderFactory<String, InputStream> {

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

}

因此,在multiFactory.build执行到最后,loaders.size() > 1 一定是成立的,源码如下:

public class MultiModelLoaderFactory {

  public synchronized <Model, Data> ModelLoader<Model, Data> build(Class<Model> modelClass,Class<Data> dataClass) {

     List<ModelLoader<Model, Data>> loaders = new ArrayList<>();

     for (Entry<?, ?> entry : entries) {

       if (entry.handles(modelClass, dataClass)) {

         loaders.add(this.<Model, Data>build(entry));

       }
     }
     if (loaders.size() > 1) {
       return factory.build(loaders, throwableListPool);
     } else if (loaders.size() == 1) {
       return loaders.get(0);
     } 

  }
	
}
class Factory {

   public <Model, Data> MultiModelLoader<Model, Data> build(List<ModelLoader<Model, Data>> modelLoaders,Pool<List<Throwable>> throwableListPool) {
     return new MultiModelLoader<>(modelLoaders, throwableListPool);
   }
 }

Factory 的build方法,返回了一个MultiModelLoader实例,从类的名称能看出来,这个类的就是用来处理多个ModelLoader的情形的:

class MultiModelLoader<Model, Data> implements ModelLoader<Model, Data> {

  private final List<ModelLoader<Model, Data>> modelLoaders;

  @Override
  public LoadData<Data> buildLoadData(@NonNull Model model, int width, int height,Options options) {
    Key sourceKey = null;
    int size = modelLoaders.size();
    List<DataFetcher<Data>> fetchers = new ArrayList<>(size);
 
    for (int i = 0; i < size; i++) {
      ModelLoader<Model, Data> modelLoader = modelLoaders.get(i);
      if (modelLoader.handles(model)) {
        LoadData<Data> loadData = modelLoader.buildLoadData(model, width, height, options);
        if (loadData != null) {
          sourceKey = loadData.sourceKey;
          fetchers.add(loadData.fetcher);
        }
      }
    }
    return !fetchers.isEmpty() && sourceKey != null
        ? new LoadData<>(sourceKey, new MultiFetcher<>(fetchers, exceptionListPool)) : null;
  }

与之匹配的是MultiFetcher,loadData方法中轮询加载数据,加载成功会调用onDataReady,当data 不为null时,这个循环就会结束。因此http链接不会加载两次,当第一次成功后,就返回了,如果第一次失败了,才会去加载第二次。

class MultiFetcher implements DataFetcher {

  @Override
   public void loadData(Priority priority, DataCallback<? super Data> callback) {
     this.priority = priority;
     this.callback = callback;
     exceptions = throwableListPool.acquire();
     fetchers.get(currentIndex).loadData(priority, this);
   }

  @Override
   public void onDataReady(@Nullable Data data) {
     if (data != null) {
       callback.onDataReady(data);
     } else {
       startNextOrFail();
     }
   }

}

至此,通过http/https地址获取DataFetcher的流程结束了。

加载本地资源流程

除了从网络加载图片,加载本地资源,接下来看一下Glide加载本地资源的流程
依然从Glide中对Integer类的注册开始,Integer有很多注册工厂,其中处理本地资源的是这个:

.append(Integer.class, InputStream.class, resourceLoaderStreamFactory)
public static class StreamFactory implements ModelLoaderFactory<Integer, InputStream> {

	 @Override
	 public ModelLoader<Integer, InputStream> build(MultiModelLoaderFactory multiFactory) {
	   return new ResourceLoader<>(resources, multiFactory.build(Uri.class, InputStream.class));
	 }

}

工厂创建了一个ResourceLoader的实例,并且把Integer代理给了Uri,Integer转Uri的方法,在ResourceLoader有实现,又学到了一招:

public class ResourceLoader<Data> implements ModelLoader<Integer, Data> {

  @Override
  public LoadData<Data> buildLoadData(@NonNull Integer model, int width, int height,Options options) {
    Uri uri = getResourceUri(model);
    return uri == null ? null : uriLoader.buildLoadData(uri, width, height, options);
  }

  private Uri getResourceUri(Integer model) {

     return Uri.parse(ContentResolver.SCHEME_ANDROID_RESOURCE + "://"
         + resources.getResourcePackageName(model) + '/'
         + resources.getResourceTypeName(model) + '/'
         + resources.getResourceEntryName(model));

  }

}

回到Glide,Uri注册的工厂类是UriLoader.StreamFactory:

.append(Uri.class,InputStream.class,new UriLoader.StreamFactory(contentResolver))

在UriLoader的handles方法中,表明了这个类是可以处理由Integer转化的Uri的:

public class UriLoader<Data> implements ModelLoader<Uri, Data> {
  private static final Set<String> SCHEMES = Collections.unmodifiableSet(
      new HashSet<>(
          Arrays.asList(
              ContentResolver.SCHEME_FILE,
              ContentResolver.SCHEME_ANDROID_RESOURCE,
              ContentResolver.SCHEME_CONTENT
          )
      )
  );

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

从Uri加载数据的真正实现,则非常简单:

public class StreamLocalUriFetcher extends LocalUriFetcher{

	private InputStream loadResourceFromUri(Uri uri, ContentResolver contentResolver){
    switch (URI_MATCHER.match(uri)) {
      case ID_CONTACTS_THUMBNAIL:
      case ID_CONTACTS_PHOTO:
      case UriMatcher.NO_MATCH:
      default:
        return contentResolver.openInputStream(uri);
    }
  }

}

至此,加载本地R文件资源的流程结束。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值