说一说Glide.with()

一、引子

  Glide框架是google推荐的Android图片加载框架,使用起来非常轻便,比如以下代码就可以实现在fragment内,以fitCenter方式加载图片,未加载成功前显示placeholder。

Glide.with(fragment)
   .load(myUrl)
   .placeholder(placeholder)
   .fitCenter()
   .into(imageView);


  这里说下Glide.with()。Glide.with()一共有六个同名方法,分别为Glide.with(Context context), Glide.with(Activity activity), Glide.with(FragmentActivity fragmentActivity), Glide.with(Fragment fragment), Glide.with(android.app.Fragment fragment),Glide.with(View view)。可以分成四类,Glide.with(Activity),Glide.with(Fragment), Glide.with(View),Glide.with(Context)。

  它们之间有什么区别呢,分别在何种情况下使用呢?

  先上结论吧,结论在Glide源码的说明文档里:

In general, loads should be started at the level the result will be used in. If the resource will be used in a view in a child fragment, the load should be started with with(android.app.Fragment)} using that child fragment. Similarly, if the resource will be used in a view in the parent fragment, the load should be started with with(android.app.Fragment) using the parent fragment. In the same vein, if the resource will be used in a view in an activity, the load should be started with with(Activity)}.This method is appropriate for resources that will be used outside of the normal fragment or activity lifecycle (For example in services, or for notification thumbnails).

  如果加载的结果需要在子Fragment中使用,则使用Glide.with(该子Fragment);如果需要在父fragment中使用,则使用Glide.with(该父Fragment);如果需要在Activity的View中使用,则使用Glide.with(Activity);如果需要在Fragment或Activity的生命周期之外,比如在service中或作为Notification的缩略图使用,则使用Glide.with(Context)。

  至于Glide.with(View)

This method will not work if the View is not attached. Prefer the Activity and Fragment variants unless you’re loading in a View subclass.
This method may be inefficient aways and is definitely inefficient for large hierarchies. Consider memoizing the result after the View is attached or again, prefer the Activity and Fragment variants whenever possible.

  除非在自定义View中,否则尽量不要使用,因为在复杂的View层级中,会非常的低效。

  Glide加载与Fragment或Activity生命周期绑定,可以减少CPU和内存占用,降低流量消耗。

  这里我们就通过源码看下,以下代码是Glide 4.7.1的代码。

二、Glide.with(Activity)

public static RequestManager with(@NonNull FragmentActivity activity) {
  return getRetriever(activity).get(activity);
}

这个方法的注释:

Begin a load with Glide that will tied to the give FragmentActivity’s lifecycle and that uses the given FragmentActivity’s default options.

  使用Glide开启一个图片加载(load),这个图片加载与给定的Activity参数的生命周期绑定。

######2.1 getRetriever(Context context)
  先看getRetriever(Context context)方法,它输入一个context,获取一个RequestManagerRetriever对象。RequestManagerRetriever对象应该是全局唯一的,因为传入Context对象,相当于抛弃了Activity信息,只保留了它的Context信息。

// (代码在Glide.java中)
@NonNull
private static RequestManagerRetriever getRetriever(@Nullable Context context) {
  // Context could be null for other reasons (ie the user passes in null), but in practice it will
  // only occur due to errors with the Fragment lifecycle.
  Preconditions.checkNotNull(
      context,
      "You cannot start a load on a not yet attached View or a Fragment where getActivity() "
          + "returns null (which usually occurs when getActivity() is called before the Fragment "
          + "is attached or after the Fragment is destroyed).");
  return Glide.get(context).getRequestManagerRetriever();
}

2.1.1 内部调用Glide.get(context)方法,获取Glide的单例对象glide。

private static volatile Glide glide;
 
@NonNull
public static Glide get(@NonNull Context context) {
  if (glide == null) {
    synchronized (Glide.class) {
      if (glide == null) {
        checkAndInitializeGlide(context);
      }
    }
  }
 
  return glide;
}

2.1.2 之后直接使用glide对象获取RequestManagerRetriever对象。

private final RequestManagerRetriever requestManagerRetriever;
 
