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?
Antworten
Zu viele Anzeigen?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.
Ein Beispiel findet sich in der Referenzdokumentation unter Beispiel für einen Remote Messenger Service .
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;
}
}
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)
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.