活动与服务之间的沟通

时间:2013-12-15 13:14:43

标签: android android-activity android-service message-passing

我正在尝试为Android创建自己的MusicPlayer。我遇到问题的地方是在后台运行一些东西。主要活动管理GUI,到目前为止所有的歌曲都在播放。我想分开GUI和音乐播放课程。我想把音乐管理部分放在服务中,并留下现在的其他东西。

我的问题是我无法组织活动和服务之间的通信,因为它们之间正在进行大量的通信,包括双向移动对象。我尝试了很多技术,我在这里搜索Stack Overflow但每次遇到问题时都会这样做。我需要Service才能将对象发送到Activity,反之亦然。当我添加小部件时,我也希望它能够与服务进行通信。

任何提示都值得赞赏,如果你需要源代码发表评论,但现在在这个转变中它变得混乱。

是否有更高级的教程,而不是调用一个从服务中返回随机数的方法? :P

编辑:可能的解决方案是使用RoboGuice库并使用注入移动对象

7 个答案:

答案 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);
    }
}
  • 如果我们不注销BroadcastReceiver,我们就会出错,你需要在活动开始时取消注册onPause,onStop,onDestroy ......
  • 如果你在回到活动时没有注册BroadcastReceiver,它将不会从服务中收听任何内容......服务会向BroadcastReceiver发送信息,但由于它没有注册,它将不会收到任何信息
  • 当您创建多个服务时,以下服务将从onStartCommand开始。
  • 您可以通过意图将信息传递给服务,然后通过onStartCommand
  • 获取
  • returnonStartCommand的差异: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());