@NonNull
public RequestManagerRetriever getRequestManagerRetriever() {
  return requestManagerRetriever;
}

requestManagerRetriever是glide对象的一个成员变量,它的初始化操作在checkAndInitializeGlide()方法中。

2.2 看下RequestManagerRetriever的get(Activity activity)方法,这个get()方法传入的是Activity对象,是不是会得到一个与Activity生命周期相关的RequestManager呢?
直接看

// RequestManagerRetriever.java
@NonNull
public RequestManager get(@NonNull FragmentActivity activity) {
  if (Util.isOnBackgroundThread()) {return get(activity.getApplicationContext());
  } else {
    assertNotDestroyed(activity); ②
    FragmentManager fm = activity.getSupportFragmentManager();
    return supportFragmentGet(
        activity, fm, /*parentHint=*/ null, isActivityVisible(activity));
  }
}

2.2.1 首先判断当前是否在后台线程,如果在后台线程中,则直接调用同名的get(Context context)方法。判断是否是后台线程就不多说了,get(Context context)方法也先放一放。
  先看下在主线程中的处理。分两步,先调用assertNotDestroyed(Activity),再调用supportFragmentGet()方法获取

@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
private static void assertNotDestroyed(@NonNull Activity activity) {
  if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1 && activity.isDestroyed()) {
    throw new IllegalArgumentException("You cannot start a load for a destroyed activity");
  }
}

  判断Activity是否destroyed了,如果Activity.isDestroyed()为true,直接抛出IllegalArgumentException,这个异常Glide框架并未捕获,需要开发者处理!

  处理方式有多种,如:

处理一:Glide.with(Activity)放入try catch中

处理二:Glide.with(Activity activity)传入Activity之前,过滤掉Activity为空或isDestroyed()为true的情况

处理三:app内部异步回调返回到主线程后,先判断Activity生命周期是否结束,再进行UI相关操作



  建议封装个拓展框架,按处理二处理

2.2.2 supportFragmentGet(Context, FragmentManager, Fragment, Boolean)方法,传入的参数为activity, fm, /parentHint=/ null, isActivityVisible(activity)
其中isActivityVisible(Activity)

private static boolean isActivityVisible(Activity activity) {
  // This is a poor heuristic, but it's about all we have. We'd rather err on the side of visible
 // and start requests than on the side of invisible and ignore valid requests.
 return !activity.isFinishing();
}

仅仅通过isFinishing()判断是否可见

看下supportFragmentGet()方法

@NonNull
private RequestManager supportFragmentGet(
    @NonNull Context context,
    @NonNull FragmentManager fm,
    @Nullable Fragment parentHint,
    boolean isParentVisible) {
  SupportRequestManagerFragment current =
      getSupportRequestManagerFragment(fm, parentHint, isParentVisible); ①
  RequestManager requestManager = current.getRequestManager();
  if (requestManager == null) {
    Glide glide = Glide.get(context);
    requestManager = factory.build(glide, current.getGlideLifecycle(), current.getRequestManagerTreeNode(), context); ②
    current.setRequestManager(requestManager);
  }
  return requestManager;
}

  主要有两个部分,

① 先通过getRequestManagerFragment()方法获取SupportRequestManagerFragment,并通过SupportRequestManagerFragment获取RequestManager;

② 如果RequestManager为空,则通过Glide创建一个。

2.2.2.1 先看下getSupportRequestManagerFragment(FragmentManager, Fragment, boolean)方法

@NonNull
private SupportRequestManagerFragment getSupportRequestManagerFragment(
    @NonNull final FragmentManager fm, @Nullable Fragment parentHint, boolean isParentVisible) {
  SupportRequestManagerFragment current =
      (SupportRequestManagerFragment) fm.findFragmentByTag(FRAGMENT_TAG);if (current == null) {
    current = pendingSupportRequestManagerFragments.get(fm);if (current == null) { ③
      current = new SupportRequestManagerFragment();
      current.setParentFragmentHint(parentHint);
      if (isParentVisible) {
        current.getGlideLifecycle().onStart();
      }
      pendingSupportRequestManagerFragments.put(fm, current);
      fm.beginTransaction().add(current, FRAGMENT_TAG).commitAllowingStateLoss();
      handler.obtainMessage(ID_REMOVE_SUPPORT_FRAGMENT_MANAGER, fm).sendToTarget();
    }
  }
  return current;
}
 
