隐私政策整改之Glide框架封装

背景

  • App合规现在是越来越严了,也越来越多App被通报,为了能隐私合规呢,我对公司App也进行很多的整改。其中最主要的是通过ASM字节码去拦截第三方库频繁调用隐私方法的问题。
  • 主要思路就是找到第三方库频繁调用隐私方法的方法,将其替换成我们自己的方法。AsmActualCombat项目地址:https://github.com/Peakmain/AsmActualCombat
  • 那今天呢,讲的是对Glide进行封装,移除User-Agent,相当来说简单多了,主要要熟悉代码
  • 背景:我们公司App加载网络图片时候,App移动应用检测的时候说我们应用自身获取个人信息行为,描述说的是我们有图片上传行为,看了堆栈,主要问题是加载图片的时候,user-Agent有读取设备型号行为


    image.png
  • 于是我就猜测是Glide在加载网络的时候,它内部设置了User-Agent,那就让我们一起来验证咋们的猜测吧。

源码

Glide的简单使用
Glide.with(imageView.getContext().getApplicationContext()).asDrawable().load(url).into(imageView);

因为我们主要想知道加载图片的时候是否设置了User-Agent,那肯定就是主要看加载图片时的into源码

into源码
  public ViewTarget into(@NonNull ImageView view) {
    Util.assertMainThread();
    Preconditions.checkNotNull(view);
    RequestOptions requestOptions = this.requestOptions;
    //代码省略
    return into(
        glideContext.buildImageViewTarget(view, transcodeClass),
        /*targetListener=*/ null,
        requestOptions);
  }

buildImageViewTarget

  public  ViewTarget buildImageViewTarget(
      @NonNull ImageView imageView, @NonNull Class transcodeClass) {
    return imageViewTargetFactory.buildTarget(imageView, transcodeClass);
  }
public class ImageViewTargetFactory {
  @NonNull
  @SuppressWarnings("unchecked")
  public  ViewTarget buildTarget(@NonNull ImageView view,
      @NonNull Class clazz) {
    if (Bitmap.class.equals(clazz)) {
      return (ViewTarget) new BitmapImageViewTarget(view);
    } else if (Drawable.class.isAssignableFrom(clazz)) {
      return (ViewTarget) new DrawableImageViewTarget(view);
    } else {
      throw new IllegalArgumentException(
          "Unhandled class: " + clazz + ", try .as*(Class).transcode(ResourceTranscoder)");
    }
  }
}
  • 此时我们无论点击BitmapImageViewTarget还是DrawableImageViewTarget他们的父类其实都是ImageViewTarget,而ImageViewTarget的父类是ViewTarget
  • 所以上面我们的into源码第一个参数实际是ViewTarget,第二个参数为null第三个参数暂时不用关心,如果需要我们呢再回来看。上面就可以变成这样
  public ViewTarget into(@NonNull ImageView view) {
    Util.assertMainThread();
    Preconditions.checkNotNull(view);
    RequestOptions requestOptions = this.requestOptions;
    //代码省略
    return into(
       ViewTarget
        null,
        requestOptions);
  }

