J'essaie de créer mon propre MusicPlayer pour Android. Là où j’ai eu un problème, j’exécute certaines choses en arrière-plan. L’activité principale gère l’interface graphique et jusqu’à présent toutes les chansons sont en cours de lecture. Je voulais séparer l'interface utilisateur graphique et la musique en cours. Je veux mettre la gestion de la musique dans Service et laisser les autres choses telles qu’elles sont maintenant.
Mon problème est que je ne peux pas organiser la communication entre l'activité et le service car beaucoup de communication se passe entre eux, y compris des objets en mouvement dans les deux sens. J'ai essayé de nombreuses techniques que j'ai recherchées ici sur Stack Overflow mais à chaque fois que j'ai eu des problèmes. J'ai besoin de Service pour pouvoir envoyer des objets à Activity et vice versa. Lorsque j'ajoute un widget, je souhaite également qu'il puisse communiquer avec le service.
Tous les conseils sont appréciés, si vous avez besoin du commentaire de code source ci-dessous, mais maintenant, dans cette transition, il est devenu chaotique.
Existe-t-il un didacticiel plus avancé que l’appel d’une méthode renvoyant un nombre aléatoire du service? : P
EDIT: la solution possible consiste à utiliser la bibliothèque RoboGuice et à déplacer des objets avec injection
J'ai mis en place une communication entre Activity et Service à l'aide des interfaces Bind et Callbacks.
Pour envoyer des données au service, j'ai utilisé Binder qui renvoie l'instance de service à l'activité, puis l'activité peut accéder aux méthodes publiques du service.
Pour renvoyer des données à l’activité depuis le service, j’ai utilisé l’interface de rappel comme vous l’utilisez lorsque vous souhaitez communiquer entre Fragment et Activity.
Voici quelques exemples de code pour chacun: L'exemple suivant illustre la relation bidirectionnelle Activité et Service: L'activité comporte 2 boutons: Le premier bouton permet de démarrer et d'arrêter le service . Le deuxième bouton démarrer une minuterie qui s'exécute dans le service.
Le service mettra à jour l'activité via un rappel avec la progression du minuteur.
Mon activité:
//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));
}
}
Et voici le 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);
}
}
Mise à jour: 10 juillet 2016
OMI Je pense que l’utilisation de BroadcastReceiver pour des événements personnalisés est un meilleur moyen comme les messagers mentionnés ne gèrent pas la récréation d'activité sur l'appareil rotation ainsi que les éventuelles fuites de mémoire.
Vous pouvez créer un récepteur BroadCast personnalisé pour les événements de l'activité. Vous pouvez également utiliser Messengers.
Dans votre Activity
créer une classe MessageHandler en tant que
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;
}
}
}
Maintenant, vous pouvez avoir son exemple
public static Handler messageHandler = new MessageHandler();
Commencez votre Service
avec cet objet Handler en tant que données supplémentaires en tant que
Intent startService = new Intent(context, SERVICE.class)
startService.putExtra("MESSENGER", new Messenger(messageHandler));
context.startService(startService);
Dans votre Service
, vous recevez cet objet de l'intention et initialisez la variable Messenger
dans Service en tant que
private Messenger messageHandler;
Bundle extras = intent.getExtras();
messageHandler = (Messenger) extras.get("MESSENGER");
sendMessage(ProgressBarState.SHOW);
Et écrivez ensuite une méthode sendMessage
pour envoyer des messages à activity.
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();
}
}
L'exemple de code ci-dessus affiche et masque une barre de progression dans l'activité lorsque les messages sont reçus du service.
Les intentions sont une bonne solution pour la communication entre l’Activité et le Service.
Une solution rapide pour recevoir les intentions de votre service consiste à sous-classer IntentService class. Il traite les demandes asynchrones exprimées en tant qu'intentions à l'aide d'une file d'attente et d'un thread de travail.
Pour la communication de service à activité, vous pouvez diffuser l'intention, mais au lieu d'utiliser sendBroadcast () normal de Context, une méthode plus efficace consiste à utiliser LocalBroadcastManager depuis la bibliothèque de support.
Exemple de service.
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);
}
}
Exemple d'activité
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);
}
}
Je pense qu'il y a un problème avec la bonne réponse. Je n'ai pas assez de réputation pour commenter.
Droit dans la réponse: Activité appelez bindService () pour obtenir le pointeur sur le service est ok. Parce que le contexte de service est maintenu lorsque la connexion est maintenue.
Faux dans la réponse: Le pointeur de service sur la classe d'activité à rappeler est un mauvais moyen. L'instance d'activité n'est peut-être pas nulle pendant le contexte d'activité. Version => exception ici.
solution pour le mauvais dans la réponse: service envoyer l'intention à l'activité. et l'intention du récepteur d'activité via BroadcastReceiver.
Remarque: Dans ce cas, Service et Activité dans le même processus, vous devez utiliser LocalBroadcastManager pour envoyer une intention. Cela améliore les performances et la sécurité
Le meilleur moyen dans ce cas est de communiquer en diffusant à partir de votre service pour différentes actions et en le recevant dans votre activité. Vous pouvez créer une diffusion personnalisée et envoyer des codes définissant des événements spécifiques tels que compléter, modifier, préparer, etc.
Ceci est un exemple simple de communication entre activité et service
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");
}
}
}
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);
}
}
onStartCommand
. onStartCommand
return
dans onStartCommand
: Différence entre START_STICKY et START_REDELIVER_INTENT? et consultez le site officiel de google: ServicesLe moyen le plus simple et le plus efficace consiste à utiliser EventBus de GreenRobot.
Utilisez 3 étapes simples:
1 Définir les événements
public static class MessageEvent { /* Additional fields if needed */ }
2 Préparer les abonnés: Déclarez et annotez votre méthode d'abonnement, spécifiez éventuellement un mode de thread:
@Subscribe(threadMode = ThreadMode.MAIN)
public void onMessageEvent(MessageEvent event) {/* Do something */};
Enregistrez et désinscrivez votre abonné. Par exemple, sur Android, les activités et les fragments doivent normalement s'inscrire en fonction de leur cycle de vie:
@Override
public void onStart() {
super.onStart();
EventBus.getDefault().register(this);
}
@Override
public void onStop() {
super.onStop();
EventBus.getDefault().unregister(this);
}
3 événements post:
EventBus.getDefault().post(new MessageEvent());
Le moyen très simple et puissant consiste à utiliser EventBus, vous pouvez l’ajouter à votre génération et profiter du modèle facile éditeur/abonné.
consultez la documentation Android
http://developer.Android.com/guide/components/bound-services.html#Binder