/**
 * Pending adds for SupportRequestManagerFragments.
 */
@VisibleForTesting
final Map<FragmentManager, SupportRequestManagerFragment> pendingSupportRequestManagerFragments =
    new HashMap<>();

① 和 ② 分别通过FragmentManager.findFragmentByTag()和Map查找SupportRequestManagerFragment,如果存在,则直接使用存在的;

③ 如果不存在,则创建并添加该Fragment;

在isParentVisible为true的情况下,调用current.getGlideLifecycle().onStart();
Handler的ID_REMOVE_SUPPORT_FRAGMENT_MANAGER消息用来在Fragment添加成功后从pendingSupportRequestManagerFragments中删除,减少不必要的引用,避免有可能的内存泄漏。
2.2.2.1.1 看下SupportRequestManagerFragment

它的描述:

A view-less Fragment used to safely store an RequestManager that can be used to start, stop and manage Glide requests started for targets within the fragment or activity this fragment is a child of.

  一个没有View(不可见)的用来存储RequestManager的Fragment,它的RequestManager负责启动、停止和管理与这个Fragment有关的Glide请求,这些Glide请求的target是这个fragment所在的父fragment或Activity,也就是通过Glide.with(Fragment)或Glide.with(Activity)方式启动的Glide请求。

public class SupportRequestManagerFragment extends Fragment {
    private final ActivityFragmentLifecycle lifecycle;
 
    public SupportRequestManagerFragment() {
        this(new ActivityFragmentLifecycle());
    }
 
    public SupportRequestManagerFragment(@NonNull ActivityFragmentLifecycle lifecycle) {
        this.lifecycle = lifecycle;
    }
 
    @NonNull
    ActivityFragmentLifecycle getGlideLifecycle() {
        return lifecycle;
    }
 
    @Override
    public void onStart() {
        super.onStart();
        lifecycle.onStart();
    }
 
    @Override
    public void onStop() {
        super.onStop();
        lifecycle.onStop();
    }
 
    @Override
    public void onDestroy() {
        super.onDestroy();
        lifecycle.onDestroy();
    }
 
}

  可以看到SupportRequestManagerFragment持有ActivityFragmentLifecycle引用lifecycle,在onStart()、onStop()、onDestroy()中分别调用了lifecycle的相关方法。

  看下ActivityFragmentLifecycle类

class ActivityFragmentLifecycle implements Lifecycle {
    private final Set<LifecycleListener> lifecycleListeners =
            Collections.newSetFromMap(new WeakHashMap<LifecycleListener, Boolean>());
    private boolean isStarted;
    private boolean isDestroyed;
 
    @Override
    public void addListener(@NonNull LifecycleListener listener) {
        lifecycleListeners.add(listener);
 
        if (isDestroyed) {
            listener.onDestroy();
        } else if (isStarted) {
            listener.onStart();
        } else {
            listener.onStop();
        }
    }
 
    @Override
    public void removeListener(@NonNull LifecycleListener listener) {
        lifecycleListeners.remove(listener);
    }
 
    void onStart() {
        isStarted = true;
        for (LifecycleListener lifecycleListener : Util.getSnapshot(lifecycleListeners)) {
            lifecycleListener.onStart();
        }
    }
 
    void onStop() {
        isStarted = false;
        for (LifecycleListener lifecycleListener : Util.getSnapshot(lifecycleListeners)) {
            lifecycleListener.onStop();
        }
    }
 
    void onDestroy() {
        isDestroyed = true;
        for (LifecycleListener lifecycleListener : Util.getSnapshot(lifecycleListeners)) {
            lifecycleListener.onDestroy();
        }
    }
}

  主要保存了isStarted和isDestroyed变量,并提供了addListener和removeListener方法,如果想获取到生命周期的回调,只需调用addListener()方法注册即可。

2.2.2.2 创建RequestManager,传入了SupportRequestManagerFragment的lifecycle对象