继续往下走

  private > Y into(
      @NonNull Y target,
      @Nullable RequestListener targetListener,
      @NonNull RequestOptions options) {
    Util.assertMainThread();
    Preconditions.checkNotNull(target);
    if (!isModelSet) {
      throw new IllegalArgumentException("You must call #load() before calling #into()");
    }

    options = options.autoClone();
    Request request = buildRequest(target, targetListener, options);//①

    Request previous = target.getRequest();
   //代码省略
    requestManager.clear(target);
    target.setRequest(request);
    requestManager.track(target, request);//②
    return target;
  }
  • 注释①,它的作用主要是创建一个Request,点击Request我们发现它是一个接口,那么我么就看看它的实现类是啥
  private Request buildRequest(
      Target target,
      @Nullable RequestListener targetListener,
      RequestOptions requestOptions) {
    return buildRequestRecursive(
        target,
        targetListener,
        /*parentCoordinator=*/ null,
        transitionOptions,
        requestOptions.getPriority(),
        requestOptions.getOverrideWidth(),
        requestOptions.getOverrideHeight(),
        requestOptions);
  }
 private Request buildRequestRecursive(
      Target target,
      @Nullable RequestListener targetListener,
      @Nullable RequestCoordinator parentCoordinator,
      TransitionOptions transitionOptions,
      Priority priority,
      int overrideWidth,
      int overrideHeight,
      RequestOptions requestOptions) {

    // Build the ErrorRequestCoordinator first if necessary so we can update parentCoordinator.
    ErrorRequestCoordinator errorRequestCoordinator = null;
    Request mainRequest =
        buildThumbnailRequestRecursive(
            target,
            targetListener,
            parentCoordinator,
            transitionOptions,
            priority,
            overrideWidth,
            overrideHeight,
            requestOptions);

    if (errorRequestCoordinator == null) {
      return mainRequest;
    }
   //代码省略
    return errorRequestCoordinator;
  }
 private Request buildThumbnailRequestRecursive(
      Target target,
      RequestListener targetListener,
      @Nullable RequestCoordinator parentCoordinator,
      TransitionOptions transitionOptions,
      Priority priority,
      int overrideWidth,
      int overrideHeight,
      RequestOptions requestOptions) {
       //代码省略
if (thumbnailBuilder != null) {
  
} else if (thumbSizeMultiplier != null) {
      return obtainRequest(
          target,
          targetListener,
          requestOptions,
          parentCoordinator,
          transitionOptions,
          priority,
          overrideWidth,
          overrideHeight);
  }
}

这里我偷懒就直接去掉不必要的判断代码了,因为我们代码都没有去配置那些参数,所以一般前面两个判断是不会进来的。

  private Request obtainRequest(
      Target target,
      RequestListener targetListener,
      RequestOptions requestOptions,
      RequestCoordinator requestCoordinator,
      TransitionOptions transitionOptions,
      Priority priority,
      int overrideWidth,
      int overrideHeight) {
    return SingleRequest.obtain(
        context,
        glideContext,
        model,
        transcodeClass,
        requestOptions,
        overrideWidth,
        overrideHeight,
        priority,
        target,
        targetListener,
        requestListener,
        requestCoordinator,
        glideContext.getEngine(),
        transitionOptions.getTransitionFactory());
  }

这时候我们可以看到了,实现类是SingleRequest,利用享元模式创建的实例

  • 注释②,上面注释①我们知道了request是SingleRequest,继续跟下源码
  void track(Target target, Request request) {
    targetTracker.track(target);
    requestTracker.runRequest(request);
  }
  public void runRequest(Request request) {
    requests.add(request);
    if (!isPaused) {
      request.begin();
    } else {
      pendingRequests.add(request);
    }
  }

之后走到的是request的begin,也就是SingleRequest的begin方法

SingleRequest的begin源码
  public void begin() {
   
    if (status == Status.RUNNING) {//①
      throw new IllegalArgumentException("Cannot restart a running request");
    }
    if (status == Status.COMPLETE) {
      onResourceReady(resource, DataSource.MEMORY_CACHE);
      return;
    }
    status = Status.WAITING_FOR_SIZE;
    if (Util.isValidDimensions(overrideWidth, overrideHeight)) {
      onSizeReady(overrideWidth, overrideHeight);//②
    } else {
      target.getSize(this);
    }
  }
  • 注释①,state的默认初始是在init方法,而init是SingleRequest.obtain调用初始化的
  private void init(...) {
    status = Status.PENDING;
  }

