Je ne trouvais aucun exemple montrant comment envoyer des messages entre une activité et un service, et j'ai passé beaucoup trop de temps à le découvrir. Voici un exemple de projet pour les autres à référencer.
Cet exemple vous permet de démarrer ou d'arrêter un service directement, et de lier/dissocier séparément du service. Lorsque le service est en cours d'exécution, il incrémente un nombre à 10 Hz. Si l'activité est liée à Service
, la valeur actuelle sera affichée. Les données sont transférées sous forme d'entier et de chaîne afin que vous puissiez voir comment le faire de deux manières différentes. Il existe également des boutons dans l'activité permettant d'envoyer des messages au service (modifie la valeur d'incrémentation).
Capture d'écran:
AndroidManifest.xml:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:Android="http://schemas.Android.com/apk/res/Android"
package="com.exampleservice"
Android:versionCode="1"
Android:versionName="1.0">
<application Android:icon="@drawable/icon" Android:label="@string/app_name">
<activity Android:name=".MainActivity"
Android:label="@string/app_name">
<intent-filter>
<action Android:name="Android.intent.action.MAIN" />
<category Android:name="Android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service Android:name=".MyService"></service>
</application>
<uses-sdk Android:minSdkVersion="8" />
</manifest>
res\values \ strings.xml:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">ExampleService</string>
<string name="service_started">Example Service started</string>
<string name="service_label">Example Service Label</string>
</resources>
res\layout\main.xml:
<RelativeLayout
Android:id="@+id/RelativeLayout01"
Android:layout_width="fill_parent"
Android:layout_height="wrap_content" >
<Button
Android:id="@+id/btnStart"
Android:layout_width="wrap_content"
Android:layout_height="wrap_content"
Android:text="Start Service" >
</Button>
<Button
Android:id="@+id/btnStop"
Android:layout_width="wrap_content"
Android:layout_height="wrap_content"
Android:layout_alignParentRight="true"
Android:text="Stop Service" >
</Button>
</RelativeLayout>
<RelativeLayout
Android:id="@+id/RelativeLayout02"
Android:layout_width="fill_parent"
Android:layout_height="wrap_content" >
<Button
Android:id="@+id/btnBind"
Android:layout_width="wrap_content"
Android:layout_height="wrap_content"
Android:text="Bind to Service" >
</Button>
<Button
Android:id="@+id/btnUnbind"
Android:layout_width="wrap_content"
Android:layout_height="wrap_content"
Android:layout_alignParentRight="true"
Android:text="Unbind from Service" >
</Button>
</RelativeLayout>
<TextView
Android:id="@+id/textStatus"
Android:layout_width="fill_parent"
Android:layout_height="wrap_content"
Android:text="Status Goes Here"
Android:textSize="24sp" />
<TextView
Android:id="@+id/textIntValue"
Android:layout_width="fill_parent"
Android:layout_height="wrap_content"
Android:text="Integer Value Goes Here"
Android:textSize="24sp" />
<TextView
Android:id="@+id/textStrValue"
Android:layout_width="fill_parent"
Android:layout_height="wrap_content"
Android:text="String Value Goes Here"
Android:textSize="24sp" />
<RelativeLayout
Android:id="@+id/RelativeLayout03"
Android:layout_width="fill_parent"
Android:layout_height="wrap_content" >
<Button
Android:id="@+id/btnUpby1"
Android:layout_width="wrap_content"
Android:layout_height="wrap_content"
Android:text="Increment by 1" >
</Button>
<Button
Android:id="@+id/btnUpby10"
Android:layout_width="wrap_content"
Android:layout_height="wrap_content"
Android:layout_alignParentRight="true"
Android:text="Increment by 10" >
</Button>
</RelativeLayout>
src\com.exampleservice\MainActivity.Java:
package com.exampleservice;
import Android.app.Activity;
import Android.content.ComponentName;
import Android.content.Context;
import Android.content.Intent;
import Android.content.ServiceConnection;
import Android.os.Bundle;
import Android.os.Handler;
import Android.os.IBinder;
import Android.os.Message;
import Android.os.Messenger;
import Android.os.RemoteException;
import Android.util.Log;
import Android.view.View;
import Android.view.View.OnClickListener;
import Android.widget.Button;
import Android.widget.TextView;
public class MainActivity extends Activity {
Button btnStart, btnStop, btnBind, btnUnbind, btnUpby1, btnUpby10;
TextView textStatus, textIntValue, textStrValue;
Messenger mService = null;
boolean mIsBound;
final Messenger mMessenger = new Messenger(new IncomingHandler());
class IncomingHandler extends Handler {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MyService.MSG_SET_INT_VALUE:
textIntValue.setText("Int Message: " + msg.arg1);
break;
case MyService.MSG_SET_STRING_VALUE:
String str1 = msg.getData().getString("str1");
textStrValue.setText("Str Message: " + str1);
break;
default:
super.handleMessage(msg);
}
}
}
private ServiceConnection mConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder service) {
mService = new Messenger(service);
textStatus.setText("Attached.");
try {
Message msg = Message.obtain(null, MyService.MSG_REGISTER_CLIENT);
msg.replyTo = mMessenger;
mService.send(msg);
}
catch (RemoteException e) {
// In this case the service has crashed before we could even do anything with it
}
}
public void onServiceDisconnected(ComponentName className) {
// This is called when the connection with the service has been unexpectedly disconnected - process crashed.
mService = null;
textStatus.setText("Disconnected.");
}
};
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
btnStart = (Button)findViewById(R.id.btnStart);
btnStop = (Button)findViewById(R.id.btnStop);
btnBind = (Button)findViewById(R.id.btnBind);
btnUnbind = (Button)findViewById(R.id.btnUnbind);
textStatus = (TextView)findViewById(R.id.textStatus);
textIntValue = (TextView)findViewById(R.id.textIntValue);
textStrValue = (TextView)findViewById(R.id.textStrValue);
btnUpby1 = (Button)findViewById(R.id.btnUpby1);
btnUpby10 = (Button)findViewById(R.id.btnUpby10);
btnStart.setOnClickListener(btnStartListener);
btnStop.setOnClickListener(btnStopListener);
btnBind.setOnClickListener(btnBindListener);
btnUnbind.setOnClickListener(btnUnbindListener);
btnUpby1.setOnClickListener(btnUpby1Listener);
btnUpby10.setOnClickListener(btnUpby10Listener);
restoreMe(savedInstanceState);
CheckIfServiceIsRunning();
}
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putString("textStatus", textStatus.getText().toString());
outState.putString("textIntValue", textIntValue.getText().toString());
outState.putString("textStrValue", textStrValue.getText().toString());
}
private void restoreMe(Bundle state) {
if (state!=null) {
textStatus.setText(state.getString("textStatus"));
textIntValue.setText(state.getString("textIntValue"));
textStrValue.setText(state.getString("textStrValue"));
}
}
private void CheckIfServiceIsRunning() {
//If the service is running when the activity starts, we want to automatically bind to it.
if (MyService.isRunning()) {
doBindService();
}
}
private OnClickListener btnStartListener = new OnClickListener() {
public void onClick(View v){
startService(new Intent(MainActivity.this, MyService.class));
}
};
private OnClickListener btnStopListener = new OnClickListener() {
public void onClick(View v){
doUnbindService();
stopService(new Intent(MainActivity.this, MyService.class));
}
};
private OnClickListener btnBindListener = new OnClickListener() {
public void onClick(View v){
doBindService();
}
};
private OnClickListener btnUnbindListener = new OnClickListener() {
public void onClick(View v){
doUnbindService();
}
};
private OnClickListener btnUpby1Listener = new OnClickListener() {
public void onClick(View v){
sendMessageToService(1);
}
};
private OnClickListener btnUpby10Listener = new OnClickListener() {
public void onClick(View v){
sendMessageToService(10);
}
};
private void sendMessageToService(int intvaluetosend) {
if (mIsBound) {
if (mService != null) {
try {
Message msg = Message.obtain(null, MyService.MSG_SET_INT_VALUE, intvaluetosend, 0);
msg.replyTo = mMessenger;
mService.send(msg);
}
catch (RemoteException e) {
}
}
}
}
void doBindService() {
bindService(new Intent(this, MyService.class), mConnection, Context.BIND_AUTO_CREATE);
mIsBound = true;
textStatus.setText("Binding.");
}
void doUnbindService() {
if (mIsBound) {
// If we have received the service, and hence registered with it, then now is the time to unregister.
if (mService != null) {
try {
Message msg = Message.obtain(null, MyService.MSG_UNREGISTER_CLIENT);
msg.replyTo = mMessenger;
mService.send(msg);
}
catch (RemoteException e) {
// There is nothing special we need to do if the service has crashed.
}
}
// Detach our existing connection.
unbindService(mConnection);
mIsBound = false;
textStatus.setText("Unbinding.");
}
}
@Override
protected void onDestroy() {
super.onDestroy();
try {
doUnbindService();
}
catch (Throwable t) {
Log.e("MainActivity", "Failed to unbind from the service", t);
}
}
}
src\com.exampleservice\MyService.Java:
package com.exampleservice;
import Java.util.ArrayList;
import Java.util.Timer;
import Java.util.TimerTask;
import Android.app.Notification;
import Android.app.NotificationManager;
import Android.app.PendingIntent;
import Android.app.Service;
import Android.content.Intent;
import Android.os.Bundle;
import Android.os.Handler;
import Android.os.IBinder;
import Android.os.Message;
import Android.os.Messenger;
import Android.os.RemoteException;
import Android.util.Log;
public class MyService extends Service {
private NotificationManager nm;
private Timer timer = new Timer();
private int counter = 0, incrementby = 1;
private static boolean isRunning = false;
ArrayList<Messenger> mClients = new ArrayList<Messenger>(); // Keeps track of all current registered clients.
int mValue = 0; // Holds last value set by a client.
static final int MSG_REGISTER_CLIENT = 1;
static final int MSG_UNREGISTER_CLIENT = 2;
static final int MSG_SET_INT_VALUE = 3;
static final int MSG_SET_STRING_VALUE = 4;
final Messenger mMessenger = new Messenger(new IncomingHandler()); // Target we publish for clients to send messages to IncomingHandler.
@Override
public IBinder onBind(Intent intent) {
return mMessenger.getBinder();
}
class IncomingHandler extends Handler { // Handler of incoming messages from clients.
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_REGISTER_CLIENT:
mClients.add(msg.replyTo);
break;
case MSG_UNREGISTER_CLIENT:
mClients.remove(msg.replyTo);
break;
case MSG_SET_INT_VALUE:
incrementby = msg.arg1;
break;
default:
super.handleMessage(msg);
}
}
}
private void sendMessageToUI(int intvaluetosend) {
for (int i=mClients.size()-1; i>=0; i--) {
try {
// Send data as an Integer
mClients.get(i).send(Message.obtain(null, MSG_SET_INT_VALUE, intvaluetosend, 0));
//Send data as a String
Bundle b = new Bundle();
b.putString("str1", "ab" + intvaluetosend + "cd");
Message msg = Message.obtain(null, MSG_SET_STRING_VALUE);
msg.setData(b);
mClients.get(i).send(msg);
}
catch (RemoteException e) {
// The client is dead. Remove it from the list; we are going through the list from back to front so this is safe to do inside the loop.
mClients.remove(i);
}
}
}
@Override
public void onCreate() {
super.onCreate();
Log.i("MyService", "Service Started.");
showNotification();
timer.scheduleAtFixedRate(new TimerTask(){ public void run() {onTimerTick();}}, 0, 100L);
isRunning = true;
}
private void showNotification() {
nm = (NotificationManager)getSystemService(NOTIFICATION_SERVICE);
// In this sample, we'll use the same text for the ticker and the expanded notification
CharSequence text = getText(R.string.service_started);
// Set the icon, scrolling text and timestamp
Notification notification = new Notification(R.drawable.icon, text, System.currentTimeMillis());
// The PendingIntent to launch our activity if the user selects this notification
PendingIntent contentIntent = PendingIntent.getActivity(this, 0, new Intent(this, MainActivity.class), 0);
// Set the info for the views that show in the notification panel.
notification.setLatestEventInfo(this, getText(R.string.service_label), text, contentIntent);
// Send the notification.
// We use a layout id because it is a unique number. We use it later to cancel.
nm.notify(R.string.service_started, notification);
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.i("MyService", "Received start id " + startId + ": " + intent);
return START_STICKY; // run until explicitly stopped.
}
public static boolean isRunning()
{
return isRunning;
}
private void onTimerTick() {
Log.i("TimerTick", "Timer doing work." + counter);
try {
counter += incrementby;
sendMessageToUI(counter);
}
catch (Throwable t) { //you should always ultimately catch all exceptions in timer tasks.
Log.e("TimerTick", "Timer Tick Failed.", t);
}
}
@Override
public void onDestroy() {
super.onDestroy();
if (timer != null) {timer.cancel();}
counter=0;
nm.cancel(R.string.service_started); // Cancel the persistent notification.
Log.i("MyService", "Service Stopped.");
isRunning = false;
}
}
Regardez l'exemple LocalService .
Votre Service
renvoie une instance de lui-même aux consommateurs qui appellent onBind
. Vous pouvez ensuite interagir directement avec le service, par exemple enregistrer votre propre interface d'écoute avec le service, afin que vous puissiez obtenir des rappels.
Pour envoyer des données à un service, vous pouvez utiliser:
Intent intent = new Intent(getApplicationContext(), YourService.class);
intent.putExtra("SomeData","ItValue");
startService(intent);
Et après la mise en service dans onStartCommand (), récupère les données de l'intention.
Pour envoyer des données ou des événements d'un service à une application (pour une ou plusieurs activités):
private void sendBroadcastMessage(String intentFilterName, int arg1, String extraKey) {
Intent intent = new Intent(intentFilterName);
if (arg1 != -1 && extraKey != null) {
intent.putExtra(extraKey, arg1);
}
sendBroadcast(intent);
}
Cette méthode appelle depuis votre service. Vous pouvez simplement envoyer des données pour votre activité.
private void someTaskInYourService(){
//For example you downloading from server 1000 files
for(int i = 0; i < 1000; i++) {
Thread.sleep(5000) // 5 seconds. Catch in try-catch block
sendBroadCastMessage(Events.UPDATE_DOWNLOADING_PROGRESSBAR, i,0,"up_download_progress");
}
Pour recevoir un événement avec des données, créez et enregistrez la méthode registerBroadcastReceivers () dans votre activité:
private void registerBroadcastReceivers(){
broadcastReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
int arg1 = intent.getIntExtra("up_download_progress",0);
progressBar.setProgress(arg1);
}
};
IntentFilter progressfilter = new IntentFilter(Events.UPDATE_DOWNLOADING_PROGRESS);
registerReceiver(broadcastReceiver,progressfilter);
Pour envoyer plus de données, vous pouvez modifier la méthode sendBroadcastMessage();
. Rappelez-vous: vous devez enregistrer les émissions dans les méthodes onResume () et annuler l’enregistrement dans les méthodes onStop ()!
METTRE &AGRAVE; JOUR
Veuillez ne pas utiliser mon type de communication entre Activité et Service . Ceci est le mauvais sens . Pour une meilleure expérience, veuillez utiliser des bibliothèques spéciales, telles que:
1) EventBus de greenrobot
2) Otto de Square Inc
P.S. J'utilise uniquement EventBus de Greenrobot dans mes projets,
Remarque: vous n'avez pas besoin de vérifier si votre service est en cours d'exécution, CheckIfServiceIsRunning()
, car bindService()
le démarrera s'il ne fonctionne pas.
De plus, si vous faites pivoter le téléphone, vous ne voulez pas qu'il soit à nouveau bindService()
, car onCreate()
sera rappelé. Assurez-vous de définir onConfigurationChanged()
pour éviter cela.
Tout va bien. Bon exemple de communication activity/service
avec Messenger .
Un commentaire: la méthode MyService.isRunning()
n'est pas obligatoire .. bindService()
peut être effectué autant de fois que nécessaire. pas de mal à cela.
Si MyService est exécuté dans un processus différent, la fonction statique MyService.isRunning()
renverra toujours false. Donc, il n'y a pas besoin de cette fonction.
Message msg = Message.obtain(null, 2, 0, 0);
Bundle bundle = new Bundle();
bundle.putString("url", url);
bundle.putString("names", names);
bundle.putString("captions",captions);
msg.setData(bundle);
Donc, vous l'envoyez au service. Ensuite recevoir.
C’est ainsi que j’ai mis en œuvre Activité-> Communication de service:
private static class MyResultReciever extends ResultReceiver {
/**
* Create a new ResultReceive to receive results. Your
* {@link #onReceiveResult} method will be called from the thread running
* <var>handler</var> if given, or from an arbitrary thread if null.
*
* @param handler
*/
public MyResultReciever(Handler handler) {
super(handler);
}
@Override
protected void onReceiveResult(int resultCode, Bundle resultData) {
if (resultCode == 100) {
//dostuff
}
}
Et puis j'ai utilisé cela pour démarrer mon service
protected void onCreate(Bundle savedInstanceState) {
MyResultReciever resultReciever = new MyResultReciever(handler);
service = new Intent(this, MyService.class);
service.putExtra("receiver", resultReciever);
startService(service);
}
Dans mon service j'avais
public int onStartCommand(Intent intent, int flags, int startId) {
if (intent != null)
resultReceiver = intent.getParcelableExtra("receiver");
return Service.START_STICKY;
}
J'espère que cela t'aides
Super tutoriel, présentation fantastique. Propre, simple, court et très explicatif . Bien que, la méthode notification.setLatestEventInfo(this, getText(R.string.service_label), text, contentIntent);
ne soit plus. Comme le dit trante ici , une bonne approche serait:
private static final int NOTIFICATION_ID = 45349;
private void showNotification() {
NotificationCompat.Builder builder =
new NotificationCompat.Builder(this)
.setSmallIcon(R.mipmap.ic_launcher)
.setContentTitle("My Notification Title")
.setContentText("Something interesting happened");
Intent targetIntent = new Intent(this, MainActivity.class);
PendingIntent contentIntent = PendingIntent.getActivity(this, 0, targetIntent, PendingIntent.FLAG_UPDATE_CURRENT);
builder.setContentIntent(contentIntent);
_nManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
_nManager.notify(NOTIFICATION_ID, builder.build());
}
@Override
public void onDestroy() {
super.onDestroy();
if (_timer != null) {_timer.cancel();}
_counter=0;
_nManager.cancel(NOTIFICATION_ID); // Cancel the persistent notification.
Log.i("PlaybackService", "Service Stopped.");
_isRunning = false;
}
Vérifié moi-même, tout fonctionne comme un charme (les noms d'activité et de service peuvent différer de l'original).
Il me semble que vous auriez pu économiser de la mémoire en déclarant votre activité avec "implémente Handler.Callback"