2010-11-29 75 views
547

我找不到任何有关如何在活动和服务之间发送消息的示例,并且我花费了太多时间来弄清楚这一点。这是一个供他人参考的示例项目。示例:使用消息传送的活动和服务之间的通信

本示例允许您直接启动或停止服务,并单独绑定/取消绑定服务。当服务正在运行时,它将以10   Hz的增量递增一个数字。如果活动绑定到Service,它将显示当前值。数据以Integer形式和String形式传输,因此您可以看到如何以两种不同的方式进行操作。活动中还有按钮将消息发送到服务(更改增量值)。

截图:

Screenshot of Android service messaging example

的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 \值\ 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 \布局\ 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.examplese起动转矩\ 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; 
    } 
} 
+51

很好的例子!另一个不错的特性:如果你在你的manifest.xml中把`android:process =:myservicename`属性放到你的服务的`service`标记中,例如:``,那么它将作为一个不同的进程运行你的服务 - 因此在不同的线程中。这意味着,服务的任何繁重的计算完成/长时间请求不会挂起您的UI线程。 – sydd 2011-12-02 21:01:39

+0

你可以发布你从哪里得到这个例子代码的链接..? – 2012-04-10 10:45:04

+26

我知道你已经付出了努力来做到这一点,但将它放在github或类似的源代码共享站点并在此处发布链接会更有意义。人们以这种方式启动并运行起来会更容易。 – 2012-05-08 15:24:30

回答

43

看那LocalService example

您的Service将自己的实例返回给致电onBind的消费者。然后你可以直接与服务交互,例如注册您自己的服务监听器接口,以便您可以获得回调。

14

注意:您不需要检查您的服务是否正在运行,CheckIfServiceIsRunning(),因为bindService()将在未运行时启动它。

另外:如果你旋转手机,你不希望它再次bindService(),因为onCreate()将被再次调用。请务必将定义为防止出现这种情况。

8

一切都很好。activity/service沟通使用的好例子Messenger

一个评论:方法MyService.isRunning()不是必需的。bindService()可以做任何次数。没有伤害。

如果MyService在不同的进程中运行,那么静态函数MyService.isRunning()将始终返回false。所以不需要这个功能。

0

在我看来,你可以通过声明你的活动已经保存了一些内存“实现Handler.Callback”

7
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); 

所以你把它发送给服务。之后收到。

18

将数据发送到你可以使用一个服务:从意向

Intent intent = new Intent(getApplicationContext(), YourService.class); 
intent.putExtra("SomeData","ItValue"); 
startService(intent); 

而且在onStartCommand后服务()获取数据。

从服务发送的数据或事件的应用程序(一个或多个活动):

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"); 
    } 

用于接收与数据的事件,在您的活动创建和注册方法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()方法中注册广播!

UPDATE

请不要用我的活动之间的通信类型&服务。 这是错误的方法。 为了更好的体验,请使用专门的库,例如我们:

1)EventBus从greenrobot

2)Otto从广场公司

附:我只使用EventBus从我的项目greenrobot,

2

我这是怎么Implemeted一个活动 - >服务通讯: 我的活动我有

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; 
} 

希望这帮助

0

伟大的教程,梦幻般的演示文稿。整洁,简单,简短,非常具有说服力。 虽然,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; 
} 

控制住了自己,一切就像一个魅力(活动和服务名称可能从原来的不同)。