所以注释①的判断为false,会往下走,一直走到注释②(图片的宽高都正常的情况下)

  • 注释②,onSizeReady源码
  public void onSizeReady(int width, int height) {
    stateVerifier.throwIfRecycled();
    if (status != Status.WAITING_FOR_SIZE) {
      return;
    }
    status = Status.RUNNING;

    float sizeMultiplier = requestOptions.getSizeMultiplier();
    this.width = maybeApplySizeMultiplier(width, sizeMultiplier);
    this.height = maybeApplySizeMultiplier(height, sizeMultiplier);
    loadStatus = engine.load(
        glideContext,
        model,
        requestOptions.getSignature(),
        this.width,
        this.height,
        requestOptions.getResourceClass(),
        transcodeClass,
        priority,
        requestOptions.getDiskCacheStrategy(),
        requestOptions.getTransformations(),
        requestOptions.isTransformationRequired(),
        requestOptions.isScaleOnlyOrNoTransform(),
        requestOptions.getOptions(),
        requestOptions.isMemoryCacheable(),
        requestOptions.getUseUnlimitedSourceGeneratorsPool(),
        requestOptions.getUseAnimationPool(),
        requestOptions.getOnlyRetrieveFromCache(),
        this);

    if (status != Status.RUNNING) {
      loadStatus = null;
    }
  }
  public  LoadStatus load(
      GlideContext glideContext,
      Object model,
      Key signature,
      int width,
      int height,
      Class resourceClass,
      Class transcodeClass,
      Priority priority,
      DiskCacheStrategy diskCacheStrategy,
      Map, Transformation> transformations,
      boolean isTransformationRequired,
      boolean isScaleOnlyOrNoTransform,
      Options options,
      boolean isMemoryCacheable,
      boolean useUnlimitedSourceExecutorPool,
      boolean useAnimationPool,
      boolean onlyRetrieveFromCache,
      ResourceCallback cb) {
    Util.assertMainThread();
    long startTime = LogTime.getLogTime();
   //缓存逻辑省略
    EngineJob engineJob =
        engineJobFactory.build(
            key,
            isMemoryCacheable,
            useUnlimitedSourceExecutorPool,
            useAnimationPool,
            onlyRetrieveFromCache);

    DecodeJob decodeJob =
        decodeJobFactory.build(
            glideContext,
            model,
            key,
            signature,
            width,
            height,
            resourceClass,
            transcodeClass,
            priority,
            diskCacheStrategy,
            transformations,
            isTransformationRequired,
            isScaleOnlyOrNoTransform,
            onlyRetrieveFromCache,
            options,
            engineJob);

    jobs.put(key, engineJob);

    engineJob.addCallback(cb);
    engineJob.start(decodeJob);
    return new LoadStatus(cb, engineJob);
  }

缓存逻辑我们就直接跳过,因为我们主要关注的是网络加载图片
engineJob的start

  public void start(DecodeJob decodeJob) {
    this.decodeJob = decodeJob;
    GlideExecutor executor = decodeJob.willDecodeFromCache()
        ? diskCacheExecutor
        : getActiveSourceExecutor();
    executor.execute(decodeJob);
  }

执行的实际是decodeJob的run方法

  public void run() {
    TraceCompat.beginSection("DecodeJob#run");
    DataFetcher localFetcher = currentFetcher;
    try {
      if (isCancelled) {
        notifyFailed();
        return;
      }
      runWrapped();
    } catch (Throwable t) {

    } finally {
      if (localFetcher != null) {
        localFetcher.cleanup();
      }
      TraceCompat.endSection();
    }
  }
  private void runWrapped() {
     switch (runReason) {
      case INITIALIZE://①
        stage = getNextStage(Stage.INITIALIZE);//②
        currentGenerator = getNextGenerator();//③
        runGenerators();//④
        break;
      case SWITCH_TO_SOURCE_SERVICE:
        runGenerators();
        break;
      case DECODE_DATA:
        decodeFromRetrievedData();
        break;
      default:
        throw new IllegalStateException("Unrecognized run reason: " + runReason);
    }
  }

  • 注释①,runReason在init的时候赋值为INITIALIZE
  • 注释②,我们需要这里我们Stage是INITIALIZE
  private Stage getNextStage(Stage current) {
    switch (current) {
      case INITIALIZE:
        return diskCacheStrategy.decodeCachedResource()
            ? Stage.RESOURCE_CACHE : getNextStage(Stage.RESOURCE_CACHE);
      case RESOURCE_CACHE:
        return diskCacheStrategy.decodeCachedData()
            ? Stage.DATA_CACHE : getNextStage(Stage.DATA_CACHE);
    //代码省略
    }
  }

我们并没有对磁盘缓存进行修改,那么diskCacheStrategy肯定是默认配置,我们一步步往回走,走到初始化它的代码位置,发现最终实际是在onSizeReady的时候初始化的

