Lifecycle対応コンポーネントに対応する方法

最近のAndroidでは、Lifecycle#addObserverを使うことで様々なクラスでコンポーネントのライフサイクルイベントを処理することができるようになりました。
例えば以下のように記述することで対象のLifecycleOwnerのonResume時に呼ばれます。

lifecycle.addObserver(object: LifecycleObserver {
  @OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
  fun onResumeMethod() {
    // ここが対象のLifecycleOwnerのonResume時に呼ばれます。
  }
})

@OnLifecycleEventアノテーションを付けることで簡単にライフサイクルに対応させることができます。
実は使用する依存関係とLifecycle#addObeserverに渡されるクラスによって内部でリフレクションが使われたり、使われなかったり色々と処理が変わるので内部処理を見ていきたいと思います。

Lifecycle対応コンポーネントを使う場合は必要な機能を以下の依存関係から設定する必要があります。

dependencies {
    def lifecycle_version = "2.2.0"
    def arch_version = "2.1.0"

    // ViewModel
    implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version"
    // LiveData
    implementation "androidx.lifecycle:lifecycle-livedata-ktx:$lifecycle_version"
    // Lifecycles only (without ViewModel or LiveData)
    implementation "androidx.lifecycle:lifecycle-runtime-ktx:$lifecycle_version"

    // Saved state module for ViewModel
    implementation "androidx.lifecycle:lifecycle-viewmodel-savedstate:$lifecycle_version"

    // Annotation processor
    kapt "androidx.lifecycle:lifecycle-compiler:$lifecycle_version"
    // alternately - if using Java8, use the following instead of lifecycle-compiler
    implementation "androidx.lifecycle:lifecycle-common-java8:$lifecycle_version"

    // optional - helpers for implementing LifecycleOwner in a Service
    implementation "androidx.lifecycle:lifecycle-service:$lifecycle_version"

    // optional - ProcessLifecycleOwner provides a lifecycle for the whole application process
    implementation "androidx.lifecycle:lifecycle-process:$lifecycle_version"

    // optional - ReactiveStreams support for LiveData
    implementation "androidx.lifecycle:lifecycle-reactivestreams-ktx:$lifecycle_version"

    // optional - Test helpers for LiveData
    testImplementation "androidx.arch.core:core-testing:$arch_version"
}

この中で以下のように記述されている箇所があります。

// Annotation processor
kapt "androidx.lifecycle:lifecycle-compiler:$lifecycle_version"
// alternately - if using Java8, use the following instead of lifecycle-compiler
implementation "androidx.lifecycle:lifecycle-common-java8:$lifecycle_version"

これの設定次第でLifecycle#addObserverの引数に渡すLifecycleObserverの処理に違いが出てきます。

@OnLifecycleEventでのライフサイクルイベントへの対応

どちらの依存関係も記述しない場合

addObserverの処理の中で、リフレクションを使って対象のクラスを保持するように処理されています。
具体的には後述します。

androidx.lifecycle:lifecycle-compilerの依存関係を記述した場合

このアノテーションプロセッサーは実は、@OnLifecycleEventがついているメソッドを呼び出すGeneratedAdapterというクラスを生成します。
addObserverの処理の中でリフレクションを使って対象のGeneratedAdapterクラスを保持するように処理されます。

public class MyLifecycleObserver_LifecycleAdapter implements GeneratedAdapter {
  final MyLifecycleObserver mReceiver;

  MyLifecycleObserver_LifecycleAdapter(MyLifecycleObserver receiver) {
    this.mReceiver = receiver;
  }

  @Override
  public void callMethods(LifecycleOwner owner, Lifecycle.Event event, boolean onAny,
      MethodCallsLogger logger) {
    boolean hasLogger = logger != null;
    if (onAny) {
      return;
    }
    if (event == Lifecycle.Event.ON_RESUME) {
      if (!hasLogger || logger.approveCall("onResumeMethod", 1)) {
        mReceiver.onResumeMethod();
      }
      return;
    }
  }
}

このクラスを使ってイベントに対応されます。

androidx.lifecycle:lifecycle-common-java8の依存関係を記述した場合

