webentwicklung-frage-antwort-db.com.de

Wie kann der Android-Dienst mit der Aktivität kommunizieren?

Ich schreibe meine erste Android-Anwendung und versuche, mich mit der Kommunikation zwischen Diensten und Aktivitäten auseinanderzusetzen. Ich habe einen Dienst, der im Hintergrund ausgeführt wird und einige gps- und zeitbasierte Protokollierungen durchführt. Ich habe eine Aktivität, die zum Starten und Stoppen des Dienstes verwendet wird. 

Zuerst muss ich herausfinden können, ob der Dienst ausgeführt wird, wenn die Aktivität gestartet wird. Es gibt noch einige andere Fragen dazu, also denke ich, dass ich das herausfinden kann (aber ich kann mich gerne beraten lassen).

Mein eigentliches Problem: Wenn die Aktivität läuft und der Dienst gestartet wird, brauche ich eine Möglichkeit, dass der Dienst Nachrichten an die Aktivität sendet. Einfache Strings und Ganzzahlen an dieser Stelle - meist Statusmeldungen. Die Nachrichten werden nicht regelmäßig gesendet. Ich denke nicht, dass das Abfragen des Dienstes ein guter Weg ist, wenn es einen anderen Weg gibt. Ich möchte diese Kommunikation nur, wenn die Aktivität vom Benutzer gestartet wurde. Ich möchte die Aktivität nicht vom Dienst aus starten. Wenn Sie also die Aktivität starten und der Dienst ausgeführt wird, werden in der Aktivitätsoberfläche einige Statusmeldungen angezeigt, wenn etwas Interessantes passiert. Wenn Sie die Aktivität nicht starten, werden diese Meldungen nicht angezeigt (sie sind nicht so interessant).

Es scheint, als sollte ich in der Lage sein, festzustellen, ob der Dienst ausgeführt wird, und wenn ja, fügen Sie die Aktivität als Listener hinzu. Entfernen Sie dann die Aktivität als Listener, wenn die Aktivität angehalten oder gestoppt wird. Ist das tatsächlich möglich? Die einzige Möglichkeit, dies herauszufinden, besteht darin, die Aktivität Parcelable implementieren zu lassen und eine AIDL-Datei zu erstellen, damit ich sie durch die Remote-Schnittstelle des Dienstes übergeben kann. Das scheint mir übertrieben zu sein, und ich habe keine Ahnung, wie die Activity writeToParcel ()/readFromParcel () implementieren soll.

Gibt es einen einfacheren oder besseren Weg? Danke für jede Hilfe.

BEARBEITEN:  

Für alle, die sich später für das interessieren, gibt es einen Beispielcode von Google für die Handhabung über AIDL im Beispielverzeichnis: /apis/app/RemoteService.Java

225
Scott Saunders

Es gibt drei offensichtliche Möglichkeiten, mit Diensten zu kommunizieren:

  1. Absichten verwenden
  2. AIDL verwenden
  3. Verwenden des Serviceobjekts selbst (als Singleton)

In Ihrem Fall würde ich mit Option 3 fortfahren. Stellen Sie einen statischen Verweis auf den Dienst selbst her und füllen Sie ihn in onCreate () auf: 

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

Erstellen Sie eine statische Funktion MyService getInstance(), die die statische sInstance zurückgibt. 

Dann starten Sie in Activity.onCreate() den Dienst, warten Sie asynchron, bis der Dienst tatsächlich gestartet wird (der Dienst könnte Ihre App benachrichtigen, dass sie bereit ist, indem Sie eine Absicht an die Aktivität senden.) Und ihre Instanz abrufen. Wenn Sie über die Instanz verfügen, registrieren Sie Ihr Service-Listener-Objekt bei Ihrem Service, und Sie werden festgelegt. ANMERKUNG: Wenn Sie Ansichten innerhalb der Aktivität bearbeiten, sollten Sie sie im UI-Thread ändern. Möglicherweise führt der Dienst einen eigenen Thread aus. Daher müssen Sie Activity.runOnUiThread() aufrufen.

Das letzte, was Sie tun müssen, ist das Entfernen des Verweises auf Ihr Listener-Objekt in Activity.onPause(). Andernfalls kann eine Instanz Ihres Aktivitätskontexts nicht gut funktionieren.

HINWEIS: Diese Methode ist nur nützlich, wenn nur Ihre Anwendung/Aktivität/Task auf Ihren Dienst zugreift. Ist dies nicht der Fall, müssen Sie die Option 1 oder 2 verwenden.

82
MrSnowflake

Der Fragesteller ist wahrscheinlich längst daran vorbeigegangen, aber falls jemand anderes danach sucht ...

