2012-09-12 16 views
6

Androidはアプリケーションコンポーネント(アクティビティ、サービス)をいつでも殺す危険にさらされていることはよく知られています。これは、堅牢で漏れのないソリューションを提供したいと同時に、コードをきれいに保ちながら懸念の分離に取り組むためには、事態を本当に複雑にします。良いデザイン:ワーカースレッドとアクティビティの再起動

問題
活性が(RunnableAsyncTaskまたは他のいずれかの機構の形で)時間のかかるタスクを開始します。進行状況ダイアログが表示されます。タスクはいくつかの接続を開きますが、プログレスバーを更新する手段が必要です。完了までに半分の方法で選択ダイアログを表示する必要があります。また、エラーが発生したとき、または完了したときに、ダイアログを閉じてToastを表示できるようにする必要があります。

活動が殺され、後に、新しいインスタンスが再作成され、私は2つのオプションを考えることができます。

  1. が同様に実行中のタスクを殺す、とするときに、新しい代替別のタスクインスタンスを起動してみてくださいアクティビティインスタンスが作成されます。ユーザーの観点からは、タスクを開始し、80%から0%に進むプログレスバー・インジケーターが明白な理由なく表示されるのを容認できないため、これは長時間実行されるタスクのオプションではありません。しかしこれはShelves example by Romain Guyに続く方法です。どうやらこれは公式の開発者ガイドで推奨されているアプローチです。

  2. タスクを実行し続ける:onResumeに更新タスクをサブスクライブして(場合によっては開始して)、でサブスクリプションをサブスクライブしない(一時停止する)ことができます。

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

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

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

私は以前に質問されていることはよく知っていますが、問題の解決方法を具体的に求めているわけではありませんが、疎結合を達成し、可能。

短いで


- これは、Androidの開発で繰り返し発生する問題であると誰かが、おそらく前に巧妙なデザインを打ち出しています。これを単純化する設計パターン、または十分にテストされたライブラリはありますか?
- #2を実装する場合は、タスク(Runnable、Serviceなど)のためにどのクラスを選択し、アクティビティとどのように通信しますか?

P.S. "orientation | keyboardHidden"ハックXDに基づいてソリューションを投稿することをお控えください。これ以外にも、どんな助けもありがたいです。


UPDATE:
私の最初の試みは少し厄介持っています。このアプリは、BTのステータスを検出する(そして、それが無効になっている場合にそれを有効にするようユーザーに求めます)、ペアになっているデバイスを取得する(そして、複数のデバイスがある場合にユーザーを選択して、最後にユーザーに結果を知らせます。このタスクは80%のIOコード、20%のGUI関連の操作であったため、タスクの始めと終わりにGUIコードをグループ化し、それをタスクからアクティビティに戻す必要がありました。私はIntentServiceを持っていますが、それは重い持ち上げをしてから、BroadcastReceiverを介して活動に報告します。これはほとんどの問題を解決しますが、そのような設計にはいくつかの問題があります。まず、意味的結合を導入するインプットとアウトプットのインテントからフィールドを出し入れするためのキーとして使用される定数ストリングがすべてあります。私は、アクティビティ< - >サービス通信でタイプセーフティを優先させていたでしょう。また、複雑なカスタムオブジェクトをインテントのサービスに渡す方法を解決する必要があります。今はParcelablesとプリミティブパラメータに悩まされています。最後に、アクティビティとサービスの両方が同じAPI(ブルートゥース)を使用しているため、私はBT関連のコードを単一のクラスに持つことを好みました。私はこのデザインを捨てて、スレッドに基づいたカスタムプリントキューを使ってやり直してみましょう。それは、殺されたアクティビティをより扱いにくくします。

+0

アクティビティが再作成されている間、AsyncTaskは一時停止しています。タスクが進行状況/結果に使用するメッセージは、アクティビティが破棄されている間は配信されません(一時停止/再開前にも発生する可能性があります)。非構成インスタンスのものを使用して、アクティビティー・インスタンス間でAsyncTaskを渡すことは、かなりうまくいくはずです。 – zapl

答えて

8

長時間実行しているタスクをホストするサービスを開始するのはなぜですか?物事を長期間維持するための最良の方法です。 OSにヒントを提供して、OSを動作させたままにすることができます。 startForeground()およびSTART_STICKYを参照のこと。

サービスからのブロードキャストによりアクティビティに返信することができます。あなたのアクティビティに、broadcast receiverをプログラムで登録して、それらのインテントをリッスンします。アクティビティが一時停止している場合は、レシーバーの登録を解除して、フォアグラウンドにいないときに応答しないようにします。

OSによってサービスが破壊された場合、プロセスが強制終了するため、とにかにあなたの仕事は破壊されます。できるだけ早く再起動してください。とにかく、これはあなたが上記を考慮すれば最も極端な条件以外では起こりません。

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

アンドロイドモデルは、アプリケーションが聴くことができる強力なインテント(通知)のセットを提供することです。これらの意図は、あなたのデバイス/アプリケーションを起動させます。あなたのアプリは通知を処理し、デバイスが直ちにスリープ状態に戻るのを許可する必要があります。

イベントにマップする在庫がない場合は、Google Cloud Messagingを使用してサーバーからデバイスを起動できます。つまり、長時間実行されているプロセスをサーバー上で実行させ、必要に応じてデバイスを起動させます。

これを行うことができれば、代わりにAlarmManagerを使用して定期的に起床して状態を確認することもできます。これを頻繁に行っても(たとえば、5分ごとに、いいえ)、デバイスが常に目を覚ますようにするよりもずっと効果的です。また、不正確な起床間隔を利用してください(上記の文書を参照)。

+0

はい、サービスとして実行し、いくつかのアプリとして、試しにアイコンを追加するサービスが終了するのを防ぐために役立つでしょう、あなたはアンドロイドアプリが停止されている間にイベントを得ることができます。 –

+0

他のプラットフォームでネストされたRunnableクラスとしてコード化されたシンプルなワーカースレッド、アンドロイドではFGサービス、レシーバ、およびおそらくウェイクロックを含む方法は素晴らしいです。 –

+0

長時間実行されるバックグラウンドプロセスは、あらゆるモバイルプラットフォームで特別なケースです。モバイルデバイスには実現可能な電源がないため、ほとんどの場合、そうしたくはありません。 –

関連する問題