アクティビティとサービス間のコミュニケーション

どういうわけか、サービスからアクティビティにデータを転送するタスクがありました。 標準SDKでソリューションの検索が開始されましたが、時間がなかったため、データベースを使用するという形で悪いソリューションが作成されました。 しかし、質問は未解決であり、しばらくして、SDKのより正確な方法、つまりMessage、Handler、およびMessengerクラスを使用する方法を見つけました。



アイデア



アクティビティからサービスへ、またはその逆にデータを転送する必要があります。 これをどうやってやるの? 問題を解決するために、必要なものはすべて揃っています。 必要なのは、bindServiceを使用してサービスをアフィニティにバインドし、必要なパラメーターと、Messageクラスを使用する形式で少しの魔法を渡すことだけです。 そして、魔法は、Messageインスタンス変数、特にreplyToを使用することです。 この変数は、アクティビティからMessangerサービスのインスタンスにアクセスできるようにするために必要です。サービスでは、Messangerアクティビティのインスタンスにアクセスできます。 実際、それほど単純ではありません。 少なくとも、あまり才能のない心のために。 一部では、すでに存在するドキュメント-Servicesを改善しています 。 アクティビティへの接続を追加し、データを前後に転送することで改善しますが、これはドキュメントにはありません。 また、StackOverflowの良い例があります。 いずれにせよ、この記事が少なくとも誰かに役立つことを願っています。





例として、TextViewで、カウンター値を増減し、結果をアクティビティに返すサービスを実装します。 2つのボタンと1つのテキストフィールドがあるため、レイアウトコードは省略します。すべてがシンプルです。



実装



完全なアクティベーションコードを提供します。



public class MainActivity extends Activity { public static final String TAG = "TestService"; TestServiceConnection testServConn; TextView testTxt; final Messenger messenger = new Messenger(new IncomingHandler()); Messenger toServiceMessenger; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); testTxt = (TextView)findViewById(R.id.test_txt); bindService(new Intent(this, TestService.class), (testServConn = new TestServiceConnection()), Context.BIND_AUTO_CREATE); } @Override public void onDestroy(){ super.onDestroy(); unbindService(testServConn); } public void countIncrClick(View button){ Message msg = Message.obtain(null, TestService.COUNT_PLUS); msg.replyTo = messenger; try { toServiceMessenger.send(msg); } catch (RemoteException e) { e.printStackTrace(); } } public void countDecrClick(View button){ Message msg = Message.obtain(null, TestService.COUNT_MINUS); msg.replyTo = messenger; try { toServiceMessenger.send(msg); } catch (RemoteException e) { e.printStackTrace(); } } private class IncomingHandler extends Handler { @Override public void handleMessage(Message msg){ switch (msg.what) { case TestService.GET_COUNT: Log.d(TAG, "(activity)...get count"); testTxt.setText(""+msg.arg1); break; } } } private class TestServiceConnection implements ServiceConnection { @Override public void onServiceConnected(ComponentName name, IBinder service) { toServiceMessenger = new Messenger(service); //    Message msg = Message.obtain(null, TestService.SET_COUNT); msg.replyTo = messenger; msg.arg1 = 0; //  try { toServiceMessenger.send(msg); } catch (RemoteException e) { e.printStackTrace(); } } @Override public void onServiceDisconnected(ComponentName name) { } } }
      
      







説明します。 アクティビティを作成すると、すぐにサービスにアタッチされ、ServiceConnectionインターフェイスを実装し、「カウンター値の設定」サービスにメッセージを送信し、ゼロを渡してtoServiceMessangerを作成し、IBinderインターフェイスをコンストラクターに渡します。 ところで、サービスでこのインスタンスを返す必要があります。そうしないと、NPEが存在します。 このクラスを使用して、メッセージをサービスに送信します。 そして、これは魔法です-replyTo変数に、Messengerの他のインスタンスを保存します。これは、サーバーからの応答を受信し、それを通じてアクティビティとの接続が実行されるインスタンスです。



サービスからメッセージを受信するには、ハンドラーを使用して、必要な変数を探し、それらに対してアクションを実行します。 ボタン(メソッドcountIncrClick、countDecrClick)をクリックすることにより、リクエストをサービスに送信し、変数msg.whatで目的のアクションを示します。



次に、完全なサービスコード:



 package com.example.servicetest; import android.app.Service; import android.content.*; import android.os.*; import android.os.Process; import android.util.Log; public class TestService extends Service { public static final int COUNT_PLUS = 1; public static final int COUNT_MINUS = 2; public static final int SET_COUNT = 0; public static final int GET_COUNT = 3; int count = 0; IncomingHandler inHandler; Messenger messanger; Messenger toActivityMessenger; @Override public void onCreate(){ super.onCreate(); HandlerThread thread = new HandlerThread("ServiceStartArguments", Process.THREAD_PRIORITY_BACKGROUND); thread.start(); inHandler = new IncomingHandler(thread.getLooper()); messanger = new Messenger(inHandler); } @Override public IBinder onBind(Intent arg0) { return messanger.getBinder(); } @Override public int onStartCommand(Intent intent, int flags, int startId) { return START_STICKY; } //   private class IncomingHandler extends Handler { public IncomingHandler(Looper looper){ super(looper); } @Override public void handleMessage(Message msg){ //super.handleMessage(msg); toActivityMessenger = msg.replyTo; switch (msg.what) { case SET_COUNT: count = msg.arg1; Log.d(MainActivity.TAG, "(service)...set count"); break; case COUNT_PLUS: count++; Log.d(MainActivity.TAG, "(service)...count plus"); break; case COUNT_MINUS: Log.d(MainActivity.TAG, "(service)...count minus"); count--; break; } //     Message outMsg = Message.obtain(inHandler, GET_COUNT); outMsg.arg1 = count; outMsg.replyTo = messanger; try { if( toActivityMessenger != null ) toActivityMessenger.send(outMsg); } catch (RemoteException e) { e.printStackTrace(); } } } }
      
      







アクティビティのロジックとの類推によってすべて。 私は何かを説明する必要があるかどうかさえ知りません。 唯一のことは、replyToマジック変数を使用し、上記の目的のMessengerをプルして、すぐにhandleMessageのアクティビティにリクエストを送り返すことです。 そして、私がすでに言及した2番目のポイントは次のとおりです。



 @Override public IBinder onBind(Intent arg0) { return messanger.getBinder(); }
      
      







それなしではすべてが落ちます。 ServiceConnectionに渡されるのは、インターフェイスのこのインスタンスです



おわりに



すべてのすべて。 アクティビティとサービスの相互作用のこのような大げさな例。 それは誰かにとってはそうでないように思えるかもしれませんが、私にはかなり重要な相互作用のようです。



プロジェクトコードはBitbucketにあります



PMでの質問、説明など。 一部の側面については不正確な場合があるため、自由に記述して修正してください。

この投稿がhabrayuzeryに役立つことを願っています。



All Articles