262

私は最初の Android アプリケーションを作成しており、サービスとアクティビティ間の通信について理解しようとしています。バックグラウンドで実行され、GPS と時間ベースのログ記録を行うサービスがあります。サービスの開始と停止に使用されるアクティビティがあります。

そのため、最初に、アクティビティの開始時にサービスが実行されているかどうかを確認できる必要があります。それについては他にもいくつか質問があるので、それを理解できると思います(ただし、お気軽にアドバイスを提供してください)。

私の本当の問題: アクティビティが実行中で、サービスが開始されている場合、サービスがアクティビティにメッセージを送信する方法が必要です。この時点での単純な文字列と整数 - ほとんどのステータス メッセージ。メッセージは定期的に発生するわけではないため、別の方法がある場合、サービスをポーリングすることは良い方法ではないと思います。アクティビティがユーザーによって開始されたときにのみ、この通信が必要です。サービスからアクティビティを開始したくありません。つまり、アクティビティを開始し、サービスが実行されている場合、興味深いことが発生すると、アクティビティ UI にいくつかのステータス メッセージが表示されます。アクティビティを開始しない場合、これらのメッセージは表示されません (あまり興味深いものではありません)。

サービスが実行されているかどうかを判断できるはずです。実行されている場合は、アクティビティをリスナーとして追加します。次に、アクティビティが一時停止または停止したときに、アクティビティをリスナーとして削除します。それは実際に可能ですか?それを行う唯一の方法は、Activity に Parcelable を実装させ、サービスのリモート インターフェースを介して渡すことができるように AIDL ファイルを作成することです。それはやり過ぎのようですが、Activity が writeToParcel() / readFromParcel() を実装する方法がわかりません。

もっと簡単で良い方法はありますか?助けてくれてありがとう。

編集:

後でこれに興味がある人のために、サンプル ディレクトリに AIDL を介してこれを処理するための Google のサンプル コードがあります: /apis/app/RemoteService.java

4

13 に答える 13

276

質問者はおそらくこれをはるかに超えて移動していますが、他の誰かがこれを検索した場合...

これを処理する別の方法がありますが、これが最も簡単だと思います。

をアクティビティに追加BroadcastReceiverします。でカスタム インテントを受け取るようにonResume登録し、 で登録解除しonPauseます。次に、ステータスの更新または何を持っているかを送信したいときに、サービスからそのインテントを送信します。

他のアプリがあなたのことを聞いていても不幸にならないことを確認してくださいIntent(誰か悪意のあることをすることができますか?)。

コードサンプルがリクエストされました:

私のサービスでは、これがあります:

// Do stuff that alters the content of my local SQLite Database
sendBroadcast(new Intent(RefreshTask.REFRESH_DATA_INTENT));

(RefreshTask.REFRESH_DATA_INTENTは単なる定数文字列です。)

私のリスニング活動では、私は自分のを定義しますBroadcastReceiver:

private class DataUpdateReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        if (intent.getAction().equals(RefreshTask.REFRESH_DATA_INTENT)) {
          // Do stuff - maybe update my view based on the changed DB contents
        }
    }
}

クラスの先頭でレシーバーを宣言します。

private DataUpdateReceiver dataUpdateReceiver;

onResumeこれを追加するためにオーバーライドします:

if (dataUpdateReceiver == null) dataUpdateReceiver = new DataUpdateReceiver();
IntentFilter intentFilter = new IntentFilter(RefreshTask.REFRESH_DATA_INTENT);
registerReceiver(dataUpdateReceiver, intentFilter);

そして、オーバーライドonPauseして追加します:

if (dataUpdateReceiver != null) unregisterReceiver(dataUpdateReceiver);

現在、私のアクティビティは、サービスが「ねえ、自分で更新してください」と言うのをリッスンしています。データベース テーブルを更新してから戻ってアクティビティ内の変更を見つける代わりに、データを渡すこともできIntentますが、とにかく変更を保持したいので、DB 経由でデータを渡すのが理にかなっています。

于 2011-01-19T19:45:00.657 に答える
97

サービスと通信するための3つの明白な方法があります:

  1. インテントの使用
  2. AIDLの使用
  3. サービスオブジェクト自体の使用(シングルトンとして)

あなたの場合、私はオプション3を選択します。それ自体がサービスへの静的参照を作成し、onCreate()に入力します。

void onCreate(Intent i) {
  sInstance = this;
}

静的関数MyService getInstance()を作成します。これは静的を返しますsInstance