if (requestManager == null) {
   Glide glide = Glide.get(context);
   requestManager = factory.build(glide, current.getGlideLifecycle(), current.getRequestManagerTreeNode(), context); ②
   current.setRequestManager(requestManager);
 }

  看下RequestManagerRetriever中DEFAULT_FACTORY的build方法

private static final RequestManagerFactory DEFAULT_FACTORY = new RequestManagerFactory() {
  @NonNull
 @Override
 public RequestManager build(@NonNull Glide glide, @NonNull Lifecycle lifecycle,
      @NonNull RequestManagerTreeNode requestManagerTreeNode, @NonNull Context context) {
    return new RequestManager(glide, lifecycle, requestManagerTreeNode, context);
  }
};

  直接调用了RequestManager的构造方法

public class RequestManager implements LifecycleListener {
 
    public RequestManager(
            @NonNull Glide glide, @NonNull Lifecycle lifecycle,
            @NonNull RequestManagerTreeNode treeNode, @NonNull Context context) {
        this(
                glide,
                lifecycle,
                treeNode,
                new RequestTracker(),
                glide.getConnectivityMonitorFactory(),
                context);
    }
 
    RequestManager(
            Glide glide,
            Lifecycle lifecycle,
            RequestManagerTreeNode treeNode,
            RequestTracker requestTracker,
            ConnectivityMonitorFactory factory,
            Context context) {
        // 省略无关代码
 
        // If we're the application level request manager, we may be created on a background thread.
        // In that case we cannot risk synchronously pausing or resuming requests, so we hack around the
        // issue by delaying adding ourselves as a lifecycle listener by posting to the main thread.
        // This should be entirely safe.
        if (Util.isOnBackgroundThread()) {
            mainHandler.post(addSelfToLifecycle);
        } else {
            lifecycle.addListener(this);
        }
        lifecycle.addListener(connectivityMonitor);
        // 省略无关代码
    }
 
    /**
     * Lifecycle callback that registers for connectivity events (if the
     * android.permission.ACCESS_NETWORK_STATE permission is present) and restarts failed or paused
     * requests.
     */
    @Override
    public void onStart() {
        resumeRequests();
        targetTracker.onStart();
    }
 
    /**
     * Lifecycle callback that unregisters for connectivity events (if the
     * android.permission.ACCESS_NETWORK_STATE permission is present) and pauses in progress loads.
     */
    @Override
    public void onStop() {
        pauseRequests();
        targetTracker.onStop();
    }
 
    /**
     * Lifecycle callback that cancels all in progress requests and clears and recycles resources for
     * all completed requests.
     */
    @Override
    public void onDestroy() {
        targetTracker.onDestroy();
        for (Target<?> target : targetTracker.getAll()) {
            clear(target);
        }
        targetTracker.clear();
        requestTracker.clearRequests();
        lifecycle.removeListener(this);
        lifecycle.removeListener(connectivityMonitor);
        mainHandler.removeCallbacks(addSelfToLifecycle);
        glide.unregisterRequestManager(this);
    }
}

  通过调用lifecycle的addListener(),当SupportRequestManagerFragment的生命周期切换时,分别恢复、停止、清除请求。

三、Glide.with(Fragment)
  与Glide.with(Activity)基本相同

  它的描述:

Begin a load with Glide that will be tied to the given Fragment’s lifecycle and that uses the given Fragment’s default options.

  开启一个与给定的Fragment的生命周期相关联的图片加载请求。

@NonNull
public static RequestManager with(@NonNull Fragment fragment) {
  return getRetriever(fragment.getActivity()).get(fragment);
}

  再次看下Glide.getRetriever(Context)方法

@NonNull
private static RequestManagerRetriever getRetriever(@Nullable Context context) {
  // Context could be null for other reasons (ie the user passes in null), but in practice it will
  // only occur due to errors with the Fragment lifecycle.
  Preconditions.checkNotNull(
      context,
      "You cannot start a load on a not yet attached View or a Fragment where getActivity() "
          + "returns null (which usually occurs when getActivity() is called before the Fragment "
          + "is attached or after the Fragment is destroyed).");
  return Glide.get(context).getRequestManagerRetriever();
}

  要注意Preconditions.checkNotNull()方法,它是Glide框架定义的

