Glide.withに渡す引数による処理の違いについて

Androidの画像読み込みライブラリの Glide で画像を読み込む際に、 Glide.with に渡す引数による処理の違いについて書きます。

Glide.withの処理を追いかける

Glide.with では以下のように getRetriever(Context) を使って取得した RequestManagerRetrieverget メソッドに with の仮引数を渡して、最終的に RequestManager を取得しています。 この流れは仮引数の型が ActivityView の場合も同様です。

public static RequestManager with(@NonNull Context context) {
    return getRetriever(context).get(context);
}
private static RequestManagerRetriever getRetriever(@Nullable Context context) {
    ...
    return Glide.get(context).getRequestManagerRetriever();
}

RequestManager を生成するまで

RequestManagerRetriever#get には Glide.with と同じく ContextActivity, View など様々な引数をとるメソッドが定義されています。

まずはじめに、 ApplicationContext を指定した場合の処理を見ていきます。 get(Context) のコードは以下のようになっており、 FragmentActivityActivity などにキャストできる場合はキャストしてから再度 get を呼び出しています。

ApplicationContext の場合は getApplicationManager(Context) が呼ばれ、その中で RequestManager が作成されます。

@NonNull
public RequestManager get(@NonNull Context context) {
  if (context == null) {
      ...
  } 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
        && ((ContextWrapper) context).getBaseContext().getApplicationContext() != null) {
      return get(((ContextWrapper) context).getBaseContext());
    }
  }
  return getApplicationManager(context);
}
private RequestManager getApplicationManager(@NonNull Context context) {
    ...
    if (applicationManager == null) {
        Glide glide = Glide.get(context.getApplicationContext());
        applicationManager =
            factory.build(
                glide,
                new ApplicationLifecycle(),
                new EmptyRequestManagerTreeNode(),
                context.getApplicationContext());
    }
    ...
    return applicationManager;
}

次に Activity を指定した場合や、ContextActivity にキャストできた場合の処理について見ていきます。 バックグラウンドスレッドで呼び出された場合は、先ほどみた ApplicationContext を指定した時と同じ処理が呼ばれます。
そうでない場合は Factory から RequestManager を生成します。生成したインスタンスRequestManager を管理するための Fragment で保持されています。
getRequestManagerFragment では、 1つの FragmentManager に対して1つの RequestManagerFragment が保持されるように処理が行われています。

public RequestManager get(@NonNull Activity activity) {
    if (Util.isOnBackgroundThread()) {
        return get(activity.getApplicationContext());
    } else {
        assertNotDestroyed(activity);
        android.app.FragmentManager fm = activity.getFragmentManager();
        return fragmentGet(activity, fm, /*parentHint=*/ null, isActivityVisible(activity));
    }
}
private RequestManager fragmentGet(
        @NonNull Context context,
        @NonNull android.app.FragmentManager fm,
        @Nullable android.app.Fragment parentHint,
        boolean isParentVisible) {
    RequestManagerFragment current = getRequestManagerFragment(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;
}

FragmentFragmentActivity, androidxの Fragment の場合も FragmentManager を取得して Activity の時と同様の処理が行われます。
また、 View の場合は View#getContext を元に Activity とその配下のすべての Fragment を取得して、 View が属する FragmentActivity を探し出しています。

RequestManagerRetriever#get(View)
RequestManagerRetriever#findSupportFragment

RequestManagerを生成した後

ここまでで Glide.with の返り値である RequestManager を生成するタイミングが特定でき、以下のように大きく2パターンの RequestManager が生成されることがわかりました。

ApplicationContextまたはバックグラウンドスレッド上で生成

factory.build(
    glide,
    new ApplicationLifecycle(),
    new EmptyRequestManagerTreeNode(),
    context.getApplicationContext()
);

それ以外

factory.build(
    glide,
    current.getGlideLifecycle(), 
    current.getRequestManagerTreeNode(), 
    context
);

factory

Factoryは、GlideBuilder で指定することができます。指定しない場合は RequestManagerRetriever.DEFAULT_FACTORY が使用されます。
RequestManagerRetriever.DEFAULT_FACTORY

Lifecycleによる違い

RequestManager を生成する処理で、注目したいことは Lifecycle の違いです。
ApplicationLifecycle は常にアクティブなライフサイクルとなっています。
一方、 RequestManagerFragment#getGlideLifecycle()Fragment のライフサイクルと等しくなります。

Glideでは、この Lifecycle に応じて画像読み込みリクエストの開始/中断などが行われます。例えば、 Fragment で画像を読み込みリクエストを開始し、読み込み完了前に Fragment が破棄された時にリクエストを中断するということが可能です。
バックグラウンドスレッドでリクエストを行う時には、 ActivityFragment のライフサイクルに応じてリクエストを止めたくないケースであることが想定されるため、 ApplicationLifecycle で実行させているということが分かりました。

まとめ

Glide.with に渡す引数によって、画像読み込み処理のライフサイクルが変わります。
ActivityFragment と同じライフサイクルで画像を読み込めば十分な時には、 ActivityFragment, View を渡すと端末のリソースが無駄に使用されることがなくなります。そうでない場合は、ApplicationContext を渡したり、バックグラウンドスレッド上で Glide.with を呼び出す必要があります。