10 Stimmen

Wie kann ein entfernter Dienst Nachrichten an eine gebundene Aktivität senden?

Ich habe die Dokumentation über Bound Services in dem gezeigt wird, dass man sich problemlos über Nachrichten von einer Aktivität zu einem entfernten (d.h. nicht im selben Kontext befindlichen) Dienst, aber gibt es eine Möglichkeit, Nachrichten vom Dienst a la gebundene Aktivität ? Meine Aktivität bindet sich zum Beispiel an einen laufenden Hintergrunddienst derselben Anwendung, sendet eine Nachricht an ihn, und beim Empfang dieser Nachricht antwortet der Dienst mit einer Nachricht an die Aktivität wie implementiere ich das? Können Sie mich auf eine Dokumentation verweisen, die dieses Thema erklärt?

29voto

Danuofr Punkte 1506

HINWEIS: Dies gilt nur für prozessbegleitende Dienstleistungen und Tätigkeiten, nicht für Ferndienstleistungen wie in der Frage beschrieben.

Die Verwendung eines Dienstes zur Kommunikation mit einer Aktivität erfordert die Erstellung eines Hörers, den Sie von der Aktivität an den Dienst übergeben können.

Sie müssen einen Dienst erstellen, der an eine Aktivität gebunden ist.

Der erste Schritt ist die Einrichtung eines Dienstes. Stellen Sie sicher, dass Sie im Dienst ein Binder-Objekt und die Methode zur Rückgabe eines Binder-Objekts haben. Unten sehen Sie ein Beispiel, das ich in meinem Dienst verwendet habe, um meinen Binder abzurufen. Beachten Sie auch, dass dieses Binder-Objekt eine Methode hat, um einen Listener zu setzen, der im Dienst als Feld vom Typ BoundServiceListener gespeichert wird.

/**
 * Class used for the client Binder.  Because we know this service always
 * runs in the same process as its clients, we don't need to deal with IPC.
 */
public class DownloadBgBinder extends Binder {

    public DownloadBgService getService() {
        // Return this instance of LocalService so clients can call public methods
        return DownloadBgService.this;
    }

    public void setListener(BoundServiceListener listener) {
        mListener = listener;
    }
}

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

Nun müssen Sie eine Art Schnittstelle erstellen, die Sie an das Binder-Objekt übergeben können, an das Ihr Dienst Aktualisierungen senden kann. Unten sehen Sie meinen BoundServiceListener.

public interface BoundServiceListener {

    public void sendProgress(double progress);
    public void finishedDownloading();
}

In Ihrer Aktivität müssen Sie nun ein ServiceConnection-Objekt erstellen, das für die Anbindung an einen Dienst verwendet wird. SO fügen Sie etwas wie dies hinzu.

/** Defines callbacks for service binding, passed to bindService() */
private ServiceConnection mConnection = new ServiceConnection() {

    @Override
    public void onServiceDisconnected(ComponentName arg0) {
        mBound = false;
    }

    @Override
    public void onServiceConnected(ComponentName name, IBinder service) {
        // We've bound to LocalService, cast the IBinder and get LocalService instance
        DownloadBgBinder binder = (DownloadBgBinder) service;
        mService = binder.getService();
        binder.setListener(new BoundServiceListener() {

            @Override
            public void sendProgress(double progress) {
                // Use this method to update our download progress
            }

            @Override
            public void finishedDownloading() {

            }   
        });

        mBound = true;
    }

Die wichtigste Zeile, die hier zu beachten ist, lautet

binder.setListener(new BoundServiceListener() {

    @Override
    public void sendProgress(double progress) {
        // Use this method to update our download progress
    }

    @Override
    public void finishedDownloading() {

    }
});

Dieser Teil ist, wo ich tatsächlich meine BoundServiceListener-Schnittstelle an die Serviceklasse senden. Die Dienstklasse verwendet dann dieses Listener-Objekt hier

    if (mListener!=null)
        mListener.finishedDownloading();
    if (mListener!=null)
        mListener.sendProgress(percent);

Jetzt können Sie dies an jeder beliebigen Stelle in Ihrer Dienstklasse einfügen, und Ihre Aktivität wird Ihre Fortschrittsaktualisierung erhalten.

Stellen Sie außerdem sicher, dass Sie die folgenden Schritte in Ihre Aktivität aufnehmen, um den Dienst tatsächlich zu binden und zu starten.

Intent intent = new Intent(this, DownloadBgService.class);
startService(intent);
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);

Denken Sie daran, dass ein Dienst, auch wenn Sie ihn binden, erst dann tatsächlich gestartet wird, wenn Sie start service aufrufen. Die Bindung an den Dienst verbindet den Dienst lediglich mit einer Aktivität. Die Methode startService() ruft den Dienst auf

onStartCommand(Intent intent, int flags, int startId)

Geben Sie Ihren Dienst auch in Ihrem Manifest an

<service android:name=".services.DownloadBgService" />

Heben Sie auch die Bindung des Dienstes auf, wenn die Aktivität geht, indem Sie

@Override
protected void onStop() {
    super.onStop();
    // Unbind from the service
    if (mBound) {
        unbindService(mConnection);
        mBound = false;
    }
}

Ich hoffe, das hilft.

7voto

Gianni Costanzi Punkte 6044

Ein Beispiel findet sich in der Referenzdokumentation unter Beispiel für einen Remote Messenger Service .

1voto

Kurz gesagt, die Antwort ist die Zuweisung eines Messengers mit ResponseHandler an msg.replyTo() . Im folgenden Beispiel sehen wir, wie wir das machen.

