Androidサービスにアクティビティと通信させる方法


251

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

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

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

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

より簡単な、またはより良い方法はありますか?助けてくれてありがとう。

編集:

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

回答:


94

サービスと通信するには、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を使用する必要があります。


2
どうすればbusy-waitアクティビティができますか?説明して頂けますか?
iTurki

1
したがって、サービスクラスのonCreateまたはonStartCommandの後にパブリック静的変数を設定した後、isConnectedまたは何かをtrueに呼び出します。次に、アクティビティでこれを行います:Intent intent = new Intent(act、YourService.class); startService(intent); while(!YourService.isConnected){sleep(300); }その後、ループが実行され、サービスを実行できるようになります。私の経験では、このようなサービスとのやり取りにはいくつかの制限があります。
Matt Wolfe

なぜそれだけで開始できるのActivity.onCreate()ですか?
skywall 2012

それらの間のメッセージをIntents介してデータを送信することによって使用したくない理由についてParcellable何か一言?
アマンアラム

2
これは機能しますが、@ MaximumGoatの答えははるかに明確であり、ビジー待機を含みません。
Matt

262

質問者はおそらくこれを過ぎてからずっと長いですが、誰かがこれを検索した場合に備えて...

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

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を介してデータを渡すことは理にかなっています。


5
どうやって?あなたが説明することはまさに私が必要としていることです。しかし、それ以上に、完全なサンプルコードが必要です。前もって感謝します。ちなみに、前回ウィジェットを書こうとしたとき、メッセージングの部分に2か月かかりました。必要に応じて笑いますが、IMHO AndroidはiOSよりも複雑なので、専門家はもっと公開する必要があります。

1
aaaaaaa、ビジーコーダーの高度なAndroid開発ガイドには、ウィジェットに関する優れた章があります。それが私を混乱に陥らせた唯一のリソースです。著者であるCommonsWareは、StackOverflowで115kの評判を持っています。この本を強くお勧めします。 commonsware.com/AdvAndroid
MaximumGoat

64
Broadcasts幻想的であり、加えて、あなたの放送が、その後使用を検討し、独自のプロセスを超えて行きたいいけない場合LocalBroadcastdeveloper.android.com/reference/android/support/v4/content/...
コーディCaughlan

2
コーディさん、ありがとう。
MaximumGoat

3
これは非常に良いコミュニケーション方法です。しかし、アクティビティが一時停止状態のときに結果が配信された場合はどうすればよいですか?そのため、onResumeアクティビティの後、結果を長時間待つことができます。また、データが失われたため、データは受信されません。
Anton-M

39

LocalBroadcastManagerアプリ内のローカルサービスから送信されたブロードキャストをリッスンするレシーバーを登録するために使用します。参照は次のとおりです。

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


3
はい、localbroadcast here リンク
SohailAziz

1
LocalBroadcastManager今の賛成で廃止されLiveDataたり、他のオブザーバーパターンツール:developer.android.com/reference/androidx/localbroadcastmanager/...
アリNemと

20

オットーイベントバスライブラリに誰も言及していないことに驚きます

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

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


7
あるいは、greenrobotのEventBusライブラリがあります。
Jazzer、2015

15
ただし、EventBusとottoは常に1つのプロセスにあることに注意してください。あなたが設定を開始する独自のプロセスであるサービスを(使用している場合はandroid:process=":my_service"、それらは通信できません。
ロン・

20

メッセンジャーの使用は、サービスとアクティビティの間で通信するもう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の動作については、完全な例を参照してください。


1
とても有難い!完璧に動作します。コード内だけで詳細:myService.send(message);する必要がありますmessenger.send(message);代わりに。
フェデリコクリスティーナ

9

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


4
これは、サービスインスタンスの参照を取得する正しい方法です。
16 2016年

9

また、使用することができますLiveDataようにその作品をEventBus

class MyService : LifecycleService() {
    companion object {
        var BUS = MutableLiveData<Object>()
    }

    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
    }
})

あなたはこのブログからもっと読むことができます


2

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


私は同様の考えに苦労しています。同じモデルのアクティビティと2つのサービスがあります。モデルはアクティビティまたはそれらのサービスのいずれかで更新できるため、どういうわけかオブザーバーを使用することを考えましたが、理解できません。あなたのアイデアを示すために例を投稿できますか?
pzo

2

私の方法:

サービス/アクティビティとのメッセージの送受信を管理するクラス:

import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException;
import android.util.Log;

import java.util.ArrayList;
import java.util.List;

public class MessageManager {

    public interface IOnHandleMessage{
        // Messages
        int MSG_HANDSHAKE = 0x1;

        void onHandleMessage(Message msg);
    }

    private static final String LOGCAT = MessageManager.class.getSimpleName();

    private Messenger mMsgSender;
    private Messenger mMsgReceiver;
    private List<Message> mMessages;

    public MessageManager(IOnHandleMessage callback, IBinder target){
        mMsgReceiver = new Messenger(new MessageHandler(callback, MessageHandler.TYPE_ACTIVITY));
        mMsgSender = new Messenger(target);
        mMessages = new ArrayList<>();
    }

    public MessageManager(IOnHandleMessage callback){
        mMsgReceiver = new Messenger(new MessageHandler(callback, MessageHandler.TYPE_SERVICE));
        mMsgSender = null;
        mMessages = new ArrayList<>();
    }

    /* START Getter & Setter Methods */
    public Messenger getMsgSender() {
        return mMsgSender;
    }

    public void setMsgSender(Messenger sender) {
        this.mMsgSender = sender;
    }

    public Messenger getMsgReceiver() {
        return mMsgReceiver;
    }

    public void setMsgReceiver(Messenger receiver) {
        this.mMsgReceiver = receiver;
    }

    public List<Message> getLastMessages() {
        return mMessages;
    }

    public void addMessage(Message message) {
        this.mMessages.add(message);
    }
    /* END Getter & Setter Methods */

    /* START Public Methods */
    public void sendMessage(int what, int arg1, int arg2, Bundle msgData){
        if(mMsgSender != null && mMsgReceiver != null) {
            try {
                Message msg = Message.obtain(null, what, arg1, arg2);
                msg.replyTo = mMsgReceiver;
                if(msgData != null){
                    msg.setData(msgData);
                }
                mMsgSender.send(msg);
            } catch (RemoteException rE) {
                onException(rE);
            }
        }
    }

    public void sendHandshake(){
        if(mMsgSender != null && mMsgReceiver != null){
            sendMessage(IOnHandleMessage.MSG_HANDSHAKE, 0, 0, null);
        }
    }
    /* END Public Methods */

    /* START Private Methods */
    private void onException(Exception e){
        Log.e(LOGCAT, e.getMessage());
        e.printStackTrace();
    }
    /* END Private Methods */

    /** START Private Classes **/
    private class MessageHandler extends Handler {

        // Types
        final static int TYPE_SERVICE = 0x1;
        final static int TYPE_ACTIVITY = 0x2;

        private IOnHandleMessage mCallback;
        private int mType;

        public MessageHandler(IOnHandleMessage callback, int type){
            mCallback = callback;
            mType = type;
        }

        @Override
        public void handleMessage(Message msg){
            addMessage(msg);
            switch(msg.what){
                case IOnHandleMessage.MSG_HANDSHAKE:
                    switch(mType){
                        case TYPE_SERVICE:
                            setMsgSender(msg.replyTo);
                            sendHandshake();
                            break;
                        case TYPE_ACTIVITY:
                            Log.v(LOGCAT, "HERE");
                            break;
                    }
                    break;
                default:
                    if(mCallback != null){
                        mCallback.onHandleMessage(msg);
                    }
                    break;
            }
        }

    }
    /** END Private Classes **/

}

アクティビティの例:

public class activity extends AppCompatActivity
      implements     ServiceConnection,
                     MessageManager.IOnHandleMessage { 

    [....]

    private MessageManager mMessenger;

    private void initMyMessenger(IBinder iBinder){
        mMessenger = new MessageManager(this, iBinder);
        mMessenger.sendHandshake();
    }

    private void bindToService(){
        Intent intent = new Intent(this, TagScanService.class);
        bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE);
        /* START THE SERVICE IF NEEDED */
    }

    private void unbindToService(){
    /* UNBIND when you want (onDestroy, after operation...)
        if(mBound) {
            unbindService(mServiceConnection);
            mBound = false;
        }
    }

    /* START Override MessageManager.IOnHandleMessage Methods */
    @Override
    public void onHandleMessage(Message msg) {
        switch(msg.what){
            case Constants.MSG_SYNC_PROGRESS:
                Bundle data = msg.getData();
                String text = data.getString(Constants.KEY_MSG_TEXT);
                setMessageProgress(text);
                break;
            case Constants.MSG_START_SYNC:
                onStartSync();
                break;
            case Constants.MSG_END_SYNC:
                onEndSync(msg.arg1 == Constants.ARG1_SUCCESS);
                mBound = false;
                break;
        }
    }
    /* END Override MessageManager.IOnHandleMessage Methods */

    /** START Override ServiceConnection Methods **/
    private class BLEScanServiceConnection implements ServiceConnection {

        @Override
        public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
            initMyMessenger(iBinder);
            mBound = true;
        }

        @Override
        public void onServiceDisconnected(ComponentName componentName) {
            mMessenger = null;
            mBound = false;
        }
    }
    /** END Override ServiceConnection Methods **/