public void onSizeReady(int width, int height) {
loadStatus = engine.load(
    glideContext,
        model,
        requestOptions.getSignature(),
        this.width,
        this.height,
        requestOptions.getResourceClass(),
        transcodeClass,
        priority,
        requestOptions.getDiskCacheStrategy(),//这里初始化的
        requestOptions.getTransformations(),
        requestOptions.isTransformationRequired(),
        requestOptions.isScaleOnlyOrNoTransform(),
        requestOptions.getOptions(),
        requestOptions.isMemoryCacheable(),
        requestOptions.getUseUnlimitedSourceGeneratorsPool(),
        requestOptions.getUseAnimationPool(),
        requestOptions.getOnlyRetrieveFromCache(),
        this);
}
public class RequestOptions implements Cloneable {
      private DiskCacheStrategy diskCacheStrategy = DiskCacheStrategy.AUTOMATIC;
      public final DiskCacheStrategy getDiskCacheStrategy() {
          return diskCacheStrategy;
  }
}
  public static final DiskCacheStrategy AUTOMATIC = new DiskCacheStrategy() {
    @Override
    public boolean isDataCacheable(DataSource dataSource) {
      return dataSource == DataSource.REMOTE;
    }

    @Override
    public boolean isResourceCacheable(boolean isFromAlternateCacheKey, DataSource dataSource,
        EncodeStrategy encodeStrategy) {
      return ((isFromAlternateCacheKey && dataSource == DataSource.DATA_DISK_CACHE)
          || dataSource == DataSource.LOCAL)
          && encodeStrategy == EncodeStrategy.TRANSFORMED;
    }

    @Override
    public boolean decodeCachedResource() {
      return true;//返回true
    }

    @Override
    public boolean decodeCachedData() {
      return true;
    }
  };

我们回到getNextStage,我们发现diskCacheStrategy.decodeCachedResource()返回的是true,也就是说注释② getNextStage返回Stage.RESOURCE_CACHE

  • 注释③,getNextGenerator
  private DataFetcherGenerator getNextGenerator() {
    switch (stage) {
      case RESOURCE_CACHE:
        return new ResourceCacheGenerator(decodeHelper, this);
       //代码省略
      default:
        throw new IllegalStateException("Unrecognized stage: " + stage);
    }
  }

注释③实际是创建ResourceCacheGenerator实例并复制给了currentGenerator

  • 注释④,我们主要分析runGenerators
    runGenerators
  private void runGenerators() {
    currentThread = Thread.currentThread();
    startFetchTime = LogTime.getLogTime();
    boolean isStarted = false;
    while (!isCancelled && currentGenerator != null
        && !(isStarted = currentGenerator.startNext())) {//①
    }
  }

  • 上面我们知道currentGenerator是ResourceCacheGenerator,也就是说currentGenerator.startNext()调用的是ResourceCacheGenerator.startNext()

