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
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);
}
}
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.
-
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);
-
W Twoim
Service
otrzymujesz ten obiekt od intent i inicjalizujesz zmiennąMessenger
w usłudze jakoprivate 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.
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);
}
}
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
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...
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
wonStartCommand
: różnica między START_STICKY i START_REDELIVER_INTENT? i sprawdź oficjalną stronę google: usługi
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
Zajrzyj do dokumentacji Androida
Http://developer.android.com/guide/components/bound-services.html#Binder
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 .
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());
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