it-swarm.com.de

Kommunikation zwischen Aktivität und Service

Ich versuche, meinen eigenen MusicPlayer für Android zu erstellen. Wo ich zu einem Problem gekommen bin, laufen einige Dinge im Hintergrund. Hauptaktivität verwaltet GUI und bis jetzt werden alle Songs abgespielt. Ich wollte GUI und Musikunterricht trennen. Ich möchte das Musikmanagement in den Service aufnehmen und andere Dinge so lassen, wie sie jetzt sind. 

Mein Problem ist, dass ich die Kommunikation zwischen Aktivität und Service nicht organisieren kann, da zwischen ihnen viel Kommunikation stattfindet, einschließlich der Bewegung von Objekten in beide Richtungen. Ich habe viele Techniken ausprobiert, die ich hier bei Stack Overflow gesucht habe, aber jedes Mal hatte ich Probleme. Ich brauche den Service, um Objekte an Activity senden zu können und umgekehrt. Wenn ich ein Widget hinzufüge, möchte ich auch, dass es mit dem Dienst kommunizieren kann.

Irgendwelche Tipps werden geschätzt, wenn Sie Quelltext-Kommentar benötigen, aber jetzt in diesem Übergang wurde es chaotisch.

Gibt es ein weiterführendes Tutorial dazu, als eine Methode aufzurufen, die eine Zufallszahl vom Dienst zurückgibt? : P 

BEARBEITEN: Mögliche Lösung ist die Verwendung der RoboGuice-Bibliothek und das Verschieben von Objekten mit Injektion

48
Dejan

Ich habe die Kommunikation zwischen Activity und Service mithilfe der Bind- und Callbacks-Schnittstelle implementiert. 

Zum Senden von Daten an den Dienst habe ich Binder verwendet, der den Dienst-Instace erneut für die Aktivität ausführt, und dann kann die Aktivität auf öffentliche Methoden im Dienst zugreifen. 

Um Daten aus dem Service an die Aktivität zurückzusenden, habe ich die Callbacks-Schnittstelle verwendet, wie Sie sie verwenden, wenn Sie zwischen Fragment und Aktivität kommunizieren möchten. 

Hier einige Codebeispiele: Das folgende Beispiel zeigt die bidirektionale Beziehung zwischen Aktivität und Dienst: Die Aktivität hat 2 Schaltflächen: Die erste Schaltfläche startet und stoppt den Dienst . Die zweite Schaltfläche wird angezeigt Einen Timer starten, der im Dienst läuft. 

Der Dienst aktualisiert die Aktivität durch Rückruf mit dem Timerfortschritt. 

Meine Aktivität:

    //Activity implements the Callbacks interface which defined in the Service  
    public class MainActivity extends ActionBarActivity implements MyService.Callbacks{

    ToggleButton toggleButton;
    ToggleButton tbStartTask;
    TextView tvServiceState;
    TextView tvServiceOutput;
    Intent serviceIntent;
    MyService myService;
    int seconds;
    int minutes;
    int hours;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
         serviceIntent = new Intent(MainActivity.this, MyService.class);
        setViewsWidgets();
    }

    private void setViewsWidgets() {
        toggleButton = (ToggleButton)findViewById(R.id.toggleButton);
        toggleButton.setOnClickListener(btListener);
        tbStartTask = (ToggleButton)findViewById(R.id.tbStartServiceTask);
        tbStartTask.setOnClickListener(btListener);
        tvServiceState = (TextView)findViewById(R.id.tvServiceState);
        tvServiceOutput = (TextView)findViewById(R.id.tvServiceOutput);

    }

    private ServiceConnection mConnection = new ServiceConnection() {

        @Override
        public void onServiceConnected(ComponentName className,
                                       IBinder service) {
            Toast.makeText(MainActivity.this, "onServiceConnected called", Toast.LENGTH_SHORT).show();
            // We've binded to LocalService, cast the IBinder and get LocalService instance
            MyService.LocalBinder binder = (MyService.LocalBinder) service; 
            myService = binder.getServiceInstance(); //Get instance of your service! 
            myService.registerClient(MainActivity.this); //Activity register in the service as client for callabcks! 
            tvServiceState.setText("Connected to service...");
            tbStartTask.setEnabled(true);
        }

        @Override
        public void onServiceDisconnected(ComponentName arg0) {
            Toast.makeText(MainActivity.this, "onServiceDisconnected called", Toast.LENGTH_SHORT).show();
            tvServiceState.setText("Service disconnected");
            tbStartTask.setEnabled(false);
        }
    };

    View.OnClickListener btListener =  new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            if(v == toggleButton){
                if(toggleButton.isChecked()){
                    startService(serviceIntent); //Starting the service 
                    bindService(serviceIntent, mConnection,            Context.BIND_AUTO_CREATE); //Binding to the service! 
                    Toast.makeText(MainActivity.this, "Button checked", Toast.LENGTH_SHORT).show();
                }else{
                    unbindService(mConnection);
                    stopService(serviceIntent);
                    Toast.makeText(MainActivity.this, "Button unchecked", Toast.LENGTH_SHORT).show();
                    tvServiceState.setText("Service disconnected");
                    tbStartTask.setEnabled(false);
                }
            }

            if(v == tbStartTask){
                if(tbStartTask.isChecked()){
                      myService.startCounter();
                }else{
                    myService.stopCounter();
                }
            }
        }
    };

    @Override
    public void updateClient(long millis) {
        seconds = (int) (millis / 1000) % 60 ;
        minutes = (int) ((millis / (1000*60)) % 60);
        hours   = (int) ((millis / (1000*60*60)) % 24);

        tvServiceOutput.setText((hours>0 ? String.format("%d:", hours) : "") + ((this.minutes<10 && this.hours > 0)? "0" + String.format("%d:", minutes) :  String.format("%d:", minutes)) + (this.seconds<10 ? "0" + this.seconds: this.seconds));
    }
}