Es gibt einen anderen Weg, um damit umzugehen, der meiner Meinung nach der einfachste sein könnte.

Fügen Sie Ihrer Aktivität einen BroadcastReceiver hinzu. Registrieren Sie es, um eine benutzerdefinierte Absicht in onResume zu erhalten und die Registrierung in onPause aufzuheben. Versenden Sie diese Absicht dann von Ihrem Dienst, wenn Sie Ihre Statusaktualisierungen senden möchten oder was Sie möchten.

Stellen Sie sicher, dass Sie nicht unglücklich sein würden, wenn eine andere App auf Ihre Absicht lauschte (könnte jemand etwas bösartiges tun?), Aber darüber hinaus sollten Sie in Ordnung sein.

Code-Beispiel wurde angefordert:

In meinem Dienst habe ich:

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

(RefreshTask.REFRESH_DATA_INTENT ist nur eine konstante Zeichenfolge.)

In meiner Höraktivität definiere ich meinen 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
        }
    }
}

Ich erkläre meinen Empfänger an der Spitze der Klasse: 

private DataUpdateReceiver dataUpdateReceiver;

Ich überschreibe onResume und füge hinzu: 

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

Und ich überschreibe onPause, um hinzuzufügen: 

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

Jetzt hört meine Aktivität auf meinen Dienst und sagt "Hey, gehen Sie auf den neuesten Stand." Ich könnte Daten im Intent übergeben, anstatt die Datenbanktabellen zu aktualisieren und dann nach den Änderungen in meiner Aktivität zu suchen. Da ich die Änderungen trotzdem beibehalten möchte, ist es sinnvoll, die Daten über db zu übergeben.

242
MaximumGoat

Verwenden Sie LocalBroadcastManager, um einen Empfänger zu registrieren, um nach einer von localservice in Ihrer App gesendeten Sendung zu suchen.

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

37
Jack Gao

Ich bin überrascht, dass niemand auf die Busbibliothek von Otto Event hingewiesen hat

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

Ich habe dies in meinen Android-Apps verwendet und es funktioniert nahtlos.

18
Madhur Ahuja

Die Verwendung eines Messenger ist eine weitere einfache Möglichkeit, zwischen einem Dienst und einer Aktivität zu kommunizieren.

Erstellen Sie in der Aktivität einen Handler mit einem entsprechenden Messenger. Dadurch werden Nachrichten von Ihrem Dienst verarbeitet.

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());

Der Messenger kann an den Dienst übergeben werden, indem er an eine Nachricht angehängt wird:

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

Ein vollständiges Beispiel finden Sie in den API-Demos: MessengerService und MessengerServiceActivity . Im vollständigen Beispiel finden Sie Informationen zur Funktionsweise von MyService.

18
Kjetil

Die andere Methode, die in den anderen Kommentaren nicht erwähnt wird, besteht darin, mithilfe von bindService () aus der Aktivität an den Dienst zu binden und eine Instanz des Diensts im ServiceConnection-Callback abzurufen. Wie hier beschrieben http://developer.Android.com/guide/components/bound-services.html

7
miguel

Ein anderer Weg könnte darin bestehen, Beobachter mit einer falschen Modellklasse durch die Aktivität und den Dienst selbst zu verwenden, um eine MVC-Mustervariation zu implementieren. Ich weiß nicht, ob es der beste Weg ist, dies zu erreichen, aber es ist der Weg, der für mich funktioniert hat. Wenn Sie ein Beispiel benötigen, fragen Sie danach und ich werde etwas posten.

2
ReDirEct__

Um auf @MrSnowflake zu antworten, antworten Sie mit einem Codebeispiel . Dies ist die XABBER-Open-Source-Klasse Application . Die Application-Klasse zentralisiert und koordiniert Listeners und ManagerInterfaces und mehr. Manager aller Art werden dynamisch geladen. Activity´s, der im Xabber gestartet wird, berichtet, in welcher Art von Listener sie sind. Und wenn ein Service-Start beginnt, meldet er sich als gestartet in der Application-Klasse. Um nun eine Nachricht an ein Activity zu senden, müssen Sie aus Ihrem Activity ein listener des Typs machen, den Sie benötigen. Im OnStart()OnPause() registrieren/unreg. Die Service kann die Application-Klasse genau nach dem listener fragen, mit dem sie sprechen muss. Wenn sie dort ist, ist die Aktivität bereit zu empfangen. 

Wenn Sie durch die Application-Klasse gehen, werden Sie feststellen, dass mehr Beute passiert als diese.

2
Erik

Sie können auch LiveData verwenden, das wie eine EventBus funktioniert.

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