ResourceCacheGenerator 的StartNext

  public boolean startNext() {
    List sourceIds = helper.getCacheKeys();
    if (sourceIds.isEmpty()) {
      return false;
    }
    List> resourceClasses = helper.getRegisteredResourceClasses();
    while (modelLoaders == null || !hasNextModelLoader()) {
      resourceClassIndex++;
      if (resourceClassIndex >= resourceClasses.size()) {
        sourceIdIndex++;
        if (sourceIdIndex >= sourceIds.size()) {
          return false;
        }
        resourceClassIndex = 0;
      }

      Key sourceId = sourceIds.get(sourceIdIndex);
      Class resourceClass = resourceClasses.get(resourceClassIndex);
      Transformation transformation = helper.getTransformation(resourceClass);
      currentKey =
          new ResourceCacheKey(// NOPMD AvoidInstantiatingObjectsInLoops
              helper.getArrayPool(),
              sourceId,
              helper.getSignature(),
              helper.getWidth(),
              helper.getHeight(),
              transformation,
              resourceClass,
              helper.getOptions());
      cacheFile = helper.getDiskCache().get(currentKey);
      if (cacheFile != null) {
        sourceKey = sourceId;
        modelLoaders = helper.getModelLoaders(cacheFile);//①
        modelLoaderIndex = 0;
      }
    }

    loadData = null;
    boolean started = false;
    while (!started && hasNextModelLoader()) {
      ModelLoader modelLoader = modelLoaders.get(modelLoaderIndex++);
      loadData = modelLoader.buildLoadData(cacheFile,
          helper.getWidth(), helper.getHeight(), helper.getOptions());//②
      if (loadData != null && helper.hasLoadPath(loadData.fetcher.getDataClass())) {
        started = true;
        loadData.fetcher.loadData(helper.getPriority(), this);
      }
    }

    return started;
  }
  • 注释①,我们首先看下modelLoader是什么
  List> getModelLoaders(File file)
      throws Registry.NoModelLoaderAvailableException {
    return glideContext.getRegistry().getModelLoaders(file);
  }
  public  List> getModelLoaders(@NonNull Model model) {
    List> result = modelLoaderRegistry.getModelLoaders(model);
    return result;
  }
  public synchronized  List> getModelLoaders(@NonNull A model) {
    List> modelLoaders = getModelLoadersForClass(getClass(model));
    int size = modelLoaders.size();
     //代码省略
    return filteredLoaders;
  }
  private  List> getModelLoadersForClass(@NonNull Class modelClass) {
    List> loaders = cache.get(modelClass);
    if (loaders == null) {
      loaders = Collections.unmodifiableList(multiModelLoaderFactory.build(modelClass));
      cache.put(modelClass, loaders);
    }
    return loaders;
  }
  synchronized  List> build(@NonNull Class modelClass) {
    try {
      List> loaders = new ArrayList<>();
      for (Entry entry : entries) {
        if (alreadyUsedEntries.contains(entry)) {
          continue;
        }
        if (entry.handles(modelClass)) {
          alreadyUsedEntries.add(entry);
          loaders.add(this.build(entry));
          alreadyUsedEntries.remove(entry);
        }
      }
      return loaders;
    } catch (Throwable t) {
      alreadyUsedEntries.clear();
      throw t;
    }
  }
  private  ModelLoader build(@NonNull Entry entry) {
    return (ModelLoader) Preconditions.checkNotNull(entry.factory.build(this));
  }
  ModelLoader build(@NonNull MultiModelLoaderFactory multiFactory);

走到这里我们发现我们走不下去了,因为ModelLoaderFactory是个接口,那我们想知道entry.factory是什么,就需要知道entry是啥,我们回到build方法,我们来看看entries是啥玩意

  private final List> entries = new ArrayList<>();

我们发现就是一个List,既然是私有的,那就说明它一定是赋值或者有add方法

  private  void add(
      @NonNull Class modelClass,
      @NonNull Class dataClass,
      @NonNull ModelLoaderFactory factory,
      boolean append) {
    Entry entry = new Entry<>(modelClass, dataClass, factory);
    entries.add(append ? entries.size() : 0, entry);
  }
  synchronized  void append(
      @NonNull Class modelClass,
      @NonNull Class dataClass,
      @NonNull ModelLoaderFactory factory) {
    add(modelClass, dataClass, factory, /*append=*/ true);
  }
  public synchronized  void append(
      @NonNull Class modelClass,
      @NonNull Class dataClass,
      @NonNull ModelLoaderFactory factory) {
    multiModelLoaderFactory.append(modelClass, dataClass, factory);
    cache.clear();
  }
  public  Registry append(
      @NonNull Class modelClass, @NonNull Class dataClass,
      @NonNull ModelLoaderFactory factory) {
    modelLoaderRegistry.append(modelClass, dataClass, factory);
    return this;
  }

