7

Androidは、アプリケーションコンポーネント(アクティビティ、サービス)をいつでも殺される恐れがあることはよく知られています。これにより、堅牢で漏れのないソリューションを提供すると同時に、コードをクリーンに保ち、関心の分離に対処したい場合、事態は非常に複雑になります。

問題
アクティビティが時間のかかるタスクを開始します(RunnableAsyncTaskまたはその他のメカニズムの形式で)。進行状況ダイアログが表示されます。タスクはいくつかの接続を開きますが、進行状況バーを更新する手段が必要であり、完了までの途中で選択ダイアログを表示する必要がある場合があります。また、エラーが発生したとき、またはエラーが完了したときに、ダイアログを閉じてトーストを表示できる必要があります。

アクティビティが強制終了され、後で新しいインスタンスが再作成される場合、次の2つのオプションが考えられます。

  1. 実行中のタスクも強制終了し、新しい代替アクティビティインスタンスが作成されたときに別のタスクインスタンスを生成してみてください。タスクを開始し、明確な理由なしに進行状況バーのインジケーターが80%から0%に戻るのをユーザーの観点から見ることは受け入れられないため、これは長時間実行されるタスクのオプションではありません。ただし、これはRomainGuyによるShelvesの例で採用されているアプローチです。どうやらこれは公式の開発者ガイドで推奨されているアプローチです。

  2. タスクを実行し続ける:したがって、で更新のためにタスクをサブスクライブ(および場合によっては開始)し、onResumeでサブスクライブを解除(および場合によっては一時停止)するアクティビティを作成できますonPause

2番目のオプションに従うということは、アクティビティが一時的に破棄された場合にタスクが収集されないようにする必要があることを意味します。たとえば、カスタムApplicationクラス内で宣言したり、アプリケーションの残りの部分にサービスとして提供したりすることもできます(シングルトン、Androidサービスを使用)。ただし、タスクはアクティビティによってのみ使用されるため、他のクラスで使用できるようにすることは意味がありません。一方、Androidサービスもライフサイクルに対応する必要があります。

オプション#2には、古いアクティビティがリークされないようにすることも含まれます。アクティビティが存在しない場合は、GUIを更新しないでください。全体として、スレッドセーフである必要があります。最後に、タスクが不要になったことを確認した後は、タスクをメモリに残してはなりません。ただし、同時に、最初のアクティビティが破棄され、タスクが実行を継続し、新しい代替アクティビティが開始された場合、GUIをすぐに更新し、タスクの現在のステータス(または完了した場合は結果)を表示するために、タスクを回避する必要があります。 )。

アクティビティが表示されていないときにタスクが実行されている可能性があるという事実は、ある時点で同期的にユーザーの操作(選択ダイアログ)が必要になる可能性があるため、対処すべきもう1つの問題です。これは、アクティビティに到達するとすぐにタスクを一時停止する機能がある場合に解決できますonPauseが、タスクが一時停止するのに時間がかかるか、一時停止できない場合もあります。

以前にも同様の質問があったことは知っていますが、問題を解決する方法について具体的に質問するのではなく、緩い結合を実現し、アクティビティをタスクから可能な限り分離するために推奨される設計について質問します。

要するに
-これはAndroid開発で繰り返し発生する問題であり、誰かが以前に巧妙な設計を思いついた可能性があります。これを単純化するデザインパターン、または十分にテストされたライブラリはありますか?
-#2を実装する場合、タスク(実行可能、サービスなど)に対してどのクラスを選択し、アクティビティとどのように通信しますか?

PS「orientation|keyboardHidden」ハックXDに基づくソリューションの投稿はご遠慮ください。これ以外の助けをいただければ幸いです。


更新:
私の最初の試みは少し厄介です。このアプリはBluetooth印刷ユーティリティであり、BTステータスの検出(および無効になっている場合は有効にするようにユーザーに求める)、ペアリングされたデバイスの取得(複数ある場合はユーザーに1つを選択するように求める)、データの送信が含まれます最後に、結果についてユーザーに通知します。このタスクは80%のIOコード、20%のGUI関連の操作であったため、タスクの最初と最後でGUIコードをグループ化し、タスクからアクティビティに戻す必要がありました。私はIntentService重い物を持ち上げてから、経由して活動に報告するものを持っていますBroadcastReceiver。これはほとんどの問題を解決しますが、そのような設計にはいくつかの問題があります。まず、入力インテントと出力インテントからフィールドを配置および取得するためのキーとして使用される定数文字列がすべてあり、セマンティックカップリングが導入されています。アクティビティ<->サービス通信では型安全性を優先したいと思います。そして、複雑なカスタムオブジェクトをインテントのサービスに渡す方法をまだ解決する必要があります。今では、Parcelablesとプリミティブパラメータで立ち往生しています。最後に、アクティビティとサービスの両方が同じAPI(bluetooth)を使用しているため、BT関連のコードをすべて1つのクラスに含めることをお勧めします。私はこのデザインを捨てて、スレッドに基づいたカスタム印刷キューで再試行すると思いますが、それは殺されたアクティビティの処理を難しくします。

4

1 に答える 1

8

長時間実行されるタスクをホストするサービスをアクティビティで開始してみませんか?サービスは、物事を長期間実行し続けるための最善の策です。OSにヒントを提供して、OSを実行したままにすることができます。を参照startForeground()してくださいSTART_STICKY

サービスからブロードキャストすることで、アクティビティに戻ることができます。それらの意図を聞くためにあなたの活動にプログラムで放送受信機を登録させてください。アクティビティが一時停止している場合は、レシーバーの登録を解除してください。そうすれば、フォアグラウンドにいないときに応答しなくなります。

OSがサービスを破壊した場合、それはプロセスを強制終了するので、とにかくあなたのタスクは運命づけられます。あなたができる最善のことはそれを再起動することです。とにかく、あなたが上記を考慮すれば、これは最も極端な条件を除いて起こりません。

編集:コメントに記録されたいくつかの考えを要約します...長時間実行されているワーカープロセスを維持し、自動的に再起動することは、ほとんどの場合、間違ったことです。モバイルデバイスには、これを実現するための電源がありません。著者は、この懸念を否定する特別な条件があると述べました...これは、デバイスがほとんど常に電源に接続されている場合にのみ当てはまります。

androidモデルは、アプリケーションがリッスンできる堅牢なインテント(通知)のセットを提供することです。これらのインテントは、何かをするときにデバイス/アプリケーションをウェイクアップします。アプリが通知を処理してから、デバイスがすぐにスリープ状態に戻ることを許可する必要があります。

イベントにマッピングするストックインテントがない場合は、Googleクラウドメッセージングを使用してサーバーからデバイスをウェイクアップできます。つまり、長時間実行されるプロセスをサーバー上で実行し、必要に応じてデバイスをウェイクアップします。

それができる場合は、別の方法として、AlarmManager定期的にウェイクアップして状態を確認することもできます。これを頻繁に行ったとしても(たとえば、5分ごと、これもノーノーです)、提案されているようにデバイスを常にスリープ状態にしないよりもはるかに優れています。また、不正確なウェイクアップ間隔を利用してください(上記のリンク先のドキュメントを参照)。

于 2012-09-12T22:16:16.347 に答える