Flutterの通知とアラーム
FlutterプラグインAndroid_Alarm_Managerの操作。
これを知っています!このプラグインはAndroidプラットフォームでのみ機能します!私は個人的にiOSに相当するものを知りません。または、この記事を公開してから1か月後に、AndroidプラットフォームとiOSプラットフォームの両方で通知を提供するプラグインに出くわしました。もちろん、私も記事を書きました。下記参照。
ここで説明するプラグインを使用する場合は、readmeファイルに明示的に従って設定する必要があります。あなたはAndroidManfest.xmlであり、少なくとも以下のようになっているはずです。
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="NAME OF YOUR APPLICATION STARTING WITH COM.">
<!-- The INTERNET permission access.-->
<uses-permission android:name="android.permission.INTERNET"/>
<!-- android_alarm_manager -->
<!-- Start an Alarm When the Device Boots if past due -->
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
<!-- application needs to have the device stay on -->
<uses-permission android:name="android.permission.WAKE_LOCK"/>
<application
android:name="io.flutter.app.FlutterApplication"
android:label="code_samples"
android:icon="@mipmap/ic_launcher">
<activity
android:name=".MainActivity"
android:launchMode="singleTop"
android:theme="@style/LaunchTheme"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
android:hardwareAccelerated="true"
android:windowSoftInputMode="adjustResize">
<meta-data android:name="io.flutter.embedding.android.NormalTheme"
android:resource="@style/NormalTheme"
/>
<meta-data
android:name="io.flutter.embedding.android.SplashScreenDrawable"
android:resource="@drawable/launch_background"
/>
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<!-- android_alarm_manager -->
<service
android:name="io.flutter.plugins.androidalarmmanager.AlarmService"
android:permission="android.permission.BIND_JOB_SERVICE"
android:exported="false"/>
<receiver
android:name="io.flutter.plugins.androidalarmmanager.AlarmBroadcastReceiver"
android:exported="false"/>
<receiver
android:name="io.flutter.plugins.androidalarmmanager.RebootBroadcastReceiver"
android:enabled="false">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
</application>
</manifest>
# https://pub.dev/packages/android_alarm_manager
android_alarm_manager: ^0.4.0
この場合、それはあなたのアプリでアラームを鳴らすためのものです!私の場合、Flutterプラグインandroid_alarm_managerが、作業中の最近のアプリのニーズを満たしていることがわかりました。そのため、簡単に操作できるルーチンを作成しました。必要に応じて、コピーを取り、自分で作成して、改善点を共有してください。涼しい?
時間があれば、このプラグインをほぼ「確実に」機能させるために私がしなければならなかったことを読み続けてください。そうすれば、ある時点で「バックグラウンドで」実行するアラームやその他の操作を簡単かつ迅速に設定できます。アプリの実行中に将来的に。それは人生を少し楽にします、そしてそれは良いことです。正しい?
スクリーンショットのみ。要旨をクリックします。
いつものように、私は記事にコードを表示するために要点よりもスクリーンショットを使用することを好みます。私はそれらが扱いやすく、読みやすいと感じています。ただし、それらをクリック/タップすると、要点またはGithubでコードを表示できます。皮肉なことに、携帯電話よりもコンピューターでのモバイル開発に関するこの記事を読む方が良いでしょう。その上、私たちは主にコンピューターでプログラムします。私たちの電話ではありません。今のところ。
さぁ、始めよう。
ここでの私のアプローチは、最初にこのユーティリティクラスを例で示し、その使用方法を示すことです。したがって、Flutterプラグインandroid_alarm_managerを利用します。実際、プラグイン自体のサンプルページにリストされているのとまったく同じ例を使用します。ただし、この例は、代わりにここに示されているライブラリファイルを利用するように変更されています。この例のコピーは、要点android_alarm_managerとして利用できます。例の後で、ユーティリティクラス自体の一部を見ていき、そのようなクラスを一般大衆である屈託のない大衆が使用できるようにするために何をする必要があるかを説明します。
静的に保つ
この非常に単純な例では、表示されたボタンを押すと、5秒後に、下のスクリーンショットに示されている2つのゼロが1に変わります。ふぅ!もちろん、本当に興味深いのは、コードの内部にあるものです。
アプリが実際に実行を開始する前に実行する必要のある非同期操作に対応するために、コードを元のコードから変更したことに注意してください。これを実現するためにFutureBuilderウィジェットを利用しました。その際、上記のスクリーンショットに示されているように、発生したアラームの総数を「記憶」するために使用される「共有設定」ルーチンを初期化するために、initSettings()という新しい関数を定義しました。
以下に表示されているinitSettings()関数でも確認できます。アプリを初めて実行する場合は、「合計カウント」がゼロに設定されます。ただし、ライブラリルーチンAlarmManagerが初期化されて、Androidフォンで通知を実行するために必要な特定の「アラームサービス」が設定されます。
サンプルコードをさらに下に移動して、その小さなボタンを構成するものを見てみましょう。ライブラリルーチンは、下線を引くFlutterプラグインであるAndroid_Alarm_Managerを構成するパラメーターと関数にまったく同じ名前を使用していることに注意してください。そのようなことと一致している方が良いです。さらに、プラグイン自体のoneShot ()関数と同様に、このライブラリのバージョンは、一度呼び出されると、指定されたコールバックルーチンを起動する前に、指定された期間「待機」します。この変更された例の場合、コールバックルーチンは、5秒後に実行される無名関数です。以下のスクリーンショットでは、実際、アプリが再び起動され、ボタンが再度押されました。これは、共有設定のおかげで、ボタンが初めて実行されてから2回押されたことを示しています。ふぅ。
以下の無名関数を詳しく見ると、整数型の単一パラメーターが渡されていることがわかります。これは、Dart関数Random()。nextInt(pow(2,31))を使用して、乱数として2番目のパラメーターに渡されるID値とまったく同じであると推測できます。では、その値がそのすぐ横のパラメーターに既に渡されているのに、なぜわざわざその値を渡すのでしょうか。それについて説明します。
今のところ、コールバック関数が関数_incrementCounter()を呼び出すことを以下でさらに確認できます。そこでは、ボタンを押した現在の「合計数」が「共有設定」ルーチンから取得されます。次に、この現在のボタンの押下を考慮して1つずつ更新され、共有設定に保存されます。次に、関数setState()を使用して、増分変数_counterでアプリの画面が更新されます。これまでのところすべてをフォローしましたか?
静的に保つ
元の例(以下を参照)とは異なり、この例では無名関数を使用でき、静的関数を特に使用する必要はありません。もちろん、この例でも静的関数を使用できます—それがその唯一の整数値を受け入れる限り。ただし、元の例では、静的関数またはそのライブラリのDartファイル内のクラスの外部で定義された高レベル関数である必要があります。FlutterプラグインであるAndroid_Alarm_Managerを直接操作する場合は、どちらかが要件です。
しかし、あなたが私の記事をフォローしているなら、あなたは私がオプションが好きであることを知っています。それはすべて私と一緒にオプションを持つことです。このユーティリティクラスは、たとえば無名関数を使用できるようにするために、より柔軟に作成しました。したがって、このような配置では、そのid整数値をパラメーターとして渡すことで、関数を静的関数、高レベル関数、または無名関数にすることができます。オプション!繰り返しますが、元の例のスクリーンショットは以下のとおりです。
ユーティリティクラスAlarmManager自体に目を向けましょう。繰り返しますが、プラグインで動作するように設計されています。繰り返しになりますが、多くのパラメーターはプラグインで使用されるパラメーターとまったく同じであるため、そのプラグインに渡されますが、有効な値の広範なパラメーターテストの前ではありません。そのようなユーティリティクラスのもう1つの必要な特性。それはあなたがする必要がないようにすべての仕事をします。正しい?
以下のスクリーンショットでは、がこのユーティリティクラスの最初の部分です。その静的関数init()では、プラグインが実際に初期化されていることがわかります。初期化の試行中に発生する可能性のある不幸なエラーは、try-catchステートメントでキャッチされることに注意してください。ユーティリティクラスもそれを行う必要があります。次にinit()関数には、「ヘルパー」クラス_Callbackがあります。これは、アプリがバックグラウンドでアラームサービスによって使用される個別のIsolateと通信するために必要な手段を初期化するために呼び出されます。
最後に、null以外のすべてのパラメータ値が、ユーティリティクラスAlarmManagerを構成する特定の静的プロパティに割り当てられていることがわかります。この辺りで起こっている多くのスタティックはそこにはありません。
このクラスを構成するプロパティと関数の多くは静的であることがわかります。ただし、クラスで静的メンバーを使用するという選択は、十分に調整する必要があります。たとえば、init()関数は静的であるため、いつでもどこでも何度でも呼び出すことができます。その事実には、いくつかの追加の考慮が必要です。この例では、上のスクリーンショットの最初の1行のステートメントはifステートメントです: 'i f(_init)return_init;'。このinit()関数を書くときに必要でした。これで、その関数を何度でも呼び出すことができます。とにかく、必要なサービスとプラグインは最初の呼び出しでのみ初期化されます。したがって、たとえば、開発者のチームでは、init()関数の呼び出しが誤って複数回行われた場合でも、害はありません。ユーティリティクラスのもう1つの望ましい特性。私がここで何をしているのか分かりますか?それを「絶対確実」にします。正しい?
設定を初期化する
ちなみに、これらのパラメーターはこれらの静的変数に渡されるため、この例ではより多くのオプションがあります。ときのinit()関数が呼び出されると、設定が代わりにその場で指定されている可能性があります。そうすることで、関数oneShot()、oneShotAt()、およびperiodic()への後続のすべての呼び出しで、明示的に独自の設定を提供しない場合でも、これらの設定を使用できるようになります。これを以下に示しました。代わりにinit()関数の追加パラメーターを使用した場合、サンプルコードでの違いを確認できます。これにより、「oneShot」関数呼び出しとその期間、ID、および必要なコールバック関数が残ります。コードを少しすっきりさせます。オプション!
D build()関数にinit()を配置しないでください!AndroidAlarmManagerと呼ばれるFlutterプラグイン独自のinit()関数が表示されます。initialize();は、副作用や問題を引き起こす傾向があります。場合によっては、再構築を開始します(setState()関数を呼び出すのとよく似ています)。そのため、私のユーティリティクラスには個別のinit()関数があります。たとえば、MaterialAppを使用するFutureBuilderウィジェットでは、アプリの開始近くで呼び出すことをお勧めします。自分の目で確かめて、変更した例では、initSettings()からAlarmManger.init()関数をコメントアウトし、代わりにoneShot()関数の直前に配置してみてください(以下を参照)。その後、例でエラーが発生し始めます。
あなたのoneShotを取る
わかりました、ユーティリティクラスに戻ります。でONESHOT()関数で、最初の3つは、パラメータ値は有効性をテストし、プラグイン自身に渡され「必要な」ワンショット()関数。'Function'パラメータ、コールバックを除くすべて。代わりに、次のコマンド_Callback.oneShots [id] = callbackを使用して、指定された整数IDによって一意に識別される静的Mapオブジェクトに追加されます。すぐに戻ってきます。最後に、静的関数oneShot()の呼び出しがあり、これもそのヘルパークラス_Callbackにあります。これは、プラグインが使用するために必要な「静的関数」として機能します。これにより、残りのパラメーター値は、null合体演算子??を使用してこれらの多くの静的変数を取り込むことができます。。演算子が使用されるため、明示的なパラメーターが渡されない場合は、代わりにそれらの静的変数の値が使用されます。それを得る?ちなみに、これらの静的変数はすべてデフォルト値で初期化されるため、最終的にプラグイン自体に渡されるnull値はありません。いいね。
チャンスを逃さない
プラグイン自体の呼び出しもtry-catchステートメントで囲まれていることに注意してください。それはサードパーティのプログラムだからです。何が起こるかわかりません。これはユーティリティクラスであるため、アプリをクラッシュさせたくはありませんが、発生する可能性のある例外をキャッチします。
さらに、他の優れたユーティリティクラスと同様に、このクラスには、開発者が操作が成功したかどうかを「テスト」する手段があります。そうでない場合は、発生した可能性のある例外が記録されるため、開発者はそれを処理できます。これは、新しく挿入されたifステートメントを使用した変更された例で以下に示されています。
より静的
さらにユーティリティクラスであるAlarmManagerに沿って。私たちは、参照oneShotAt()関数を。繰り返しますが、これらの関数はすべて静的関数であるため、いくつかのセーフガードをコードに組み込む必要があります。たとえば、不幸な状況では、このonShotAt()関数が呼び出されたときにプラグインが最初に初期化されない場合があります。つまり、init()関数が最初に呼び出されなかったということです。一般の人が使用すると発生する可能性があります。以下のスクリーンショットでわかるように、このような状況はassert()関数でテストされています。これは、開発者が開発中にそのような間違いを見つけてくれることを願っています。本番環境では、assert()関数に続くifステートメントによってキャッチされます。
このoneShotAt()関数には、渡された 'callback'関数を格納するための独自のMapオブジェクトがあり、プラグイン自体のoneShotAt()関数に渡される独自の静的関数_Callback.onShatAt()があることに注意してください。ちなみに、これはすべて、アプリ内でこれらの関数を何度でも呼び出すことができ、将来発生する操作をいくつでもスケジュールできることを意味します。もちろん、それぞれに固有のID値を割り当てる必要があります。それ以外の場合、同じID値を使用すると、すでにスケジュールされている操作は新しい操作で上書きされます。これが、一意のIDを使用する場合のポイントです。正しい?
ただし、これはすべて、同じIDを使用できることも意味しますが、oneShot()、oneShotAt()、periodic()の3つの異なる関数間で別々に使用できます。それらには独自の個別のMapオブジェクトと静的関数があることを忘れないでください。この事実は、使用されたIDが常駐データベースのプライマリフィールドで見つかった値そのものである最近のプロジェクトで私に役立ちました。オプション、ベイビー!大好きです!
プラグインのピーク
Flutterプラグイン自体のoneShot()関数とoneShotAt()関数をざっと見てみると、実際には、そのoneShot()関数がそのパラメーターをoneShotAt()の対応する関数に渡すだけであることがわかります。以下のスクリーンショットに表示されている「CallbackHandle」オブジェクトは、関数_getCallbackHandle()からのものであり、Flutterのフレームワーク関数PluginUtilities.getCallbackHandle(callback)と呼ばれていることに注意してください。この操作は、コールバック関数のコピーを「ティアオフ」して、それにアクセスし、バックグラウンドで実行されているIsolateでそのような関数を呼び出すようにします。私もそれに戻ります。
コールバック操作
とりあえず続けて、ライブラリファイルの「ヘルパークラス」_Callbackを見てみましょう。以下に、コールバック関数オブジェクトに追加されているMapオブジェクトが、このクラスで静的プロパティとして定義されていることを確認します。このクラスにはinit()関数もあり、AlarmManger独自のinit()関数で呼び出されます。このinit()関数では、「通信ポート」が3つの特定の名前識別子で登録されます。ポートは、バックグラウンドの分離によって使用され、メッセージを渡すことによってフォアグラウンドの分離と通信します。それらの名前識別子の値は何だと思いますか?それらは、変数_oneShot、_oneShotAt、および_periodicに格納されている以下のスクリーンショットに表示されます。
名前が示すように、分離
は、設計上、分離されたメモリの個別のセグメントです。メモリの共有はありません。Isolate間でのメッセージの受け渡しのみがあります。このようなメッセージの内容は、プリミティブ値(null、num、bool、double、String)、SendPortオブジェクトのインスタンス、Listオブジェクト、または最初に述べたプリミティブ値のいずれかを持つMapオブジェクトにすることができます。
バックグラウンドで聞く
ポートにはさらに「リスナー」が割り当てられ、メッセージが受信された場合、この場合は、アラームサービスを実行しているバックグラウンドの分離によって応答します。リスナーは、Mapオブジェクトをパラメーターとして受け取る無名関数の形式です。以下に、Mapオブジェクトに整数値(たまたまid)と、それらの「名前識別子」の1つを格納する文字列が含まれていることがわかります。次に、caseステートメントは、どの「タイプ」の関数を起動するかを決定します。それがどのように機能するか見てみましょう。もちろん、ユーティリティクラスであるため、すべてtry-catchステートメントで囲まれています。たとえば、選択した関数を実行するとどうなるかわかりません。発生する可能性のある例外をキャッチしたいと思います。正しい?
メッセージを送ります
では、そのメッセージはフォアグラウンドで実行されているアプリにどのように送信されますか?さて、これらの名前識別子が上に登録されたら、(以下を参照)3つのユーティリティクラス関数のいずれか、AlarmManager。oneShot()、AlarmManager。oneShotAt()、およびAlarmManager。定期的()は、対応する3つの静的関数_Callbackを渡します。onShot()、_ Callback。onShotAt()および_Callback。定期的()、Flutterプラグインに直接。そうすることで、バックグラウンドのIsolateが、フォアグラウンドのIsolateで実行されているアプリにメッセージを返すことができます。3種類の通話すべてを以下に示します。
ご覧のとおり、これはこれら3つの対応する静的関数_Callbackです。onShot()、_ Callback。onShotAt()および_Callback。周期()、つまりバックグラウンド分離からフォアグラウンド分離への「ブリッジ」です。たとえば、アラームを鳴らすときは、アラームサービスがこれら3つの静的関数のいずれかを呼び出します。たまたま、それはフォアグラウンドIsolateで定義された実際の関数ではなく、その「引き裂かれたコピー」であることに注意してください。この事実のために、直感に反する動作が見られます。たとえば、フォアグラウンドIsolateで通常定義されているその関数の静的変数は、バックグラウンドIsolateではnullになります。この現象は自分でテストできます。
たとえば、変更した例では、ボタンを3回押すと、MapオブジェクトoneShotsに、5秒後に画面を実行および更新するための1つのFunctionオブジェクトが含まれていることがわかります。これは、スクリーンショットで実行されます。以下の「リスナー」ルーチンの ただし、その過程で、バックグラウンドでアクセスした場合、そのMapオブジェクトは空になりますIsolate?!そんなことがあるものか?これは、前景のIsolateにあるものではなく、Mapオブジェクトのコピーであるために可能です。繰り返しますが、Isolatesは「メッセージ」のみを相互に渡すことができます。それらはメモリを共有しません。
この場合も、これら3つの静的関数はヘルパークラス_Callbackにあり、次々にリストされています。それらは下のスクリーンショットに表示されています。それぞれで、前景のIsolateが「名前識別子」を使用して参照され、背景のIsolateからMapオブジェクトが渡されていることがわかります。条件付きメンバーアクセス演算子?に注意してください。、ルックアップ操作がnullを返す場合に使用されます。たとえば、名前が存在しない場合にそうします。これはすべて内部化されたコードであるため、発生する可能性はほとんどありませんが、ユーティリティクラスであるため、チャンスはありません。正しい?
これはすべてライブラリファイルの最後にあり、変数_oneShot、_oneShotAt、および_periodicでこれらの「名前識別子」の値が最終的に表示される場所です。それらはそれぞれ、対応する関数タイプにちなんで名付けられています。あまり想像力に欠けますが、それは理にかなっています。また、それらはクラスまたは高レベル関数の外部で定義された高レベル変数であることがわかります。実際、これらはキーワードconstを持つ定数変数です。最終変数と同様に、const変数は一度だけ初期化され、その後変更することはできません。最終変数とは異なり、アプリのコンパイル時に定義されます。したがって、ここでのニーズのために、それらはバックグラウンドの分離物にも利用できます。いいね。
このすべてのIsolateのものを処理できない場合。今は気にしないでください。それがユーティリティクラスの目的です。元のプラグインのサンプルコードとは異なり、「通信ポート」の設定や、静的または高レベルの関数や変数をいつ使用するかについて心配する必要はありません。そもそもこのクラスを書いたのはそのためです。そのため、これらすべてについて心配する必要もありません。そのため、このようなユーティリティクラスはまったく作成されていません。そのため、将来作成する多くのアプリで何度も使用できます。正しい?
乾杯。