我们再继续反推我们发现会走到Glide的初始化

  Glide(
      @NonNull Context context,
      @NonNull Engine engine,
      @NonNull MemoryCache memoryCache,
      @NonNull BitmapPool bitmapPool,
      @NonNull ArrayPool arrayPool,
      @NonNull RequestManagerRetriever requestManagerRetriever,
      @NonNull ConnectivityMonitorFactory connectivityMonitorFactory,
      int logLevel,
      @NonNull RequestOptions defaultRequestOptions,
      @NonNull Map, TransitionOptions> defaultTransitionOptions) {
         registry
        .append(ByteBuffer.class, new ByteBufferEncoder())
        .append(InputStream.class, new StreamEncoder(arrayPool))
        /* Bitmaps */
        .append(Registry.BUCKET_BITMAP, ByteBuffer.class, Bitmap.class, byteBufferBitmapDecoder)
        .append(Registry.BUCKET_BITMAP, InputStream.class, Bitmap.class, streamBitmapDecoder)
        .append(
            Registry.BUCKET_BITMAP,
            ParcelFileDescriptor.class,
            Bitmap.class,
            parcelFileDescriptorVideoDecoder)
        .append(
            Registry.BUCKET_BITMAP,
            AssetFileDescriptor.class,
            Bitmap.class,
            VideoDecoder.asset(bitmapPool))
        .append(Bitmap.class, Bitmap.class, UnitModelLoader.Factory.getInstance())
        .append(
            Registry.BUCKET_BITMAP, Bitmap.class, Bitmap.class, new UnitBitmapDecoder())
        .append(Bitmap.class, bitmapEncoder)
        /* BitmapDrawables */
        .append(
            Registry.BUCKET_BITMAP_DRAWABLE,
            ByteBuffer.class,
            BitmapDrawable.class,
            new BitmapDrawableDecoder<>(resources, byteBufferBitmapDecoder))
        .append(
            Registry.BUCKET_BITMAP_DRAWABLE,
            InputStream.class,
            BitmapDrawable.class,
            new BitmapDrawableDecoder<>(resources, streamBitmapDecoder))
        .append(
            Registry.BUCKET_BITMAP_DRAWABLE,
            ParcelFileDescriptor.class,
            BitmapDrawable.class,
            new BitmapDrawableDecoder<>(resources, parcelFileDescriptorVideoDecoder))
        .append(BitmapDrawable.class, new BitmapDrawableEncoder(bitmapPool, bitmapEncoder))
        /* GIFs */
        .append(
            Registry.BUCKET_GIF,
            InputStream.class,
            GifDrawable.class,
            new StreamGifDecoder(registry.getImageHeaderParsers(), byteBufferGifDecoder, arrayPool))
        .append(Registry.BUCKET_GIF, ByteBuffer.class, GifDrawable.class, byteBufferGifDecoder)
        .append(GifDrawable.class, new GifDrawableEncoder())
        /* GIF Frames */
        // Compilation with Gradle requires the type to be specified for UnitModelLoader here.
        .append(
            GifDecoder.class, GifDecoder.class, UnitModelLoader.Factory.getInstance())
        .append(
            Registry.BUCKET_BITMAP,
            GifDecoder.class,
            Bitmap.class,
            new GifFrameResourceDecoder(bitmapPool))
        /* Drawables */
        .append(Uri.class, Drawable.class, resourceDrawableDecoder)
        .append(
            Uri.class, Bitmap.class, new ResourceBitmapDecoder(resourceDrawableDecoder, bitmapPool))
        /* Files */
        .register(new ByteBufferRewinder.Factory())
        .append(File.class, ByteBuffer.class, new ByteBufferFileLoader.Factory())
        .append(File.class, InputStream.class, new FileLoader.StreamFactory())
        .append(File.class, File.class, new FileDecoder())
        .append(File.class, ParcelFileDescriptor.class, new FileLoader.FileDescriptorFactory())
        // Compilation with Gradle requires the type to be specified for UnitModelLoader here.
        .append(File.class, File.class, UnitModelLoader.Factory.getInstance())
        /* Models */
        .register(new InputStreamRewinder.Factory(arrayPool))
        .append(int.class, InputStream.class, resourceLoaderStreamFactory)
        .append(
            int.class,
            ParcelFileDescriptor.class,
            resourceLoaderFileDescriptorFactory)
        .append(Integer.class, InputStream.class, resourceLoaderStreamFactory)
        .append(
            Integer.class,
            ParcelFileDescriptor.class,
            resourceLoaderFileDescriptorFactory)
        .append(Integer.class, Uri.class, resourceLoaderUriFactory)
        .append(
            int.class,
            AssetFileDescriptor.class,
            resourceLoaderAssetFileDescriptorFactory)
        .append(
            Integer.class,
            AssetFileDescriptor.class,
            resourceLoaderAssetFileDescriptorFactory)
        .append(int.class, Uri.class, resourceLoaderUriFactory)
        .append(String.class, InputStream.class, new DataUrlLoader.StreamFactory())
        .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,
            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())
        .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.getInstance())
        .append(Drawable.class, Drawable.class, UnitModelLoader.Factory.getInstance())
        .append(Drawable.class, Drawable.class, new UnitDrawableDecoder())
        /* Transcoders */
        .register(
            Bitmap.class,
            BitmapDrawable.class,
            new BitmapDrawableTranscoder(resources))
        .register(Bitmap.class, byte[].class, bitmapBytesTranscoder)
        .register(
            Drawable.class,
            byte[].class,
            new DrawableBytesTranscoder(
                bitmapPool, bitmapBytesTranscoder, gifDrawableBytesTranscoder))
        .register(GifDrawable.class, byte[].class, gifDrawableBytesTranscoder);

}