サービスの例:

public class Blablabla extends Service
    implements     MessageManager.IOnHandleMessage {

    [...]

    private MessageManager mMessenger;

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        super.onBind(intent);
        initMessageManager();
        return mMessenger.getMsgReceiver().getBinder();
    }

    private void initMessageManager(){
        mMessenger = new MessageManager(this);
    }

    /* START Override IOnHandleMessage Methods */
    @Override
    public void onHandleMessage(Message msg) {
    /* Do what you want when u get a message looking the "what" attribute */
    }
    /* END Override IOnHandleMessage Methods */

アクティビティ/サービスからメッセージを送信:

mMessenger.sendMessage(what, arg1, arg2, dataBundle);

仕組み:

サービスを開始またはバインドするアクティビティ。サービスの "OnBind"メソッドは、バインダーを彼のMessageManagerに返します。アクティビティでは、 "Service Connection"インターフェイスメソッドの実装 "OnServiceConnected"を介して、このIBinderを取得し、それを使用してMessageManagerを開始します。アクティビティがMessageManagerを初期化した後、MessageHandlerがサービスに送信およびハンドシェイクするため、「MessageHandler」送信者(MessageManagerの「プライベートメッセンジャーmMsgSender;」)を設定できます。これを行うと、サービスの送信者が誰にメッセージを送信したかがわかります。

また、MessageManagerのメッセンジャー「送信者」のリスト/キューを使用してこれを実装し、複数のメッセージを異なるアクティビティ/サービスに送信したり、MessageManagerのメッセンジャー「受信者」のリスト/キューを使用して複数を受信したりできます。さまざまなアクティビティ/サービスからのメッセージ。

「MessageManager」インスタンスには、受信したすべてのメッセージのリストがあります。

この「MessageManager」インスタンスを使用した「アクティビティのメッセンジャー」と「サービスメッセンジャー」の間の接続は自動であることがわかるので、「OnServiceConnected」メソッドと「ハンドシェイク」を使用して行われます。

これがあなたのお役に立てば幸いです:)どうもありがとうございました!バイバイ


2

@MrSnowflakeの回答をコード例でフォローアップします。 これは、XABBERの現在のオープンソースApplicationクラスです。このApplicationクラスはListeners、ManagerInterfacesなどを集中化および調整しています。あらゆる種類のマネージャーが動的に読み込まれます。Activity´sXabberで開始すると、Listenerそれらのタイプが報告されます。そして、Service開始すると、開始時にApplicationクラスに報告します。今Activityあなたにしなければならないすべてにメッセージを送るためにはあなたActivitylistenerあなたが必要とするタイプのになるようにすることです。ではOnStart() OnPause()レジスタ/ UNREG。Service頼むことができるApplicationだけで、そのためにクラスをlistener、それがに話す必要があり、それがありますならば、活動が受信する準備ができています。

Applicationクラスを通過すると、これよりもさらに多くの戦利品があることがわかります。


提供されたリンクにより、github 404が生成されます。移動しましたか?
JE Carter II

うん、今は動いているようです。githubの一時的な問題であったに違いありません。
JE Carter II

1

バインディングは通信のもう1つの方法です

コールバックを作成する

public interface MyCallBack{

   public void getResult(String result);

}