Und hier ist der Service:

 public class MyService extends Service {
    NotificationManager notificationManager;
    NotificationCompat.Builder mBuilder;
    Callbacks activity;
    private long startTime = 0;
    private long millis = 0;
    private final IBinder mBinder = new LocalBinder();
    Handler handler = new Handler();
    Runnable serviceRunnable = new Runnable() {
        @Override
        public void run() {
            millis = System.currentTimeMillis() - startTime;
            activity.updateClient(millis); //Update Activity (client) by the implementd callback
            handler.postDelayed(this, 1000);
        }
    };


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

        //Do what you need in onStartCommand when service has been started
        return START_NOT_STICKY;
    }

    @Override
    public IBinder onBind(Intent intent) {
        return mBinder;
    }
    //returns the instance of the service
    public class LocalBinder extends Binder{
        public MyService getServiceInstance(){
            return MyService.this;
        }
    }

    //Here Activity register to the service as Callbacks client
    public void registerClient(Activity activity){
        this.activity = (Callbacks)activity;
    }

    public void startCounter(){
        startTime = System.currentTimeMillis();
        handler.postDelayed(serviceRunnable, 0);
        Toast.makeText(getApplicationContext(), "Counter started", Toast.LENGTH_SHORT).show();
    }

    public void stopCounter(){
        handler.removeCallbacks(serviceRunnable);
    }


    //callbacks interface for communication with service clients! 
    public interface Callbacks{
        public void updateClient(long data);
    }
}
66
Moti Bartov

Update: 10. Juli 2016

IMO Ich denke, BroadcastReceiver für benutzerdefinierte Ereignisse zu verwenden, ist der bessere Weg wie die erwähnten Messengers nicht mit der Aktivitätserholung auf dem Gerät umgehen Rotation sowie mögliche Speicherverluste.

Sie können einen benutzerdefinierten BroadCast-Empfänger für Ereignisse in der Aktivität erstellen. Dann können Sie auch Botschafter verwenden.

  1. In deiner Activity 

    erstellen Sie eine MessageHandler-Klasse als 

    public static class MessageHandler extends Handler {
        @Override
        public void handleMessage(Message message) {
            int state = message.arg1;
            switch (state) {
            case HIDE:
                progressBar.setVisibility(View.GONE);
                break;
            case SHOW:
                progressBar.setVisibility(View.VISIBLE);
                break;
            }
        }
    }
    

    Jetzt können Sie es als Instanz haben 

    public static Handler messageHandler = new MessageHandler();
    

    Starten Sie Ihre Service mit diesem Handler-Objekt als Zusatzdaten als 

    Intent startService = new Intent(context, SERVICE.class)
    startService.putExtra("MESSENGER", new Messenger(messageHandler));
    context.startService(startService);
    
  2. In Ihrer Service erhalten Sie dieses Objekt aus der Absicht und initialisieren die Messenger-Variable in Service as

    private Messenger messageHandler;
    Bundle extras = intent.getExtras();
    messageHandler = (Messenger) extras.get("MESSENGER");
    sendMessage(ProgressBarState.SHOW);
    

    Schreiben Sie dann eine Methode sendMessage, um Nachrichten an die Aktivität zu senden.

    public void sendMessage(ProgressBarState state) {
    Message message = Message.obtain();
    switch (state) {
        case SHOW :
            message.arg1 = Home.SHOW;
            break;
        case HIDE :
            message.arg1 = Home.HIDE;
            break;
    }
    try {
        messageHandler.send(message);
    } catch (RemoteException e) {
        e.printStackTrace();
    }
    }
    

