我正在尝试为Android创建自己的MusicPlayer。我遇到问题的地方是在后台运行一些东西。主要活动管理GUI,到目前为止所有的歌曲都在播放。我想分开GUI和音乐播放课程。我想把音乐管理部分放在服务中,并留下现在的其他东西。
我的问题是我无法组织活动和服务之间的通信,因为它们之间正在进行大量的通信,包括双向移动对象。我尝试了很多技术,我在这里搜索Stack Overflow但每次遇到问题时都会这样做。我需要Service才能将对象发送到Activity,反之亦然。当我添加小部件时,我也希望它能够与服务进行通信。
任何提示都值得赞赏,如果你需要源代码发表评论,但现在在这个转变中它变得混乱。
是否有更高级的教程,而不是调用一个从服务中返回随机数的方法? :P
编辑:可能的解决方案是使用RoboGuice库并使用注入移动对象
答案 0 :(得分:69)
我已使用Bind和Callbacks接口在Activity和Service之间实现了通信。
为了向服务发送数据,我使用了Binder将服务实例重新转换为Activity,然后Activity可以访问服务中的公共方法。
为了从服务中将数据发送回Activity,我使用了Callbacks界面,就像你想要在Fragment和Activity之间进行通信一样。
以下是每个代码示例: 以下示例显示了Activity和Service双向关系: 活动有2个按钮: 第一个按钮将启动和停止服务。 第二个按钮将启动在服务中运行的计时器。
该服务将通过回调更新Activity以及计时器进度。
我的活动:
//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);
}
}
答案 1 :(得分:15)
意图是Activitiy和Service之间通信的良好解决方案。
服务中接收意图的快速解决方案是继承IntentService类。它使用队列和工作线程处理表示为Intents的异步请求。
对于从服务到活动的通信,您可以广播意图,但不是使用Context中的普通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);
}
}
答案 2 :(得分:5)
我认为正确答案存在问题。我没有足够的声誉来评论它。
答案正确: 活动调用bindService()来获取指向Service的指针是可以的。因为维持连接时维护服务上下文。
答案错误: 服务指向Activity类的回调是不好的方法。在活动上下文正在发布时,活动实例可能不为null =&gt;例外。
解答错误的答案: 服务向Activity发送意图。和通过BroadcastReceiver的活动接收者意图。
注意:强> 在这种情况下,Service和Activity在同一个Process中,你应该使用LocalBroadcastManager来发送intent。它使性能和安全性更好
答案 3 :(得分:2)
这是活动和服务之间通信的简单示例
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);
}
}
onStartCommand
开始。 onStartCommand
return
中onStartCommand
的差异:Difference between START_STICKY and START_REDELIVER_INTENT?并查看Google官方网站:Services 答案 4 :(得分:0)
答案 5 :(得分:0)
非常简单但功能强大的方法是使用 EventBus ,您可以将其添加到您的gradle版本中,并享受简单的发布商/订阅者模式。
答案 6 :(得分:0)
最简单有效的方法是使用GreenRobot的EventBus。
使用简单的3个步骤:
1定义事件
public static class MessageEvent { /* Additional fields if needed */ }
2准备订阅者:声明并注释您的订阅方法,可选择指定主题模式:
@Subscribe(threadMode = ThreadMode.MAIN)
public void onMessageEvent(MessageEvent event) {/* Do something */};
注册并取消注册您的订阅者。例如,在Android上,活动和片段通常应根据其生命周期进行注册:
@Override
public void onStart() {
super.onStart();
EventBus.getDefault().register(this);
}
@Override
public void onStop() {
super.onStop();
EventBus.getDefault().unregister(this);
}
3发布活动:
EventBus.getDefault().post(new MessageEvent());