示例:使用消息传递的活动和服务之间的通信
我找不到任何有关如何在活动和服务之间发送消息的例子,我花了太多的时间来解决这个问题。 这是一个供他人参考的示例项目。
此示例允许您直接启动或停止服务,并单独绑定/取消绑定服务。 当服务正在运行时,它以10Hz递增一个数字。 如果活动绑定到Service
,它将显示当前值。 数据以Integerforms传输,并以stringforms传输,这样您就可以看到如何做到这两种不同的方式。 活动中还有button将消息发送到服务(更改增量值)。
截图:
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>
水库\值\ 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>
水库\布局\ 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; } }
看看LocalService的例子 。
您的Service
将自己的一个实例返回给调用onBind
消费者。 然后,您可以直接与服务交互,例如向服务注册您自己的监听器接口,以便获得callback。
要将数据发送到服务,您可以使用:
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); }
此方法正在从您的服务调用。 您可以简单地为您的活动发送数据。
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"); }
为了接收数据事件,请在您的活动中创build并注册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();
方法sendBroadcastMessage();
。 请记住:您必须在onResume()和注销onStop()方法中注册广播!
UPDATE
请不要在活动与服务之间使用我的交stream方式。 这是错误的方式。 为了更好的体验,请使用特殊的库,如我们:
1)来自greenrobot的EventBus
2) Otto来自Square Inc
PS我只在我的项目中使用greenrobot的EventBus,
注意:你不需要检查你的服务是否正在运行, CheckIfServiceIsRunning()
,因为bindService()
会在没有运行时启动它。
另外:如果你旋转手机,你不希望它再次bindService()
,因为onCreate()
将被再次调用。 一定要定义onConfigurationChanged()
来防止这种情况。
一切都很好。使用Messenger的activity/service
交stream的好例子。
一个注释:方法MyService.isRunning()
不是必需的bindService()
可以做任何次数。 没有伤害的。
如果MyService在不同的进程中运行,则静态函数MyService.isRunning()
将始终返回false。 所以不需要这个function。
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);
所以你把它发送到服务。 之后收到。
这是我如何实施活动 – >服务沟通:在我的活动上
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; }
希望这可以帮助
在我看来,你可以通过“implements Handler.Callback”声明你的活动来保存一些内存。
伟大的教程,精彩的演示。 整洁,简单,简短,非常具有说服力。 虽然, notification.setLatestEventInfo(this, getText(R.string.service_label), text, contentIntent);
方法不在了。 正如这里所说,好的方法是:
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; }
检查自己,一切都像一个魅力(活动和服务名称可能不同于原来的)。