Der obige Beispielcode zeigt und verbirgt eine Fortschrittsleiste in Activity, wenn Nachrichten vom Dienst empfangen werden.

54
Rachit Mishra

Absichten sind eine gute Lösung für die Kommunikation zwischen Activitiy und Service.

Eine schnelle Lösung für die Empfangsabsichten in Ihrem Service ist die Unterklasse IntentService class. Es verarbeitet asynchrone Anforderungen, die als Intents ausgedrückt werden, unter Verwendung einer Warteschlange und eines Arbeitsthreads.

Für die Kommunikation von Dienst zu Aktivität können Sie die Absicht übertragen, aber anstatt normales sendBroadcast () von Context zu verwenden, ist der effektivere Weg die Verwendung von LocalBroadcastManager aus der Support-Bibliothek.

Beispieldienst. 

public class MyIntentService extends IntentService {
    private static final String ACTION_FOO = "com.myapp.action.FOO";
    private static final String EXTRA_PARAM_A = "com.myapp.extra.PARAM_A";

    public static final String BROADCAST_ACTION_BAZ = "com.myapp.broadcast_action.FOO";
    public static final String EXTRA_PARAM_B = "com.myapp.extra.PARAM_B";

    // called by activity to communicate to service
    public static void startActionFoo(Context context, String param1) {
        Intent intent = new Intent(context, MyIntentService.class);
        intent.setAction(ACTION_FOO);
        intent.putExtra(EXTRA_PARAM1, param1);
        context.startService(intent);
    }

    public MyIntentService() {
        super("MyIntentService");
    }

    @Override
    protected void onHandleIntent(Intent intent) {
        if (intent != null) {
            final String action = intent.getAction();
            if (ACTION_FOO.equals(action)) {
                final String param1 = intent.getStringExtra(EXTRA_PARAM_A);
                // do something
            }
        }
    }

    // called to send data to Activity
    public static void broadcastActionBaz(String param) {
        Intent intent = new Intent(BROADCAST_ACTION_BAZ);
        intent.putExtra(EXTRA_PARAM_B, param);
        LocalBroadcastManager bm = LocalBroadcastManager.getInstance(this);
        bm.sendBroadcast(intent);
    }
}

Beispielaktivität

public class MainActivity extends ActionBarActivity {

    // handler for received data from service
    private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            if (intent.getAction().equals(MyIntentService.BROADCAST_ACTION_BAZ)) {
                final String param = intent.getStringExtra(EXTRA_PARAM_B);
                // do something
            }
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        IntentFilter filter = new IntentFilter();
        filter.addAction(MyIntentService.BROADCAST_ACTION_BAZ);
        LocalBroadcastManager bm = LocalBroadcastManager.getInstance(this);
        bm.registerReceiver(mBroadcastReceiver, filter);
    }

    @Override
    protected void onDestroy() {
        LocalBroadcastManager bm = LocalBroadcastManager.getInstance(this);
        bm.unregisterReceiver(mBroadcastReceiver);
        super.onDestroy();
    }

    // send data to MyService
    protected void communicateToService(String parameter) {
        MyIntentService.startActionFoo(this, parameter);
    }
}
14
mpolci

Ich denke, es gibt ein Problem mit der richtigen Antwort. Ich habe nicht genug Ruf, um mich dazu zu äußern.

Direkt in der Antwort: Aktivitätsaufruf bindService (), um einen Zeiger auf Service zu erhalten, ist in Ordnung. Weil der Dienstkontext beibehalten wird, wenn die Verbindung beibehalten wird.

falsch in der Antwort: Service-Zeiger auf die Aktivitätsklasse, um zurückzurufen, ist ein schlechter Weg. Die Aktivitätsinstanz kann während des Aktivitätskontexts möglicherweise nicht null sein. Hier wird Release => Ausnahme angezeigt.

Lösung für das falsche in der Antwort: Dienst sendet Absicht an Aktivität. und Aktivitätsempfängerabsicht über BroadcastReceiver.

Hinweis: In diesem Fall, Service und Aktivität im selben Prozess, sollten Sie LocalBroadcastManager zum Senden der Absicht verwenden. Es verbessert die Leistung und Sicherheit

5
HungNM2

In diesem Fall ist es am besten, wenn Sie kommunizieren, indem Sie von Ihrem Dienst aus verschiedene Aktionen senden und diese in Ihrer Aktivität empfangen. Sie können eine benutzerdefinierte Sendung erstellen und einige Codes senden, die bestimmte Ereignisse definieren, z. B. abschließen, ändern, vorbereiten usw.