@NonNull
public static <T> T checkNotNull(@Nullable T arg, @NonNull String message) {
  if (arg == null) {
    throw new NullPointerException(message);
  }
  return arg;
}

  如果arg为空,**会直接抛出NullPointerException,并且Glide框架并未捕获,需要开发者自己处理!**处理方式如前文的Activity.isDestroyed()的处理。

  一个Glide.with()都能抛出两种异常给开发者,感觉该框架应该是不想活了。

  看下RequestManagerRetriever.get(Fragment)方法

@NonNull
public RequestManager get(@NonNull Fragment fragment) {
  Preconditions.checkNotNull(fragment.getActivity(),
        "You cannot start a load on a fragment before it is attached or after it is destroyed");
  if (Util.isOnBackgroundThread()) {
    return get(fragment.getActivity().getApplicationContext());
  } else {
    FragmentManager fm = fragment.getChildFragmentManager();
    return supportFragmentGet(fragment.getActivity(), fm, fragment, fragment.isVisible());
  }
}

  依然是异步线程中,使用ApplicationContext;UI线程中创建SupportRequestManagerFragment,只是在给定的Fragment(而非Activity)中创建,且isParentVisible变量使用Fragment.isVisible()。

四、Glide.with(Context)
描述:

Begin a load with Glide by passing in a context.
Any requests started using a context will only have the application level options applied and will not be started or stopped based on lifecycle events. In general, loads should be started at the level the result will be used in. If the resource will be used in a view in a child fragment, the load should be started with with(android.app.Fragment)} using that child fragment. Similarly, if the resource will be used in a view in the parent fragment, the load should be started with with(android.app.Fragment) using the parent fragment. In the same vein, if the resource will be used in a view in an activity, the load should be started with with(Activity)}.
This method is appropriate for resources that will be used outside of the normal fragment or activity lifecycle (For example in services, or for notification thumbnails).

@NonNull
public static RequestManager with(@NonNull Context context) {
  return getRetriever(context).get(context);
}

  另人意外的是,Glide此处竟然爆发了强大的求生欲,尽力尝试从Context获取Activity信息,甚至用上了递归调用。

@NonNull
public RequestManager get(@NonNull Context context) {
  if (context == null) {
    throw new IllegalArgumentException("You cannot start a load on a null Context");
  } else if (Util.isOnMainThread() && !(context instanceof Application)) {
    if (context instanceof FragmentActivity) {
      return get((FragmentActivity) context);
    } else if (context instanceof Activity) {
      return get((Activity) context);
    } else if (context instanceof ContextWrapper) {
      return get(((ContextWrapper) context).getBaseContext());
    }
  }
 
  return getApplicationManager(context);
}

  如果Context是Application或在异步线程中,则获取与Application生命周期绑定的RequestManager。

/**
 * The top application level RequestManager.
 */
private volatile RequestManager applicationManager;
 
 
@NonNull
private RequestManager getApplicationManager(@NonNull Context context) {
  // Either an application context or we're on a background thread.
  if (applicationManager == null) {
    synchronized (this) {
      if (applicationManager == null) {
        // Normally pause/resume is taken care of by the fragment we add to the fragment or
        // activity. However, in this case since the manager attached to the application will not
        // receive lifecycle events, we must force the manager to start resumed using
        // ApplicationLifecycle.
        Glide glide = Glide.get(context.getApplicationContext());
        applicationManager =
            factory.build(
                glide,
                new ApplicationLifecycle(),
                new EmptyRequestManagerTreeNode(),
                context.getApplicationContext());
      }
    }
  }
 
  return applicationManager;
}

五、Context.with(View)
方法说明:

Begin a load with Glide that will be tied to the lifecycle of the Fragment, android.app.Fragment, or Activity that contains the View.
A Fragment or android.app.Fragment is assumed to contain a View if the View is a child of the View returned by the Fragment.getView()} method.
This method will not work if the View is not attached. Prefer the Activity and Fragment variants unless you’re loading in a View subclass.
This method may be inefficient aways and is definitely inefficient for large hierarchies. Consider memoizing the result after the View is attached or again, prefer the Activity and Fragment variants whenever possible.
When used in Applications that use the non-support android.app.Fragment classes, calling this method will produce noisy logs from android.app.FragmentManager. Consider avoiding entirely or using the Fragments from the support library instead.
If the support FragmentActivity class is used, this method will only attempt to discover support Fragments. Any non-support android.app.Fragments attached to the FragmentActivity will be ignored.