活動側:

  1. アクティビティにインターフェースを実装する

  2. メソッドの実装を提供する

  3. アクティビティをサービスにバインドする

  4. サービスがアクティビティにバインドおよびバインド解除されたときにコールバックを登録および登録解除します。

    public class YourActivity extends AppCompatActivity implements MyCallBack{
    
          private Intent notifyMeIntent;
          private GPSService gpsService;
          private boolean bound = false;
    
          @Override
          public void onCreate(Bundle sis){
    
              // activity code ...
    
              startGPSService();
    
          }
    
          @Override
          public void getResult(String result){
           // show in textView textView.setText(result);
          }
    
          @Override
          protected void onStart()
          {
              super.onStart();
              bindService();
          }
    
          @Override
          protected void onStop() {
              super.onStop();
              unbindService();
          }
    
          private ServiceConnection serviceConnection = new ServiceConnection() {
    
                @Override
                public void onServiceConnected(ComponentName className, IBinder service) {
    
                      GPSService.GPSBinder binder = (GPSService.GPSBinder) service;
                      gpsService= binder.getService();
                      bound = true;
                      gpsService.registerCallBack(YourActivity.this); // register
    
               }
    
               @Override
               public void onServiceDisconnected(ComponentName arg0) {
                      bound = false;
               }
          };
    
          private void bindService() {
    
               bindService(notifyMeIntent, serviceConnection, Context.BIND_AUTO_CREATE);
          }
    
          private void unbindService(){
               if (bound) {
                     gpsService.registerCallBack(null); // unregister            
                     unbindService(serviceConnection);
                     bound = false;
                }
          }
    
          // Call this method somewhere to start Your GPSService
          private void startGPSService(){
               notifyMeIntent = new Intent(this, GPSService.class);
               startService(myIntent );
          }
    
     }

サービス側:

  1. コールバックを初期化する

  2. 必要なときにいつでもコールバックメソッドを呼び出す

     public class GPSService extends Service{
    
         private MyCallBack myCallback;
         private IBinder serviceBinder = new GPSBinder();
    
         public void registerCallBack(MyCallBack myCallback){
              this.myCallback= myCallback;
         }
    
         public class GPSBinder extends Binder{
    
             public GPSService getService(){
                  return GPSService.this;
             }
        }
    
        @Nullable
        @Override
        public IBinder onBind(Intent intent){
             return serviceBinder;
        }
     }

あなたのserviceConnection外ですonCreate。編集してください。
Adeel Zafar

onCreate内にある必要はありません
Rohit Singh


0

LocalBroadcastManager、Event Bus、Messengerのほか 、この質問で既に回答されているほか、Pending Intentを使用してサービスから通信できます。

私のブログ投稿でここで述べたように

サービスとアクティビティ間の通信はPendingIntentを使用して行うことができます。そのために createPendingResult().createPendingResult()を使用して、サービスに渡してonActivityResult(int、 int、Intent)コールバック。PendingIntentはParcelableであるため、Intentエクストラに入れることができるため、アクティビティはこのPendingIntentをサービスに渡すことができます。サービスは、PendingIntentでsend()メソッドを呼び出して、イベントのonActivityResultによるアクティビティ。

アクティビティ

public class PendingIntentActivity extends AppCompatActivity
{
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

PendingIntent pendingResult = createPendingResult(
100, new Intent(), 0);
Intent intent = new Intent(getApplicationContext(), PendingIntentService.class);
intent.putExtra("pendingIntent", pendingResult);
startService(intent);

}

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == 100 && resultCode==200) {
Toast.makeText(this,data.getStringExtra("name"),Toast.LENGTH_LONG).show();
}
super.onActivityResult(requestCode, resultCode, data);
}
}

サービス

public class PendingIntentService extends Service {

    private static final String[] items= { "lorem", "ipsum", "dolor",
            "sit", "amet", "consectetuer", "adipiscing", "elit", "morbi",
            "vel", "ligula", "vitae", "arcu", "aliquet", "mollis", "etiam",
            "vel", "erat", "placerat", "ante", "porttitor", "sodales",
            "pellentesque", "augue", "purus" };
    private PendingIntent data;

    @Override
    public void onCreate() {
        super.onCreate();
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {

        data = intent.getParcelableExtra("pendingIntent");

        new LoadWordsThread().start();
        return START_NOT_STICKY;
    }

    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
    }

    class LoadWordsThread extends Thread {
        @Override
        public void run() {
            for (String item : items) {
                if (!isInterrupted()) {

                    Intent result = new Intent();
                    result.putExtra("name", item);
                    try {
                        data.send(PendingIntentService.this,200,result);
                    } catch (PendingIntent.CanceledException e) {

                        e.printStackTrace();
                    }
                    SystemClock.sleep(400);

                }
            }
        }
    }
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.