示例:使用消息传递在 Activity 和 Service 之间进行通信
Posted
技术标签:
【中文标题】示例:使用消息传递在 Activity 和 Service 之间进行通信【英文标题】:Example: Communication between Activity and Service using Messaging 【发布时间】:2011-05-17 01:31:32 【问题描述】:我找不到任何关于如何在活动和服务之间发送消息的示例,而且我花了太多时间来解决这个问题。这是一个示例项目供其他人参考。
此示例允许您直接启动或停止服务,并分别与服务绑定/取消绑定。当服务运行时,它会以 10 Hz 的频率递增一个数字。如果活动绑定到Service
,它将显示当前值。数据以整数和字符串的形式传输,因此您可以了解如何以两种不同的方式进行传输。 Activity 中还有一些按钮可以向服务发送消息(更改增量值)。
截图:
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_
android:layout_ >
<Button
android:id="@+id/btnStart"
android:layout_
android:layout_
android:text="Start Service" >
</Button>
<Button
android:id="@+id/btnStop"
android:layout_
android:layout_
android:layout_alignParentRight="true"
android:text="Stop Service" >
</Button>
</RelativeLayout>
<RelativeLayout
android:id="@+id/RelativeLayout02"
android:layout_
android:layout_ >
<Button
android:id="@+id/btnBind"
android:layout_
android:layout_
android:text="Bind to Service" >
</Button>
<Button
android:id="@+id/btnUnbind"
android:layout_
android:layout_
android:layout_alignParentRight="true"
android:text="Unbind from Service" >
</Button>
</RelativeLayout>
<TextView
android:id="@+id/textStatus"
android:layout_
android:layout_
android:text="Status Goes Here"
android:textSize="24sp" />
<TextView
android:id="@+id/textIntValue"
android:layout_
android:layout_
android:text="Integer Value Goes Here"
android:textSize="24sp" />
<TextView
android:id="@+id/textStrValue"
android:layout_
android:layout_
android:text="String Value Goes Here"
android:textSize="24sp" />
<RelativeLayout
android:id="@+id/RelativeLayout03"
android:layout_
android:layout_ >
<Button
android:id="@+id/btnUpby1"
android:layout_
android:layout_
android:text="Increment by 1" >
</Button>
<Button
android:id="@+id/btnUpby10"
android:layout_
android:layout_
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;
【问题讨论】:
很好的例子!另一个不错的功能:如果您在 manifest.xml 中将android:process=:myservicename
属性添加到服务的service
标记中,例如:<service android:name="sname" android:process=":myservicename" />
,那么它将作为不同的进程运行您的服务 - 因此在不同的线程中。这意味着,服务完成的任何繁重计算/长时间请求都不会挂起您的 UI 线程。
我知道您为此付出了努力,但将其放在 github 或类似的源代码共享网站上并在此处发布链接会更有意义。人们更容易以这种方式启动和运行它。
我不认为你关于并发修改的假设是准确的。
只有当您的服务可以被其他应用程序调用时,才真正需要消息传递。否则,您可能会坚持使用 Binder,它会返回对 Service 的引用并调用它的公共方法。
您应该先提出问题,然后自己创建答案,而不是在问题上回答问题。不过很好的例子;)
【参考方案1】:
看看LocalService example。
您的Service
将自己的一个实例返回给调用onBind
的消费者。然后您可以直接与服务交互,例如向服务注册自己的监听器接口,以便获得回调。
【讨论】:
唯一的问题是它不会使用 Messenger,所以它不会回答这个伪问题。我使用了 LocalService,但我很高兴找到一个 Messenger/Handler 的示例。我不相信 LocalService 可以放在另一个进程中。 @Christoper-Orr:非常感谢您发布了Android BroadcastReceiver
教程的链接。我使用LocalBroadcastManager
在两个Activity
实例之间不断交换数据。
LocalBroadcastManager
的问题在于它是非阻塞的,你必须等待结果。有时您想要立竿见影的效果。
你能帮我解决这个问题吗***.com/questions/51508046/…【参考方案2】:
要向您可以使用的服务发送数据:
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");
要接收带有数据的事件,请在您的活动中创建并注册方法 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() 方法中取消注册!
更新
请不要在活动和服务之间使用我的通信类型。 这是错误的方式。 为了获得更好的体验,请使用特殊库,例如我们:
1) EventBus 来自 greenrobot
2) Otto 来自 Square Inc
附:我只在我的项目中使用来自 greenrobot 的 EventBus,
【讨论】:
我应该在哪里注册 onResume 和 onStop 中的接收者,我可能需要在活动中做什么? 是的。要从服务接收事件,您必须在 onResume 中注册广播。请记住,您必须在 onStop 中取消注册广播。现在,我不建议使用我的方法。请使用特殊库与其他视图/活动/服务进行通信,例如 EventBus github.com/greenrobot/EventBus 或 Otto github.com/square/otto 你能帮我如何使用这个我在服务通信中卡在我的项目中 Google 是否建议不要这样做,或者您只是说它“错误”是因为您认为其他解决方案更好? 加一个用于提供指向EventBus
和Otto
的链接。【参考方案3】:
注意:你不需要检查你的服务是否正在运行,CheckIfServiceIsRunning()
,因为如果它没有运行,bindService()
会启动它。
另外:如果您旋转手机,您不希望它再次拨到bindService()
,因为onCreate()
将再次被呼叫。请务必定义 onConfigurationChanged()
以防止这种情况发生。
【讨论】:
就我而言,我不需要一直运行该服务。如果活动开始时服务已经在运行,那么我想绑定到它。如果活动开始时服务没有运行,我想停止服务。 我不确定这是不是真的,bindService 没有启动服务,你能指点文档吗? developer.android.com/reference/android/app/Service.html第一段声明Services can be started with Context.startService() and Context.bindService()
【参考方案4】:
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);
所以你将它发送到服务。之后收到。
【讨论】:
【参考方案5】:一切都很好。activity/service
使用 Messenger 通信的好例子。
一条评论:方法MyService.isRunning()
不是必需的。bindService()
可以执行任意次数。没有坏处。
如果 MyService 在不同的进程中运行,则静态函数 MyService.isRunning()
将始终返回 false。所以不需要这个函数。
【讨论】:
【参考方案6】:这就是我实现 Activity->Service Communication 的方式: 在我的活动中,我有
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
然后我用它来启动我的服务
protected void onCreate(Bundle savedInstanceState)
MyResultReciever resultReciever = new MyResultReciever(handler);
service = new Intent(this, MyService.class);
service.putExtra("receiver", resultReciever);
startService(service);
在我的服务中
public int onStartCommand(Intent intent, int flags, int startId)
if (intent != null)
resultReceiver = intent.getParcelableExtra("receiver");
return Service.START_STICKY;
希望对你有帮助
【讨论】:
【参考方案7】:在我看来,您可以通过使用“实现 Handler.Callback”声明您的活动来节省一些内存
【讨论】:
【参考方案8】:很棒的教程,精彩的演示。整洁,简单,简短且非常易于解释。
虽然,notification.setLatestEventInfo(this, getText(R.string.service_label), text, contentIntent);
方法已不复存在。正如 trante 所说的here,好的方法是:
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;
检查自己,一切都像魅力一样(活动和服务名称可能与原始名称不同)。
【讨论】:
【参考方案9】:我已经看到了所有的答案。我想告诉现在一天最强大的方式。这将使您在Activity - Service - Dialog - Fragments
(Everything) 之间进行通信。
EventBus
我在我的项目中使用的这个库具有与消息传递相关的强大功能。
EventBus 分 3 步
定义事件:
public static class MessageEvent /* Additional fields if needed */
准备订阅者:
声明并注释您的订阅方法,可选择指定thread mode:
@Subscribe(threadMode = ThreadMode.MAIN)
public void onMessageEvent(MessageEvent event) /* Do something */;
注册和注销您的订阅者。例如在 Android 上,Activity 和 Fragment 通常应该根据它们的生命周期进行注册:
@Override
public void onStart()
super.onStart();
EventBus.getDefault().register(this);
@Override
public void onStop()
super.onStop();
EventBus.getDefault().unregister(this);
发布活动:
EventBus.getDefault().post(new MessageEvent());
只需在您的应用级别 gradle 中添加此依赖项
compile 'org.greenrobot:eventbus:3.1.1'
【讨论】:
它没有IPC,所以我们需要双向的信使。 AIDL 已弃用以上是关于示例:使用消息传递在 Activity 和 Service 之间进行通信的主要内容,如果未能解决你的问题,请参考以下文章
如何从 BroadcastReceiver 在 Activity 中发送和保存字符串消息
11) 十分钟学会android--Intent消息处理与传递详解