我们这时候发现,在new Glide的时候,它会去注册一系列类放到List集合entries,之后取出他们调用build。

  • 注释②,我们主要是想知道网络请求的时候有没有添加User-Agent,从上面命名中,我们很容易关注到HttpUriLoader,那么我们就看看它的build方法都干了啥
public class HttpUriLoader implements ModelLoader {
   public HttpUriLoader(ModelLoader urlLoader) {
    this.urlLoader = urlLoader;
  }
  public static class Factory implements ModelLoaderFactory {
    public ModelLoader build(MultiModelLoaderFactory multiFactory) {
      return new HttpUriLoader(multiFactory.build(GlideUrl.class, InputStream.class));
    }
  }
}

实际最终会调用HttpUriLoader的buildLoadData方法

  @Override
  public LoadData buildLoadData(@NonNull Uri model, int width, int height,
      @NonNull Options options) {
    return urlLoader.buildLoadData(new GlideUrl(model.toString()), width, height, options);
  }
  public GlideUrl(String url) {
    this(url, Headers.DEFAULT);
  }
Headers DEFAULT = new LazyHeaders.Builder().build();

最终我们走到了创建LazyHeaders对象

  public static final class Builder {
    private static final String USER_AGENT_HEADER = "User-Agent";
    private static final String DEFAULT_USER_AGENT = getSanitizedUserAgent();
    private static final Map> DEFAULT_HEADERS;

    // Set Accept-Encoding header to do our best to avoid gzip since it's both inefficient for
    // images and also makes it more difficult for us to detect and prevent partial content
    // rendering. See #440.
    static {
      Map> temp
          = new HashMap<>(2);
      if (!TextUtils.isEmpty(DEFAULT_USER_AGENT)) {
        temp.put(USER_AGENT_HEADER,
            Collections.singletonList(
                new StringHeaderFactory(DEFAULT_USER_AGENT)));
      }
      DEFAULT_HEADERS = Collections.unmodifiableMap(temp);
    }
  public Builder addHeader(String key, String value) {
      return addHeader(key, new StringHeaderFactory(value));
    }
    public Builder addHeader(String key, LazyHeaderFactory factory) {
      if (isUserAgentDefault && USER_AGENT_HEADER.equalsIgnoreCase(key)) {
        return setHeader(key, factory);
      }

      copyIfNecessary();
      getFactories(key).add(factory);
      return this;
    }
     public Builder setHeader(String key, LazyHeaderFactory factory) {
      copyIfNecessary();
      if (factory == null) {
        headers.remove(key);
      } else {
        List factories = getFactories(key);
        factories.clear();
        factories.add(factory);
      }

      if (isUserAgentDefault && USER_AGENT_HEADER.equalsIgnoreCase(key)) {
        isUserAgentDefault = false;
      }

      return this;
    }
}

我们发现LazyHeaders在创建的时候就已经默认添加了User-Agent
那么想要替换或者删除User-Agent怎么做呢?我们看setHeader源码,当我们设置factory为null,则会直接移除,创建新的LazyHeaderFactory则直接替换

实战与总结

实战替换User-Agent

 GlideUrl glideUrl = new GlideUrl(url1,new LazyHeaders.Builder().addHeader("User-Agent","android").build());   
Glide.with(imageView.getContext().getApplicationContext()).asDrawable().load(glideUrl).apply(requestOptions).into(imageView);

总结

  • 整体来说代码还是相当简单的,主要就是要熟悉源码
  • glide加载图片默认用的是HttpUrlConnection,当然它其实还有个库,可以替换成okhttp。
  • 加载网络图片的时候,默认是在GlideUrl中设置了Headers.DEFAULT,它的内部会在static中添加默认的User-Agent。

你可能感兴趣的:(隐私政策整改之Glide框架封装)