Komunikacja między działalnością a służbą

[[0]}próbuję stworzyć własny odtwarzacz muzyki dla Androida. Gdzie doszedłem do problemu jest uruchamianie niektórych rzeczy w tle. Główna aktywność zarządza GUI i do tej pory wszystkie utwory są odtwarzane. Chciałem oddzielić lekcje gry na GUI i muzyki. Chcę umieścić część zarządzania muzyką w służbie i zostawić inne rzeczy takie, jakie są teraz.

Mój problem polega na tym, że nie mogę zorganizować komunikacji między aktywnością a usługą, ponieważ wiele komunikacji dzieje się między nimi, w tym poruszających się obiektów w w obu kierunkach. Próbowałem wielu technik, które Szukałem tutaj na Stack Overflow, ale za każdym razem miałem problemy. Potrzebuję usługi, aby móc wysyłać obiekty do aktywności i vice versa. Po dodaniu widżetu chcę również, aby był w stanie komunikować się z usługą.

Wszelkie wskazówki są mile widziane, jeśli potrzebujesz kodu źródłowego umieść komentarz poniżej, ale teraz w tym przejściu stało się chaotyczne.

Czy Jest jakiś bardziej zaawansowany tutorial na ten temat niż wywołanie jednej metody, która zwraca losową liczbę z obsługa? : P

EDIT: możliwym rozwiązaniem jest użycie biblioteki RoboGuice i przenoszenie obiektów za pomocą iniekcji

Author: Dejan, 2013-12-15

9 answers

Zaimplementowałem komunikację pomiędzy aktywnością a usługą za pomocą interfejsu Bind i Callbacks.

Do wysyłania danych do usługi użyłem Binder, który retrun usługi instace do aktywności, a następnie aktywność może uzyskać dostęp do publicznych metod w usłudze.

Aby wysłać dane z powrotem do aktywności z usługi, użyłem interfejsu wywołań zwrotnych, jakiego używasz, gdy chcesz komunikować się między fragmentem a aktywnością.

Oto kilka próbek kodu do każdy: Poniższy przykład pokazuje dwukierunkową zależność aktywności i usługi: Aktywność posiada 2 przyciski: Pierwszy przycisk uruchomi i zatrzyma usługę. Drugi przycisk uruchomi timer, który działa w serwisie.

Usługa będzie aktualizować aktywność poprzez callback z postępem timera.

Moja Aktywność:

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

A oto serwis:

 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);
    }
}
 61
Author: Moti Bartov,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-05-14 08:26:23

Aktualizacja: Lipiec 10 2016

IMO myślę, że używanie BroadcastReceiver dla niestandardowych wydarzeń jest lepszym sposobem jak Wspomniali posłańcy nie obsługują aktywności na urządzeniu rotacja, jak również możliwe wycieki pamięci.

Możesz utworzyć własny Odbiornik transmisji dla zdarzeń w aktywności, a następnie możesz również korzystać z komunikatorów.

  1. W Twoim Activity

    Utwórz klasę MessageHandler jako

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

    Teraz możesz mieć jest to instancja

    public static Handler messageHandler = new MessageHandler();
    

    Rozpocznij {[6] } z tym obiektem obsługi jako dodatkowymi danymi jako

    Intent startService = new Intent(context, SERVICE.class)
    startService.putExtra("MESSENGER", new Messenger(messageHandler));
    context.startService(startService);
    
  2. W Twoim Service otrzymujesz ten obiekt od intent i inicjalizujesz zmienną Messenger w usłudze jako

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

    A następnie napisz metodę sendMessage, aby wysyłać wiadomości do aktywności.

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

Powyższy przykładowy kod pokazuje i ukrywa pasek postępu w aktywności, gdy wiadomości są odbierane z usługi.

 54
Author: Rachit Mishra,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-07-10 13:47:51

Intencje są dobrym rozwiązaniem do komunikacji między aktywnością a Obsługą.

