-
Notifications
You must be signed in to change notification settings - Fork 113
Fix issue that background task fail on Android. #30
Fix issue that background task fail on Android. #30
Conversation
…iles for prevent fail from background worker.
…ut also on MainActivity.onCreate
現状、本PR で追加したコンテナと Prism が提供する Appクラスのコンテナとの関連がないので、いくつかの Singletonオブジェクトが重複してしまうのではないかと思います。 子コンテナを作成するのであれば下記が参考になりそうです。 あと |
勢いを止めるようで大変心苦しいのですが、本件はキリの良いところで一旦止めておいた方が良いように思います。 |
おっしゃるとおり、開発チームも改修作業に入っているかもしれませんし、もしかしたら修正済でテストが行われているかもしれません。もしかすると、すでにテストまで完了していて、月曜日にも新しいバージョンがリリースされるかもしれませんね。 以前、こういうコメント( #21 (comment) )をつけたことがあります。 現在のCocoaはオープンソースの方に舵を切りつつあると認識していています。ぼくも言ったことには責任をとらなきゃな。という感じです。 最終的に、Androidで定期実行がうまくいかない問題が解決すればいいわけなので、問題が解決したバージョンがリリースされればもったいなさは許容できる範囲です(使った時間は土、日ですし)。 一方で、開発チーム側で調査が難航していたり、対応策の検討が進んでいない場合、ぼくの作ったコードがなにかの形で開発チーム側の役に立つことも若干ながら期待してたりもします。 あとは、ただのWorkManagerの制約の設定が問題なのだろうと思って開けてみたら、なかなか一筋縄ではいかなくて驚いていたりします。 |
533f30d
to
7467284
Compare
App.InitializeServiceLocator(RegisterPlatformTypes); | ||
|
||
App.InitializeExposureNotification(); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
iOSにおけるAndroidのMainApplicationと同等のタイミングはここ(Main
関数)?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
FHI ではAppDelegate.FinishedLaunching
で行っていますね。
https://github.com/folkehelseinstituttet/Fhi.Smittestopp.App/blob/dev/NDB.Covid19/NDB.Covid19.iOS/AppDelegate.cs#L54
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
AppDelegate.FinishedLaunching
だとUI作成が始まってしまう経路という認識なんですよね……(LoadApplication(new App(new iOSInitializer()));
してるのもここ)。
iOSだと気にしなくていいのかな。今のiOSでDependencyService
がちゃんと動いているなら、バックグラウンドタスクでもAppDelegate.FinishedLaunching
通ってるって事なんだと思いますが。
.SetRequiredNetworkType(NetworkType.Connected) | ||
.Build()); | ||
|
||
static TimeSpan bgRepeatInterval = TimeSpan.FromHours(6); | ||
static TimeSpan bgRepeatInterval = TimeSpan.FromMinutes(16); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
デバッグ(テスト用の変更)。
WorkManager(のバックエンドにあるJobSheduler)はWorkerの実行に際して最短15分の間隔が必要なので、ここは余裕を見て16分に設定している。
@@ -126,11 +127,10 @@ public static void ConfigureBackgroundWorkRequest(TimeSpan repeatInterval, Actio | |||
static Action<PeriodicWorkRequest.Builder> bgRequestBuilder = b => | |||
b.SetConstraints(new Constraints.Builder() | |||
.SetRequiresBatteryNotLow(true) | |||
.SetRequiresDeviceIdle(true) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
デバッグ(テスト用の変更)。
#27 と同様に、RequiresDeviceIdle
を設定すると動作タイミングの予測が難しくなるため制約を削除している。
Appクラスについてですが、本PR では |
このPRでは、Issueの解決を目的としてバックグラウンドタスク周りの処理から おそらくですが、今回の変更でIssueが解決できることが確認できたら、もう一歩先の取り組みとして「DependencyService自体の全廃」という課題が出てくる気がします。Prism 8.0へのアップグレードは #22 で提案されていたので、それに関連して課題が生まれるものと思われます。 |
ExposureNotification will be initialized at the entry point of each platform.
Replace IfAlreadyRegistered.Replace with IfAlreadyRegistered.Keep.
d00be84
to
8fb306b
Compare
8fb306b
to
57a1b02
Compare
57a1b02
to
7b5b158
Compare
7b5b158
to
238b55c
Compare
Covid19Radar/Covid19Radar/Services/ExposureNotificationHandler.cs
Outdated
Show resolved
Hide resolved
App.InitializeServiceLocator(RegisterPlatformTypes); | ||
|
||
App.UseMockExposureNotificationImplementationIfNeeded(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
開発担当チームから「iOSの場合、これらの処理は AppDelegate
に置く方が適切」とのレビューをもらったので修正する。
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
疑うのもあれですが、そのレビューは根拠のある意見なんでしょうか?
どちらに置いても実質的に差異はないとは思うので、普通は AppDelegate にみんな書いているしそれに合わせておいた方がいいかな、くらいの差分しかないような気がします。
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
そうですね。適切というのは自然な書き方としてどうかというニュアンスだと受け取りました。
XCodeでプロジェクト作ってみると、Main
の名の付くファイルはstoryboardでSwiftのようなコードじゃないんですよね。
そういう意味ではiOSわかる人にとってはMainにいろいろ書いてあるのは不自然で、AppDelegateに書いても動作は同じなのだからそちらがより適切。と言うことだと理解しています。
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
そのニュアンスであればそれ自体には異存はないので移動しても問題ないと思います。
ちなみに、XCode 12.4 でプロジェクトを作ってみましたが main.m が生成されて
UIApplicationMain を呼び出すコードになりました(Objective-C の場合)ので、
普通あまり使われてはいないとは思うものの、過去の経緯としては
全く不自然というわけでもないはずです。
(Swift の場合はこうなるようですが試してはいないです)
https://ez-net.jp/article/BC/vWrNTeBO/85hmtcwh9W3Y/
…ImplementationIfNeeded to AppDelegate
e8a53b7
to
f138c17
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
開発チームの方針を反映しました( f138c17 )。
ExposureNotification.android.cs
に大きく手を入れているのは、既存のWorkerをキャンセルする必要があるためです。
var workRequest = CreatePeriodicWorkRequest(); | ||
EnqueueUniquePeriodicWork(workRequest); | ||
|
||
return Task.CompletedTask; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
一度Workerを設定した状態でExistingPeriodicWorkPolicy.Keep
でWorkerを設定しても新しい条件(setRequiredDeviceIdle = false
)が反映されないため、旧Workerを一度キャンセルして再設定する処理を入れています。
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
素朴な疑問としては旧Worker と同じ名前だとダメなんでしょうか?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
BackgroundFetchWorker はonStopped
を実装していないので、実行中の Worker は (DB からは削除されるが) 処理が完了するまで止まらないのではないかと思います。
(そもそも今はREPLACE
なので、同じ問題はあるのでしょうが...)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ここはアップデートの時に一度だけ実行されるMY_PACKAGE_REPLACED
と違って、何度も実行される部分なので、同じ名前にしてしまうと毎回キャンセルと設定をすることになってReplaceと変わらなくなるからだと理解しています。
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ExposureNotificationHandler 側で Interlocked してるから並列で接触チェックが動くことはなさそうですね。
私のコメントは忘れてください。
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
自分自身だと考慮するべきことが増えるのかもしれないですが、Worker 外部からの削除と再登録なので、
名前を変えたから害があるようなことではないですが変える理由としては不思議な感じでした。
ありがとうございます
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@keiji 雑なコメントをしてしまいました。
Worker のキャンセルを ExposureNotificationHandler に伝播させるには
onStopped()
でCancellationTokenSource.Cancel()
を呼ぶ- (iOS と同じように)
UpdateKeysFromServer()
に CancellationToken を渡す
必要があると思っているのですが、残念ながらそのようになっていません。
ですので接触チェックが並列実行される可能性があると思っていたのですが、FetchExposureKeyBatchFilesFromServerAsync()
でInterlocked.Exchange()
を使って並列実行しないようにしているので心配ないだろう、ということを言いたかったのでした。
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
WorkerのonStoppedは、ワーカー停止時の後始末をするために使うメソッド
これはたぶん Activity の onStop みたいなイベントハンドラという意図だと思うのですが
それだと違っているはずで、onStopped は外部から Worker が停止される場合に
そのタイミングで呼ばれて、内部的には Future がキャンセルされるとありますね
(isStopped みたほうがわかりやすい)
https://developer.android.com/reference/androidx/work/ListenableWorker?hl=ja#isStopped()
しかし Worker の doWork の途中でキャンセルされたときその瞬間に勝手に
スレッドが死ぬわけではないと思うので、自分で止まる必要があるんですかね。。(よくわからず)
Xamarin の思想としては CancellationTokenSource は Worker が供給するべきということになりますか?
自分で new して自分で止まるためにつかうこともできますか?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
単純にWorker.doWork()
内に処理が書かれているのであればisStopped()
を見てキャンセルされているかどうか判断すれば良いと思いますが、ExposureNotificationHandler 内でisStopped()
を呼ぶことはできません。
ですので Worker のキャンセルを ExposureNotificationHandler 内で判断できるようにする(例えば下記行で処理を止める)
cancellationToken.ThrowIfCancellationRequested(); |
ためには、Worker の状態と連動する CancellationToken を Worker 側から ExposureNotificationHandler へ渡す必要があるのではないかと思います。
ExposureNotification.ios.cs
ではcocoa/Covid19Radar/Xamarin.ExposureNotification/ExposureNotification.ios.cs
Lines 107 to 108 in 0fd648f
var cancelSrc = new CancellationTokenSource(); | |
task.ExpirationHandler = cancelSrc.Cancel; |
await UpdateKeysFromServer(cancelSrc.Token); |
のように BGTask に連動する CancellationToken を
UpdateKeysFromServer()
に渡しています。
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
なるほど。Xamarin.EN 側で対称になっていないんですね。。
@@ -95,24 +96,20 @@ public async Task ExposureDetectedAsync(ExposureDetectionSummary summary, Func<T | |||
var exposureInfo = await getExposureInfo(); | |||
loggerService.Info($"ExposureInfo: {exposureInfo.Count()}"); | |||
|
|||
// Add these on main thread in case the UI is visible so it can update | |||
await Device.InvokeOnMainThreadAsync(() => |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
元々、このブロックの中でUIに関わる処理をしていたため付けていましたが、現在はMainThreadで実行する必要はなくなったので削除しました。
#62 で、アプリのアップデート時に |
ありやまさんのコードをベースにいけるのであれば当初懸念していたほどの問題はないと思います。 1点だけ、 このあたりでオブジェクトを Prism.Ioc とは別に生成する処理は残るのだと思うのですが、 スレッドセーフに関しては InvokeOnMainThreadAsync を消すことで別の問題が起きないかも |
{ | ||
return; | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
UserDataのMigrationもここでやってしまっていいかも?
検証手順です。 Visual Studioからのインストールだとアプリの一部しか転送していない(Hot Loading?)らしく
ぼくの手元では、5の実行時に次のようなログを確認しました(試行錯誤の過程でバージョンを6に変えていますが5のままでも動きます)。
|
Covid19Radar/Xamarin.ExposureNotification/ExposureNotification.android.cs
Outdated
Show resolved
Hide resolved
4c3d3a5
to
a1f6754
Compare
a1f6754
to
f138c17
Compare
Ready for review, but contain a commit for debug use only. DO NOT MERGE.
Purpose
Fix issue #25
Does this introduce a breaking change?
Pull Request Type
What kind of change does this Pull Request introduce?
How to Test
#25 において、画面を介さずに起動したバックグラウンドタスクでは、
App
に依存したDependencyService.Resolve
は使用できないことがわかった。このPullRequestでは、
DependencyService.Resolve
に代わりDryIoc.CommonServiceLocator
で依存性の注入(正確にはInversion of Control)を行う経路を新たに追加した。また、
ExposureNotificationHandler
の経路上でApp
に依存したクラスや処理についても適宜変更を加えている。今後の課題(iOS対応・テスト)