2
Vikram

Dies ist ein einfaches Beispiel für die Kommunikation zwischen Aktivität und Service

Aktivität

MyReceiver myReceiver; //my global var receiver
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.layourAwesomexD);
    registerReceiver();
}

//When the activity resume, the receiver is going to register...
@Override
protected void onResume() {
    super.onResume();
    checkStatusService(); // verficarStatusServicio(); <- name change
    registerReceiver();
}
//when the activity stop, the receiver is going to unregister...
@Override
protected void onStop() {
    unregisterReceiver(myReceiver); //unregister my receiver...
    super.onStop();
}
//function to register receiver :3
private void registerReceiver(){
    //Register BroadcastReceiver
    //to receive event from our service
    myReceiver = new MyReceiver();
    IntentFilter intentFilter = new IntentFilter();
    intentFilter.addAction(MyService.SENDMESAGGE);
    registerReceiver(myReceiver, intentFilter);
}

// class of receiver, the magic is here...
private class MyReceiver extends BroadcastReceiver {

    @Override
    public void onReceive(Context arg0, Intent arg1) {
        //verify if the extra var exist
        System.out.println(arg1.hasExtra("message")); // true or false
        //another example...
        System.out.println(arg1.getExtras().containsKey("message")); // true or false
        //if var exist only print or do some stuff
        if (arg1.hasExtra("message")) {
            //do what you want to
            System.out.println(arg1.getStringExtra("message"));
        }    
    }
}

public void checkStatusService(){
    if(MyService.serviceStatus!=null){
        if(MyService.serviceStatus == true){
            //do something
            //textview.text("Service is running");
        }else{
            //do something
            //textview.text("Service is not running");
        }
    }
}

Bedienung

public class MyService extends Service {

final static String SENDMESAGGE = "passMessage";

public static Boolean serviceStatus = false;

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

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

@Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        //you service etc...             
        passMessageToActivity("hello my friend this an example of send a string...");
        return START_STICKY;
    }

@Override
    public void onDestroy() {
        super.onDestroy();
        passMessageToActivity("The service is finished, This is going to be more cooler than the heart of your ex...");
        System.out.println("onDestroy");
        serviceStatus=false;
   }

private void passMessageToActivity(String message){
        Intent intent = new Intent();
        intent.setAction(SENDMESAGGE);
        intent.putExtra("message",message);
        sendBroadcast(intent);
    }
}
  • wenn wir die Registrierung von BroadcastReceiver nicht aufheben, wird ein Fehler auftreten. Sie müssen die Registrierung aufheben, wenn die Aktivität onPause, onStop, onDestroy geht.
  • wenn Sie BroadcastReceiver nicht registrieren, wenn Sie wieder aktiv sind, hört der Dienst nichts. Der Dienst sendet Informationen an BroadcastReceiver, empfängt jedoch nichts, da er nicht registriert ist.
  • Wenn Sie mehrere Services erstellen, beginnen die folgenden Services in onStartCommand
  • Sie können Informationen mit Absicht an den Dienst übergeben und Sie erhalten sie in onStartCommand.
  • Unterschied über return in onStartCommand: Unterschied zwischen START_STICKY und START_REDELIVER_INTENT? und überprüfen Sie die offizielle Website von Google: Services
2
DarckBlezzer

Der einfachste und effizienteste Weg ist EventBus von GreenRobot.

Verwenden Sie einfache 3 Schritte:

1 Ereignisse definieren

public static class MessageEvent { /* Additional fields if needed */ }

2 Abonnenten vorbereiten: Deklarieren und kommentieren Sie Ihre Abonnementmethode, geben Sie optional einen Thread-Modus an:

@Subscribe(threadMode = ThreadMode.MAIN)  
public void onMessageEvent(MessageEvent event) {/* Do something */};

Registrieren Sie Ihren Abonnenten und heben Sie die Registrierung auf. Bei Android sollten sich Aktivitäten und Fragmente normalerweise gemäß ihrem Lebenszyklus registrieren:

@Override
 public void onStart() {
     super.onStart();
     EventBus.getDefault().register(this);
 }

 @Override
 public void onStop() {
     super.onStop();
     EventBus.getDefault().unregister(this);
 }

3 Post-Events:

EventBus.getDefault().post(new MessageEvent());
0
Khemraj

Eine sehr einfache und dennoch leistungsstarke Methode ist die Verwendung von EventBus. Sie können es zu Ihrem Gradle-Build hinzufügen und das einfache Publisher/Subscriber-Muster genießen.

0
Melad
0
Dheeraj Sachan