このクラスは、@OnLifecycleEventと直接関係ないのですがDefaultLifecycleObserverが追加されこのクラスを使うことで対応します。
このクラスはJava8からinterfaceに追加されたインターフェイスのデフォルト実装を使うことで実現されています。

class MyDefaultLifecycleObserver : DefaultLifecycleObserver {
    override fun onResume(owner: LifecycleOwner) {
        super.onResume(owner)
        // このメソッドがonResumeのタイミングで呼び出されます
    }
}

実際のLifecycle#addObserverの処理

前提としてライフサイクルイベントはLifecycleEventObserver経由で実際には登録されているLifecycleOwnerに通知されます。
なのでaddObserverで引数に渡したLifecycleObserverをどうにかしてLifecycleEventObserver経由で通知をできる仕組みを作っています。

その仕組みを作っているのがaddObserverの内部で呼び出されるLifecycling.lifecycleEventObserverメソッドです。

Lifecycling.lifecycleEventObserverの内容

  1. 引数がLifecycleEventObserverのインスタンスの場合
    • LifecycleEventObserverをそのまま保持して、ライフサイクルイベントを通知します。
  2. 引数がFullLifecycleObserverのインスタンスの場合(DefalutLifecycleObserverFullLifecycleObserverを継承しているinterfaceです)
    • FullLifecycleObserverAdapterというクラスでLifecycleObserverを保持して、ライフサイクルイベントを通知します。
  3. GeneratedAdapterが存在する場合
    • リフレクションを使って対象クラスのGeneratedAdapterが存在するかクラス名を元に探します。
      存在したらそのクラスでLifecycleObseverを保持して、ライフサイクルイベントを通知します。
  4. 上記以外
    • ReflectiveGenericLifecycleObserverというクラスでLifecycleObserverを保持して、ライフサイクルイベントを通知します。
      通知時にもリフレクションを使って対象メソッドが呼ばれます。
      @OnLifecycleEventを使っていてandroidx.lifecycle:lifecycle-compilerのアノテーションプロセッサーを使っていなかった場合にこのパターンになります。
static LifecycleEventObserver lifecycleEventObserver(Object object) {
    boolean isLifecycleEventObserver = object instanceof LifecycleEventObserver;
    boolean isFullLifecycleObserver = object instanceof FullLifecycleObserver;
    if (isLifecycleEventObserver && isFullLifecycleObserver) {
        return new FullLifecycleObserverAdapter((FullLifecycleObserver) object,
                (LifecycleEventObserver) object);
    }
    if (isFullLifecycleObserver) {
        return new FullLifecycleObserverAdapter((FullLifecycleObserver) object, null);
    }

    if (isLifecycleEventObserver) {
        return (LifecycleEventObserver) object;
    }

    final Class<?> klass = object.getClass();
    int type = getObserverConstructorType(klass);
    if (type == GENERATED_CALLBACK) {
        List<Constructor<? extends GeneratedAdapter>> constructors =
                sClassToAdapters.get(klass);
        if (constructors.size() == 1) {
            GeneratedAdapter generatedAdapter = createGeneratedAdapter(
                    constructors.get(0), object);
            return new SingleGeneratedAdapterObserver(generatedAdapter);
        }
        GeneratedAdapter[] adapters = new GeneratedAdapter[constructors.size()];
        for (int i = 0; i < constructors.size(); i++) {
            adapters[i] = createGeneratedAdapter(constructors.get(i), object);
        }
        return new CompositeGeneratedAdaptersObserver(adapters);
    }
    return new ReflectiveGenericLifecycleObserver(object);
}

Lifecycleイベントを処理するおすすめの実装方法は?

以下のように記述されているようにandroidx.lifecycle:lifecycle-common-java8を使うことをすすめているように思いますが、@OnLifecycleEventを使う方が処理はわかりやすくなるので自分としては好きです。
チームで意思統一ができていればいいのではと思っています。

// alternately - if using Java8, use the following instead of lifecycle-compiler
implementation "androidx.lifecycle:lifecycle-common-java8:$lifecycle_version"

注意事項

  • 利用する方法によっては内部でリフレクションが使われているので難読化などでエラーが発生しないように注意が必要になります。
  • リフレクションは普通のメソッド呼び出しよりは動作が遅くなります。