Beschreiben Sie kurz, was dieses Beispiel bewirkt: In diesem Beispiel haben wir eine Schaltfläche in MainActivity, deren onClick() mit sendMessage(View view) verknüpft ist. Sobald die Schaltfläche angeklickt wird, senden wir eine benutzerdefinierte Nachricht an RemoteService. Sobald die benutzerdefinierte Nachricht im RemoteService empfangen wurde, fügen wir die CurrentTime an die benutzerdefinierte Nachricht an und senden sie zurück an MainActivity.

MainActivity.java

public class MainActivity extends AppCompatActivity {

    ServiceConnector serviceConnector;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        this.serviceConnector = new ServiceConnector();
        Intent intent = new Intent(this,RemoteService.class);
        bindService(intent,serviceConnector, Context.BIND_AUTO_CREATE);
    }

    public void sendMessage(View view) {
        Message msg = Message.obtain();
        msg.replyTo = new Messenger(new ResponseHandler(this));
        Bundle bundle = new Bundle();
        bundle.putString("MyString", "Time");
        msg.setData(bundle);
        try {
            this.serviceConnector.getMessenger().send(msg);
        } catch (RemoteException e) {
            e.printStackTrace();
        }
    }
}

ResponseHandler.java

public class ResponseHandler extends Handler {

    MainActivity mainActivity;
    public ResponseHandler(Context context){
        this.mainActivity = (MainActivity) context;
    }
    @Override
    public void handleMessage(@NonNull Message msg) {
        Bundle data = msg.getData();
        String dataString = data.getString("respData");
        Toast.makeText(this.mainActivity,dataString,Toast.LENGTH_SHORT).show();
    }
}

ServiceConnector.java

public class ServiceConnector implements ServiceConnection {

    private Messenger messenger;

    @Override
    public void onServiceConnected(ComponentName componentName, IBinder iBinder) 
   {
          this.messenger = new Messenger(iBinder);
    }

    @Override
    public void onServiceDisconnected(ComponentName componentName) {
        this.messenger = null;
    }

    public Messenger getMessenger(){
        return this.messenger;
    }
}

RemoteService.java

public class RemoteService extends Service {

    private final IBinder iBinder = new Messenger(new IncomingHandler(this)).getBinder();

    @Override
    public IBinder onBind(Intent intent) {
        // TODO: Return the communication channel to the service.
        return iBinder;
    }

}

IncomingHandler.java

public class IncomingHandler extends Handler {

    private RemoteService remoteService;

    public IncomingHandler(Context context)
    {
        this.remoteService = (RemoteService)context;
    }

    public RemoteService getService()
    {
        return this.remoteService;
    }

    @Override
    public void handleMessage(@NonNull Message msg) {
        try {
            msg.replyTo.send(getCurrentTime(msg));
        } catch (RemoteException e) {
            e.printStackTrace();
        }
    }

    public Message getCurrentTime(Message msg){
        SimpleDateFormat dateFormat = new SimpleDateFormat("HH:mm:ss MM/dd/yyyy", Locale.US);
        Message resp = Message.obtain();
        Bundle bResp = new Bundle();
        bResp.putString("respData", msg.getData().getString("MyString") + " : " +(dateFormat.format(new Date())).toString());
        resp.setData(bResp);
        return resp;

    }
}

0voto

ceph3us Punkte 7061

1) Implementieren von Transact/OnTransact-Methoden in eigenen Binder.class- und Binder-Proxy-Objekten, die IInterface.class implementieren (anon oder durch direkte Erweiterung einer Klasse), unter Verwendung der in diesen Methoden übergebenen Parcel.class-Objekte
2) lokale Schnittstelle an eigenes Binder-Objekt anhängen 3) Dienst erstellen und eine Binder-Proxy-Implementierung von der onBind-Methode zurückgeben 4) Bindung mit bindService(ServiceConnection) erstellen 5) dies führt zur Rückgabe eines Proxy-Binders über die erstellte Bindung in der interfece Implementierung

dies ist eine Android-Implementierung von IPC mit Nutzung des Kernel-Binderraums

Vereinfachung im Codebeispiel :

class ServiceIPC extends Service {

    @Override
    public Binder onBind()  {
        return new IInterface() {

            IInterface _local = this;         

            @Override 
            public IBinder asBinder() {
               return new Binder() 

                           {   
                               //
                               // allow distinguish local/remote impl 
                               // avoid overhead by ipc call 
                               // see Binder.queryLocalInterface("descriptor");
                               //
                               attachLocalInterface(_local,"descriptor");
                           }

                           @Override
                           public boolean onTransact(int code,
                                                     Parcel in,
                                                     Parcel out,
                                                     int flags) 
                                   throws RemoteException {
                               //
                               //  your talk between client & service goes here 
                               //
                               return whatsoever // se super.onTransact(); 
                           }
                      }
            }    

        }.asBinder();
    }

}

*dann könnte man die IBinder auf Client- und Service-Seite die Transact-Methode verwenden, um miteinander zu kommunizieren (4 Beispiel mit odd/eve-Codes, um die lokale Remote-Seite zu disgusting, da wir die gleiche onTransact-Methode für die Standseiten verwenden)

0voto

j2emanue Punkte 56131

Sollte dies mit Hilfe von . a tun können AIDL-Datei wie Android billing api tut. es ist eine Möglichkeit, RPC-Aufrufe (Kommunikation über Remote-Prozesse) zu tun. aber Sie müssen jede Methode, die Sie verwenden möchten, zu deklarieren. wie die Schnittstelle oben bereits erwähnt.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X