次にActivity.onCreate()、サービスを開始し、サービスが実際に開始されるまで非同期で待機し(アクティビティにインテントを送信することで、サービスに準備ができたことをアプリに通知させることができます)、そのインスタンスを取得します。インスタンスができたら、サービスリスナーオブジェクトをサービスに登録すると、設定が完了します。注:アクティビティ内のビューを編集する場合は、UIスレッドでビューを変更する必要があります。サービスはおそらく独自のスレッドを実行するため、を呼び出す必要がありますActivity.runOnUiThread()

最後に行う必要があるのは、のリスナーオブジェクトへの参照を削除することですActivity.onPause()。そうしないと、アクティビティコンテキストのインスタンスがリークし、適切ではありません。

注:この方法は、アプリケーション/アクティビティ/タスクがサービスにアクセスする唯一のプロセスである場合にのみ役立ちます。そうでない場合は、オプション1または2を使用する必要があります。

于 2010-03-17T15:54:17.920 に答える
40

LocalBroadcastManagerアプリ内のローカルサービスから送信されたブロードキャストをリッスンする受信者を登録するために使用します。参照はここにあります:

http://developer.android.com/reference/android/support/v4/content/LocalBroadcastManager.html

于 2012-01-13T02:15:17.570 に答える
21

メッセンジャーの使用は、サービスとアクティビティの間で通信するもう1つの簡単な方法です。

アクティビティで、対応するメッセンジャーを使用してハンドラーを作成します。これにより、サービスからのメッセージが処理されます。

class ResponseHandler extends Handler {
    @Override public void handleMessage(Message message) {
            Toast.makeText(this, "message from service",
                    Toast.LENGTH_SHORT).show();
    }
}
Messenger messenger = new Messenger(new ResponseHandler());

メッセンジャーは、メッセージに添付することでサービスに渡すことができます。

Message message = Message.obtain(null, MyService.ADD_RESPONSE_HANDLER);
message.replyTo = messenger;
try {
    myService.send(message);
catch (RemoteException e) {
    e.printStackTrace();
}

完全な例は、APIデモ(MessengerServiceおよびMessengerServiceActivity )にあります。MyServiceの動作については、完全な例を参照してください。

于 2012-02-25T21:16:21.397 に答える
21

Otto イベント バス ライブラリを誰も参照していないことに驚いています。

http://square.github.io/otto/

私はこれを Android アプリで使用しており、シームレスに動作します。

于 2014-03-11T11:22:14.540 に答える
18

LiveDataのように機能するものを使用することもできますEventBus

class MyService : LifecycleService() {
    companion object {
        val BUS = MutableLiveData<Any>()
    }

    override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int {
        super.onStartCommand(intent, flags, startId)

        val testItem : Object

        // expose your data
        if (BUS.hasActiveObservers()) {
            BUS.postValue(testItem)
        }

        return START_NOT_STICKY
    }
}

次に、からオブザーバーを追加しますActivity

MyService.BUS.observe(this, Observer {
    it?.let {
        // Do what you need to do here
    }
})

このブログから詳細を読むことができます。

于 2018-11-19T07:01:57.460 に答える
9

他のコメントで言及されていない他の方法は、bindService() を使用してアクティビティからサービスにバインドし、ServiceConnection コールバックでサービスのインスタンスを取得することです。ここで説明されているようにhttp://developer.android.com/guide/components/bound-services.html

于 2014-12-04T20:35:20.700 に答える
2

もう 1 つの方法は、アクティビティとサービス自体を介して偽のモデル クラスでオブザーバーを使用し、MVC パターン バリエーションを実装することです。これが最善の方法かどうかはわかりませんが、私にとってはうまくいった方法です。例が必要な場合は、それを求めてください。何かを投稿します。

于 2012-03-26T13:58:03.773 に答える
2

@MrSnowflakeの回答をコード例でフォローアップします。 これが XABBER の現在のオープン ソースApplicationクラスです。このApplicationクラスはListeners、ManagerInterfaces などを集中管理および調整します。あらゆる種類のマネージャーが動的にロードされます。Activity´sXabberで開始されたものは、Listenerそれらがどのタイプであるかを報告します。そして、開始すると、開始されたとしてクラスServiceに報告します。Applicationにメッセージを送信するにActivityは、必要なタイプにActivityなるようにするだけです。登録/登録解除listenerOnStart() OnPause()は、話す必要があることだけをクラスにService要求できます。それがあれば、Activity は受け取る準備ができています。Applicationlistener

クラスを通過するApplicationと、これよりも多くの戦利品が行われていることがわかります.

于 2013-08-17T18:41:22.173 に答える