活动与服务之间的沟通
我正在尝试为Android制作自己的MusicPlayer。 我遇到问题的地方是在后台运行一些东西。 主要活动pipe理GUI,直到现在所有的歌曲都在播放。 我想分开GUI和音乐播放课程。 我想把音乐pipe理部分放在服务中,并保留现在的其他东西。
我的问题是我无法组织Activity和Service之间的通信,因为它们之间发生了很多的通信,包括两个方向的移动对象。 我尝试了很多技术,我在这里searchStack Overflow,但每次遇到问题。 我需要服务能够发送对象到活动,反之亦然。 当我添加小部件时,我也希望它能够与服务进行通信。
任何提示都赞赏,如果你需要源代码的地方评论下面,但现在在这个过渡变得混乱。
有没有更多的高级教程比调用一个从服务返回随机数的方法? :P
编辑:可能的解决scheme是使用RoboGuice库和注射移动对象
我已经使用绑定和callback接口实现了活动和服务之间的通信。
为了将数据发送到服务,我使用了Binder,将服务实例重新join到Activity中,然后Activity可以访问Service中的公共方法。
为了将数据发送回服务中的Activity,我使用了当您想要在Fragment和Activity之间进行通信时使用的Callbacks界面。
以下是每个代码示例:以下示例显示了“活动和服务”双向关系:活动有两个button:第一个button将启动和停止服务。 第二个button将启动在服务中运行的计时器。
该服务将通过定时器进程通过callback来更新活动。
我的活动:
//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)); } }
这里是服务:
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); } }
更新:2016年7月10日
国际海事组织我认为使用BroadcastReceiver自定义事件是更好的方式,因为提到的信使不处理设备旋转活动娱乐以及可能的内存泄漏。
您可以为活动中的事件创build自定义的BroadCast Receiver,然后您也可以使用Messengers。
-
在你的
Activity
创build一个MessageHandler类
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; } } }
现在你可以拥有它的实例
public static Handler messageHandler = new MessageHandler();
用这个Handler对象作为一个额外的数据来启动你的
Service
Intent startService = new Intent(context, SERVICE.class) startService.putExtra("MESSENGER", new Messenger(messageHandler)); context.startService(startService);
-
在您的
Service
您从意图中收到此对象,并将服务中的Messenger
variables初始化为private Messenger messageHandler; Bundle extras = intent.getExtras(); messageHandler = (Messenger) extras.get("MESSENGER"); sendMessage(ProgressBarState.SHOW);
然后写一个
sendMessage
方法来发送消息给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(); } }
上面的示例代码显示并隐藏Activity中的ProgressBar,因为从Service接收到消息。
意图是Activitiy和Service之间沟通的良好解决scheme。
在您的服务中接收意图的快速解决scheme是inheritanceIntentService类。 它使用队列和工作线程处理以Intents表示的asynchronous请求。
对于从服务到Activity的通信,您可以广播意图,而不是从上下文中使用正常的sendBroadcast(),更有效的方法是使用支持库中的LocalBroadcastManager 。
示例服务。
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); } }
示例活动
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); } }
我认为正确的答案是有问题的。 我没有足够的声望评论它。
正确的答案:活动调用bindService()获取指向服务是好的。 因为维护连接时维护服务上下文。
在回答错误:服务指针的Activity类的callback是不好的方法。 活动上下文正在释放=>exception在这里,活动实例可能不为空。
在答案中解决了错误:服务发送意图到Activity。 和通过BroadcastReceiver的活动接收者意图。
注意:在这种情况下,Service和Activity在同一个Process中,应该使用LocalBroadcastManager发送intent。 它使性能和安全性更好
在这种情况下,最好的办法是通过从您的服务进行广播来进行不同的操作,并在您的活动中接收。 您可以创build一个自定义的广播,并发送一些代码来定义特定事件,如完成,更改,准备等。
这是活动与服务之间沟通的一个简单例子
活动
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); } }
- 如果我们不取消注册BroadcastReceiver,我们将有一个错误,你需要取消注册时,活动去onPause,onStop,onDestroy …
- 如果您在返回活动时未注册BroadcastReceiver,则不会从服务中听取任何内容…服务将向BroadcastReceiver发送信息,但因为未注册,所以不会收到任何内容。
- 当您创build多个服务时,以下服务将在
onStartCommand
开始。 - 您可以将信息传递给服务意图,并在
onStartCommand
中onStartCommand
-
onStartCommand
return
的onStartCommand
: START_STICKY和START_REDELIVER_INTENT之间的区别? 并检查谷歌的官方网站: 服务
非常简单但function强大的方法是使用EventBus,您可以将其添加到您的Gradle版本,并享受简单的发布者/订阅者模式。