@NonNull
public static RequestManager with(@NonNull View view) {
  return getRetriever(view.getContext()).get(view);
}

  该方法依然可以看到Glide的强大求生欲

@SuppressWarnings("deprecation")
@NonNull
public RequestManager get(@NonNull View view) {
  if (Util.isOnBackgroundThread()) {
    return get(view.getContext().getApplicationContext());
  }
 
  Preconditions.checkNotNull(view);
  Preconditions.checkNotNull(view.getContext(),
      "Unable to obtain a request manager for a view without a Context");
  Activity activity = findActivity(view.getContext());// The view might be somewhere else, like a service.
  if (activity == null) {
    return get(view.getContext().getApplicationContext());
  }
 
  // Support Fragments.
  // Although the user might have non-support Fragments attached to FragmentActivity, searching
  // for non-support Fragments is so expensive pre O and that should be rare enough that we
  // prefer to just fall back to the Activity directly.
  if (activity instanceof FragmentActivity) { 
    Fragment fragment = findSupportFragment(view, (FragmentActivity) activity);return fragment != null ? get(fragment) : get(activity);
  }
 
  // Standard Fragments.
  android.app.Fragment fragment = findFragment(view, activity);if (fragment == null) {
    return get(activity);}
  return get(fragment);}

① 从View的Context中获取Activity,依然是递归实现

@Nullable
private Activity findActivity(@NonNull Context context) {
  if (context instanceof Activity) {
    return (Activity) context;
  } else if (context instanceof ContextWrapper) {
    return findActivity(((ContextWrapper) context).getBaseContext());
  } else {
    return null;
  }
}

② 如果获取到Activity,是不是就可以使了呢,不,Glide说我要追求极致

@Nullable
private Fragment findSupportFragment(@NonNull View target, @NonNull FragmentActivity activity) {
  tempViewToSupportFragment.clear();
  findAllSupportFragmentsWithViews(
      activity.getSupportFragmentManager().getFragments(), tempViewToSupportFragment);
  Fragment result = null;
  View activityRoot = activity.findViewById(android.R.id.content);
  View current = target;
  while (!current.equals(activityRoot)) {
    result = tempViewToSupportFragment.get(current);
    if (result != null) {
      break;
    }
    if (current.getParent() instanceof View) {
      current = (View) current.getParent();
    } else {
      break;
    }
  }
 
  tempViewToSupportFragment.clear();
  return result;
}

  其中,findAllSupportFragmentsWithViews()通过递归调用获取Activity中所有的Fragment,并以fragment.getView()作为键,fragment作为值,以键值对的形式存入map中

private static void findAllSupportFragmentsWithViews(
    @Nullable Collection<Fragment> topLevelFragments,
    @NonNull Map<View, Fragment> result) {
  if (topLevelFragments == null) {
    return;
  }
  for (Fragment fragment : topLevelFragments) {
    // getFragment()s in the support FragmentManager may contain null values, see #1991.
 if (fragment == null || fragment.getView() == null) {
      continue;
    }
    result.put(fragment.getView(), fragment);
    findAllSupportFragmentsWithViews(fragment.getChildFragmentManager().getFragments(), result);
  }
}

  然后从View逐级往上层布局从map中查找,如果找到fragment,则退出循环,否则直到到达Activity的ViewRoot或上层布局为空。

③ 与②基本相同

④ 如果找到View所在的Fragment,就使用Fragment;

⑤ 如果没有找到View所在的Fragment,且找到View所在的Activity,则使用Activity;

如果都没有,就使用ApplicationContext,生命周期与Application相同。

六、结束语
  虽然这部分代码比较简单,但我还是学了一些知识点。有空还会读下Glide的其他代码。

你可能感兴趣的:(android)