Szybkim rozwiązaniem dla odbioru intentów w Twojej usłudze jest podklasowanie IntentService klasy. Obsługuje asynchroniczne żądania wyrażone jako intencje za pomocą kolejki i wątku roboczego.

Do komunikacji z usługi do aktywności możesz nadawać intencję, ale zamiast używać zwykłej sendbroadcast () z kontekstu, bardziej efektywnym sposobem jest użycie LocalBroadcastManager z obsługi biblioteka.

Przykładowa usługa.

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

Przykładowa Aktywność

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);
    }
}
 13
Author: mpolci,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-03-22 07:45:37

Myślę, że jest problem z prawidłową odpowiedzią. Nie mam wystarczającej reputacji, by to skomentować.

Right in the answer: Wywołanie Activity bindService (), aby uzyskać wskaźnik do usługi, jest ok. Ponieważ kontekst usługi jest utrzymywany podczas utrzymywania połączenia.

Błąd w odpowiedzi: wskaźnik usługi do klasy Activity, aby oddzwonić, to zły sposób. Instancja Activity może nie null podczas kontekstu Activity jest tutaj Release = > exception.

Rozwiązanie za złe w odpowiedzi: usługa wysyłania intencji do aktywności. i zamiar odbiornika aktywności przez BroadcastReceiver.

Uwaga: w takim przypadku usługa i aktywność w tym samym procesie należy użyć LocalBroadcastManager, aby wysłać intencję. Poprawia wydajność i bezpieczeństwo

 5
Author: HungNM2,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-09-15 15:07:24

Najlepszym sposobem w tym przypadku jest komunikowanie się poprzez nadawanie z usługi dla różnych działań i odbieranie go w swojej działalności. Możesz utworzyć niestandardową transmisję i wysłać kody określające konkretne wydarzenia, takie jak ukończenie, zmiana, przygotowanie itp...

 2
Author: Vikram,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-12-15 13:20:06

Jest to prosty przykład komunikacji między aktywnością a usługą

Aktywność

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

Serwis

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);
    }
}
  • Jeśli nie wyrejestrujemy BroadcastReceiver będziemy mieli błąd, musisz wyrejestrować się, gdy aktywność go onPause, onStop, onDestroy...
  • Jeśli nie zarejestrujesz BroadcastReceiver po powrocie do aktywności, nie będzie słuchać niczego z usługi... usługa wyśle informacje do BroadcastReceiver, ale nie będzie otrzymuj cokolwiek, ponieważ nie jest zarejestrowane.
  • gdy utworzysz więcej niż jedną usługę, następujące usługi zaczną się w onStartCommand.
  • możesz przekazać informacje do serwisu z zamiarem i otrzymasz je w onStartCommand
  • różnica o return w onStartCommand: różnica między START_STICKY i START_REDELIVER_INTENT? i sprawdź oficjalną stronę google: usługi
 2
Author: DarckBlezzer,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 11:54:50
 0
Author: Dheeraj Sachan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-03-04 08:39:27

Bardzo łatwy, a zarazem potężny sposób użycia EventBus możesz dodać go do swojej kompilacji gradle i cieszyć się łatwym wzorem wydawcy / subskrybenta .

 0
Author: Melad,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-11-16 09:24:48

Najprostszym i najskuteczniejszym sposobem będzie użycie EventBus z Greenrobota.

Użyj prostych 3 kroków:

1 Define events

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

2 Przygotuj subskrybentów: Zadeklaruj i opisz swoją metodę subskrybowania, opcjonalnie określ tryb wątku:

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

Zarejestruj i Wyrejestruj swojego Abonenta. Na przykład na Androidzie aktywności i fragmenty powinny zazwyczaj rejestrować się zgodnie z cyklem życia:

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

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

3 Post wydarzenia:

EventBus.getDefault().post(new MessageEvent());
 0
Author: Khemraj,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-05-02 11:29:03