Fügen Sie dann einen Beobachter aus Ihrer Activity hinzu.

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

Sie können mehr von diesem Blog lesen.

1
zeenosaur

Bindung ist eine andere Art zu kommunizieren

Erstellen Sie einen Rückruf

public interface MyCallBack{

   public void getResult(String result);

}

Aktivitätsseite:

  1. Implementieren Sie die Schnittstelle in der Aktivität
  2. Stellen Sie die Implementierung für die Methode bereit
  3. Binden Sie die Aktivität an den Service
  4. Registrieren und Aufheben der Registrierung eines Rückrufs, wenn der Service an Aktivität gebunden und von dieser getrennt wird.

    public class YourActivity extends AppCompatActivity implements MyCallBack{
    
          private Intent myIntent;
          private GPSService gpsService;
          private boolean bound = false;
    
          @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 startService(){
               myIntent = new Intent(this, GPSService.class);
               startService(myIntent );
          }
    
     }
    

Serviceseite:

  1. Rückruf initialisieren
  2. Rufen Sie bei Bedarf die Rückrufmethode auf

     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;
        }
     }
    
0
Rohit Singh

Wie von Madhur erwähnt, können Sie einen Bus für die Kommunikation verwenden.

Wenn Sie einen Bus verwenden, haben Sie einige Möglichkeiten:

Otto-Event-Busbibliothek (zugunsten von RxJava aufgegeben)

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

Der EventBus des Green Robot

http://greenrobot.org/eventbus/

NYBus (RxBus, implementiert mit RxJava. Sehr ähnlich dem EventBus)

https://github.com/MindorksOpenSource/NYBus

0
Mahpooya

Neben LocalBroadcastManager, Event Bus und Messenger, die bereits in dieser Frage beantwortet wurden, können Sie Pending Intent verwenden, um vom Dienst zu kommunizieren.

Wie bereits erwähnt hier in meinem Blogpost

Die Kommunikation zwischen Dienst und Aktivität kann mithilfe von .__ erfolgen. PendingIntent.For wir verwenden können createPendingResult (). createPendingResult () erstellt eine neue PendingIntent-Objekt, das Sie dem Dienst zur Verwendung und zum Senden übergeben können Ergebnisdaten zurück zu Ihrer Aktivität im Callback von onActivityResult (int, int, Intent). Da ein PendingIntent Parcelable ist und Wenn Sie also eine Absicht haben, kann Ihre Aktivität dies passieren PendingIntent für den Dienst. Der Dienst kann wiederum send () .__ aufrufen. Methode auf dem PendingIntent, um die Aktivität über .__ zu benachrichtigen. onActivityResult eines Ereignisses.

Aktivität

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

Bedienung

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

                }
            }
        }
    }
}
0

Meine Methode:

Klasse zum Verwalten von Sende- und Empfangsnachrichten von/zu Service/Aktivität:

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 **/

}

Im Aktivitätsbeispiel:

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 **/

Im Servicebeispiel:

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 */

Senden Sie eine Nachricht von Activity/Service:

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

Wie das funktioniert:

bei der Aktivität starten oder binden Sie den Dienst. Die Methoden "OnBind" des Diensts geben die Sammelmappe an seinen MessageManager zurück, und in der Aktivität "Implementierung der Schnittstellenverbindung" Dienstverbindung "OnServiceConnected" erhalten Sie diese IBinder und initiieren Ihren MessageManager using it . Nachdem die Activity ihren MessageManager initiiert hat, senden MessageHandler und Handshake an den Dienst, damit er seinen "MessageHandler" -Sender (den "privaten Messenger mMsgSender;" im MessageManager) einstellen kann. Auf diese Weise weiß der Dienst, an wen seine Nachrichten gesendet werden.

Sie können dies auch mithilfe eines Senders "List/Queue of Messenger" im MessageManager implementieren, um mehrere Nachrichten an verschiedene Aktivitäten/Dienste zu senden, oder Sie können im MessageManager einen List/Queue of Messenger-"Empfänger" verwenden, um mehrere Nachrichten zu empfangen Nachricht von verschiedenen Aktivitäten/Dienstleistungen.

In der Instanz "MessageManager" haben Sie eine Liste aller empfangenen Nachrichten.

Wie Sie sehen können, ist die Verbindung zwischen "Messenger der Aktivität" und "Service Messenger" unter Verwendung dieser "MessageManager" -Instanz automatisch. Dies geschieht über die Methode "OnServiceConnected" und die Verwendung des "Handshake".

Hoffe, das ist hilfreich für dich :) Vielen Dank! .__ Bye: D

0
Z3R0