móviles - manual de programacion android pdf
Comunicación entre actividad y servicio (9)
Estoy tratando de hacer mi propio MusicPlayer para Android. Donde llegué a un problema está ejecutando algunas cosas en segundo plano. La actividad principal administra la GUI y hasta ahora todas las canciones se están reproduciendo. Quería separar la GUI y las clases de reproducción de música. Quiero poner la administración de música en el servicio y dejar otras cosas como están ahora.
Mi problema es que no puedo organizar la comunicación entre la actividad y el servicio, ya que hay mucha comunicación entre ellos, incluido el movimiento de objetos en ambas direcciones. Probé muchas técnicas que busqué aquí en Stack Overflow pero cada vez que tuve problemas. Necesito el Servicio para poder enviar objetos a la Actividad y viceversa. Cuando agrego widget también quiero que se pueda comunicar con el Servicio.
Se agradecen todos los consejos, si necesita el código fuente, coloque el comentario a continuación, pero ahora en esta transición se volvió caótico.
¿Hay algún tutorial más avanzado sobre esto que llamar a un método que devuelve un número aleatorio del servicio? :PAG
EDITAR: La solución posible es usar la biblioteca RoboGuice y mover objetos con inyección
Creo que hay un problema con la respuesta correcta. No tengo suficiente reputación para comentar al respecto.
Justo en la respuesta: La actividad llamada bindService () para obtener el puntero al Servicio está bien. Porque el contexto del servicio se mantiene cuando se mantiene la conexión.
mal en la respuesta: puntero de servicio a clase de actividad para devolver la llamada es mala. La instancia de la actividad puede no ser nula durante el contexto de la actividad. Se trata de una excepción => excepción aquí.
solución para lo incorrecto en la respuesta: servicio enviar intento a Actividad. y la intención del receptor de actividad a través de BroadcastReceiver.
Nota: en este caso, Servicio y Actividad en el mismo Proceso, debe usar LocalBroadcastManager para enviar intenciones. Mejora el rendimiento y la seguridad
Este es un ejemplo simple de comunicación entre actividad y servicio
Actividad
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");
}
}
}
Servicio
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);
}
}
- si no cancelamos el registro de BroadcastReceiver, tendremos un error, debe anular el registro cuando la actividad se active onPause, onStop, onDestroy ...
- si no registra BroadcastReceiver cuando vuelve a la actividad, no escuchará nada del servicio ... el servicio enviará información a BroadcastReceiver pero no recibirá nada porque no está registrado.
- Cuando crea más de un servicio, los siguientes servicios comenzarán en
onStartCommand
. - Puede pasar información al servicio con intención y la puede obtener en
onStartCommand
- Diferencia sobre el
return
enonStartCommand
: diferencia entre START_STICKY y START_REDELIVER_INTENT? y consulte el sitio web oficial de google: Services
Implementé la comunicación entre Actividad y Servicio utilizando la interfaz de Vinculación y Devolución de llamada.
Para enviar datos al servicio, utilicé Binder, que devuelve el Servicio a la Actividad, y luego la Actividad puede acceder a los métodos públicos en el Servicio.
Para enviar datos a la Actividad del Servicio, utilicé la interfaz de Devolución de llamada como la que está utilizando cuando desea comunicarse entre Fragmento y Actividad.
Aquí hay algunos ejemplos de código para cada uno: El siguiente ejemplo muestra la relación bidireccional Actividad y servicio: La actividad tiene 2 botones: El primer botón se iniciará y detendrá el servicio. El segundo botón iniciará un temporizador que se ejecuta en el servicio.
El servicio actualizará la Actividad a través de una devolución de llamada con el progreso del temporizador.
Mi actividad:
//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));
}
}
Y aquí está el servicio:
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);
}
}
La manera más fácil y eficiente será usar EventBus de EventBus .
Use 3 pasos simples:
1 Define eventos
public static class MessageEvent { /* Additional fields if needed */ }
2 Prepare suscriptores: declare y anote su método de suscripción; opcionalmente, especifique un modo de secuencia:
@Subscribe(threadMode = ThreadMode.MAIN)
public void onMessageEvent(MessageEvent event) {/* Do something */};
Regístrese y elimine el registro de su suscriptor. Por ejemplo, en Android, las actividades y los fragmentos generalmente deberían registrarse de acuerdo con su ciclo de vida:
@Override
public void onStart() {
super.onStart();
EventBus.getDefault().register(this);
}
@Override
public void onStop() {
super.onStop();
EventBus.getDefault().unregister(this);
}
3 eventos de publicaciones:
EventBus.getDefault().post(new MessageEvent());
La mejor manera en este caso es comunicarse haciendo transmisiones desde su servicio para diferentes acciones y recibirlas en su actividad. Puede crear una transmisión personalizada y enviar algunos códigos que definen eventos específicos como completar, cambiar, preparar, etc.
Los intentos son una buena solución para la comunicación entre la actividad y el servicio.
Una solución rápida para los intentos de recepción en su servicio es la subclasificación de la clase IntentService . Maneja solicitudes asíncronas expresadas como Intenciones utilizando una cola y un hilo de trabajo.
Para la comunicación del servicio a la actividad, puede transmitir el intento, pero en lugar de usar sendBroadcast normal () desde Context, una forma más eficiente es usar LocalBroadcastManager desde la biblioteca de soporte.
Ejemplo de servicio.
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);
}
}
Actividad de ejemplo
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);
}
}
Una manera muy fácil pero poderosa es usar EventBus , puede agregarlo a su compilación de gradle y disfrutar del sencillo patrón de editor / suscriptor.
echa un vistazo a la documentación de Android
http://developer.android.com/guide/components/bound-services.html#Binder
Actualización: 10 de julio de 2016
IMO Creo que usar BroadcastReceiver para eventos personalizados es una mejor manera que la mencionada por los Mensajeros de no manejar la actividad de recreación en la rotación del dispositivo, así como posibles fugas de memoria.
Puede crear un receptor BroadCast personalizado para eventos en la actividad. Luego, también puede usar Mensajeros.
En tu
Activity
crear una clase MessageHandler como
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; } } }
Ahora puedes tener su instancia como
public static Handler messageHandler = new MessageHandler();
Comience su
Service
con este objeto controlador como datos adicionales comoIntent startService = new Intent(context, SERVICE.class) startService.putExtra("MESSENGER", new Messenger(messageHandler)); context.startService(startService);
En su
Service
, recibe este objeto del intento e inicializa la variableMessenger
en el Servicio comoprivate Messenger messageHandler; Bundle extras = intent.getExtras(); messageHandler = (Messenger) extras.get("MESSENGER"); sendMessage(ProgressBarState.SHOW);
Y luego escribe un método
sendMessage
para enviar mensajes a la actividad.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(); } }
El código de ejemplo anterior muestra y oculta una barra de progreso en la actividad a medida que se reciben los mensajes del servicio.