15
6

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 3 years have passed since last update.

Flutterでバックグラウンドからのコールバック処理で躓いたこと

Last updated at Posted at 2020-01-23

Flutterアプリでネイティブ処理をバックグラウンドで動かした時に躓いたことのメモです。

前提

※発生環境
Flutter (Channel beta, v1.13.6, on Mac OS X 10.15.2 19C57, locale ja-JP)
Android 9
iOS 13

公式での概要
https://flutter.dev/docs/development/packages-and-plugins/background-processes

上記公式がおすすめしている詳細とサンプル
https://medium.com/flutter/executing-dart-in-the-background-with-flutter-plugins-and-geofencing-2b3e40a1a124

コールバックで可能なこと

コールバック自体はbackground isolateで実行されるので、main isolateで動いてる画面等が直接参照することはできません。ですが、ReceiverPort/SenderPortを使うことでbackground isolateからmain isolateへデータを送信することができます。

static String sendPortName = "myapp_send_port_name";

//background isolateで実行される。実際の使い方は上のサンプルを参照
void callback(data) {
    //background isolateからmain isolateにデータを送る
    final SendPort send = IsolateNameServer.lookupPortByName(sendPortName);
    send?.send(data);
}

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() {
    return _MyAppState();
  }
}

class _MyAppState extends State<MyApp> {
  @override
  void initState() {
    super.initState();
    _initPort();
  }
  Future<void> _initPort() async {
    _rPort = ReceivePort();
    final _sPort = _rPort.sendPort;

    // register~は上書き更新できないので以前のStateが作成したものを消しておく
    IsolateNameServer.removePortNameMapping(sendPortName);
    IsolateNameServer.registerPortWithName(
        _sPort, sendPortName);
    // background isolateからデータを受け取って画面に反映
    _rPort.listen((data) {
        setState(() {
           //dataを_MyAppStateに反映させる
        });
    });
  }

  @override
  Widget build(BuildContext context) {
   //ボタンタップしたら何かのバックグラウンド処理を開始するような画面
  }
}

コールバックで不可能だったこと

MethodChannelを使ったネイティブ実行ができませんでした(MissingPluginExceptionがthrowされる)、つまりpluginが使えません。実際に以下を試したが無理でした。

  • flutter_local_notificationsを使ってデータをローカル通知で表示する。
  • sqfliteを使ってデータを格納する
  • shared_preferencesを使ってデータを格納する

これらを実行したければ、background isolateからmain isolateにデータを渡してそちらでやるしかないようです。

main isolateが死んでたらどうする?

例えばAndroidでマルチタスク画面からアプリを上スワイプで消したとします。この時、background isolateは生きてますが、main isolateは消えます(知識が不正確なのでこの表現で正しいか怪しい)、明示的にこの動作をしなくてもOSの都合によって同じ状態になることもあるでしょう。この場合、main isolate側に記述した処理は当然実行されません。

対応方法としては、callbackではなくネイティブ側に該当の処理(通知、DB保存etc)を個別に書くしかないのではと考えています(まだ試していない)。flutterのplugin機能で共通API化したはずの機能を再度ベタ書きするという悲しみ。。。

15
6
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
15
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?