android-activity - 如何使用消息进行Activity和Service 之间的通信?

  显示原文与译文双语对照的内容

我找不到如何在 Activity 和服务之间发送消息的示例,我花费了太多的时间来解决这个问题。 下面是一个供其他人参考的示例项目。

这里示例允许你直接启动或者停止服务,并分别绑定/取消绑定服务。 当服务运行时,它在 10赫兹处增加一个数字。 如果 Activity 绑定到服务,它将显示当前值。 数据以整数和字符串形式传输,因此你可以看到如何执行两种不同的方式。 Activity 中也有按钮向服务( 更改increment-by值) 发送消息。

屏幕截图:

Screenshot of Android service messaging example

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>

resvaluesstrings 。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>

reslayoutmain 。xml:


<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
 android:orientation="vertical"
 android:layout_width="fill_parent"
 android:layout_height="fill_parent"
> 

<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:text="Stop Service" android:layout_alignParentRight="true"></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:text="Unbind from Service" android:layout_alignParentRight="true"></Button>
</RelativeLayout>

<TextView android:id="@+id/textStatus" android:textSize="24sp" android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="Status Goes Here"/>
<TextView android:id="@+id/textIntValue" android:textSize="24sp" android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="Integer Value Goes Here"/>
<TextView android:id="@+id/textStrValue" android:textSize="24sp" android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="String Value Goes Here"/>

<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:text="Increment by 10" android:layout_alignParentRight="true"></Button>
</RelativeLayout>

</LinearLayout>

srccom 。exampleservicemainactivity 。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);
 }
 }
}

srccom 。exampleservicemyservice 。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;
 }
}

时间:

查看 LocalService示例

你的Service 将自己的实例返回给调用 onBind的消费者。 然后你可以直接与服务交互,比如 注册你自己的侦听器接口与服务,这样你就可以得到回调。

注意:你不需要检查你的服务是否正在运行,CheckIfServiceIsRunning(),因为 bindService() 将在没有运行时启动它。

同样:如果你旋转电话,你将不再需要 bindService(),因为 onCreate() 将被再次调用。 确保定义 onConfigurationChanged() 以防止这里操作。

要将数据发送到你可以使用的服务,请执行以下操作:


Intent intent = new Intent(getApplicationContext(), YourService.class);
intent.putExtra("SomeData","ItValue");
startService(intent);

在 onStartCommand() 中的服务之后从 Intent 获取数据。

要将数据或者事件从服务发送到应用程序( 用于一个或者多个活动):


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

这里方法正在从你的服务调用。 你可以简单地为你的Activity 发送数据。


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

在 Activity 方法 registerBroadcastReceivers() 中,为接收数据,创建并注册事件:


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

要发送更多数据,你可以修改方法 sendBroadcastMessage(); 。 记住:你必须在 onResume() &中注册广播在 onStop() 方法中注销 !


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

所以你把它发送给服务。 之后接收。

一切都不错。Activity/服务通信使用信使的好例子。 一个注释:不需要方法 MyService.isRunning() 。 bindService() 可以执行任意次数。 那也无妨。

如果MyService在另一个进程中运行,那么静态函数 MyService.isRunning() 总是 return false 。 所以不需要这个函数。

"实现 Handler.Callback",似乎对于我来说你应该留部分内存空间,通过声明 Activity

...