9
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Android上でのFlutterアプリライフサイクル

Posted at

最初に

Androidアプリではライフサイクルにとても気を遣う必要があります。なので、AndroidエンジニアとしてはFlutter上ではどのようなライフサイクル管理が必要か気になったので調べてみました。

環境はFlutter v1.5.4、Android 9.0です。

Flutterの画面ライフサイクル

Androidではうんざりするほどの数のライフサイクルコールバックがありますが、Flutterのライフサイクルはかなりシンプルです。ドキュメントのAppLifecycleStateに状態があります。
要約すると次のように書かれています。

  • resumed
    アプリが表示され、ユーザの入力を受け付けます。
  • inactive
    アプリは非アクティブで、ユーザの入力を受け付けていない状態です。
    Androidでは、アプリはフォアグラウンドで表示されていますがフォーカスを失っていてユーザの入力を受け付けない状態です。
  • paused
    アプリは一時停止状態で、ユーザからは見えず、入力も受け付けていません。
    Androidアプリではsuspending状態にいつでも入る可能性があることを想定してください。
  • suspending
    アプリは一時的に中断されます。iOSでは現在未使用です。

なんとなくAndroidのライフサイクルとの対応が見えてきますね。

Flutterのライフサイクル取得方法

Flutterで、ライフサイクルを取得するにはWidgetsBindingObserverを実装し、didChangeAppLifecycleStateメソッドをオーバーライドします。具体的には次のようにします。

class _MyHomePageState extends State<MyHomePage> with WidgetsBindingObserver {

  @override
  void initState() {
    super.initState();
    WidgetsBinding.instance.addObserver(this);
  }

  @override
  void didChangeAppLifecycleState(AppLifecycleState state) {
    debugPrint("lifecycle state change=$state");
  }

実際に動かしてみる

  • アプリ起動時
    inactive -> resumedが連続で発生します。inactiveが先に来ているのが奇妙ですね・・・。
  • HOMEボタンを押してアプリを非表示にする
    inactive -> pausedが連続で発生します。ここでもinactiveが発生します。
  • HOMEボタンを押してバックグラウンドに移動したアプリを履歴から再表示する
    inactive -> resumedが連続で発生します。やっぱりinactiveが先に来ます。
  • 画面回転させる
    何もイベントは発生しません。

他にもいろいろやってみますが、suspendingは発生しませんでした。また、アプリ表示時にinactiveが来てresumedが来るのが奇妙です・・・。

Flutterエンジンのソースコードで確認してみる

アプリを非表示にしても表示にしてもinactiveが発生するのが奇妙なので、どんなソースコードになっているのか確認してみましょう。
FlutterView.javaでAndroidのライフサイクルイベントをFlutterのライフサイクルイベントに変換しています。

    public void onStart() {
        lifecycleChannel.appIsInactive();
    }

    public void onPause() {
        lifecycleChannel.appIsInactive();
    }

    public void onPostResume() {
        for (ActivityLifecycleListener listener : mActivityLifecycleListeners) {
            listener.onPostResume();
        }
        lifecycleChannel.appIsResumed();
    }

    public void onStop() {
        lifecycleChannel.appIsPaused();
    }

おっと、なぜかonStartでinactiveイベントを送信しています。onStartとonPauseが同じinactiveイベントです。resumedがAndroidのonResume、pausedがAndroidのonStopに対応している確認が取れましたね。しかしsuspendingはどこでも送信していません。Flutterエンジンのソースコードをgrepしても、定義は見つかりますが送信している場所はありませんでした。iOSでは使われていないとありましたが、Androidでも使われていないのでしょうか。Gitのログから確認してみましょう。

Suspendingが使われていない理由

Gitのログを漁ると、このPR Do not pause rendering when android activity loses focusが見つかります。

元々は、pausedがAndroidのonPause、suspendingがAndroidのonStopに対応していたことがわかります。そして、バグの修正のためonStopにはpausedが対応することになったようです。Androidにマルチウインドウ機能が追加され、onPauseとonStopの意味合いが若干変わったため仕方ない修正ですね。
この時点でsuspendingイベントは発生しなくなりました。

アプリ表示時にinactiveイベントが発生する理由

次に、なぜAndroidのonStartでinactiveイベントを送信しているか見てみます。

add onStart hook which places flutter in an inactive stateのPR
Android's FlutterView works unexpected when there's a translucent activity above it.のバグを修正したようです。
onStartが呼ばれて、onResumeが呼ばれないケースで期待通りの動作をして欲しいのでonStartでinactiveイベントを送信しているようです。
inactiveは、アプリは表示されていますがフォーカスを得ていない状態なので、確かにonStartで送信されるべきイベントです。

まとめ

AndroidのライフサイクルコールバックとFlutterのライフサイクルイベントの対応は次の通りです。

  • inactive
    onStart、onPause
  • resumed
    onResume
  • paused
    onStop
  • suspending
    Androidでも未使用

Flutterのライフサイクルと、Androidのライフサイクルの対応がわかりました。ただ、バグ修正のために結構思い切った変更が入るので、なんとなくWidgetsBindingObserverも用いたFlutterのライフサイクルの取得は、アプリ開発者が気にするための物では無く、Flutter自体が正しく動作するために利用しているものという印象を受けました。

9
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
9
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?