Android Service绑定与跨进程通信

来源:互联网 发布:淘宝上好吃的零食推荐 编辑:程序博客网 时间:2024/05/01 06:37

为什么要绑定Service?

Android的在服务器-客户端模式大量使用了Service绑定。绑定Service的好处就是你可以向Service发送、接收请求,甚至可以跨进程通信(IPC)。被绑定的服务通常是为了其他进程服务,而不是正常情况下的后台处理事件。

绑定Service的办法

在客户端中使用bindService (Intent service, ServiceConnection conn, int flags)
其中几个参数分别意义为:

Intent: 指向绑定Service的Intent。若是在其他进程中的Service,需要调用Intent.setComponent(new ComponentName(String packageName, String className))来指定Service。

ServiceConnection:提供绑定连接的接口,有两个方法必须实现:

  1. onServiceConnected(ComponentName name, IBinder service)

    在连接建立时被调用的函数,参数意义如下:

    • name 被绑定服务的具体名,格式:”ComponentInfo{app_package/service_package.service_class}”。

    • service 服务的onBind方法所返回的IBinder,可以用来与服务通信。

  2. onServiceDisconnected(ComponentName name)

    在连接断开时被调用的函数,参数name与onServiceConnected中含义相同。

flags: Context类下的常量,具体意义如下表

常量名 值 含义 BIND_ABOVE_CLIENT 8 如果当绑定服务期间遇到OOM需要杀死进程,客户进程会先于服务进程被杀死。 BIND_ADJUST_WITH_ACTIVITY 128 允许客户进程提升被绑定服务进程的优先级 BIND_ALLOW_OOM_MANAGEMENT 16 如果绑定服务期间遇到OOM需要杀死进程,被绑定的服务进程会被OOM列入猎杀对象中。 BIND_AUTO_CREATE 1 若绑定服务时服务未启动,则会自动启动服务。 注意,这种情况下服务的onStartCommand仍然未被调用(它只会在显式调用startService时才会被调用)。 BIND_DEBUG_UNBIND 2 使用此标志绑定服务之后的unBindService方法会无效。 这种方法会引起内存泄露,只能在调试时使用。 BIND_IMPORTANT 64 被绑定的服务进程优先级会被提到FOREGROUND级别 BIND_NOT_FOREGROUND 4 被绑定的服务进程优先级不允许被提到FOREGROUND级别 BIND_WAIVE_PRIORITY 32 被绑定的服务进程不会被OOM列入猎杀对象中。

关于进程优先级,系统在将要发生Out Of Memory(OOM)异常时会挑优先级最高的进程先杀死来释放内存,具体见参考文献Android Process。

要和所绑定的Service交互,都是通过实现ServiceConnection接口,在其中的onServiceConnected方法中获取到IBinder示例,并通过以下几种方法进行沟通。

1. 继承Binder

如果服务器提供的Service与客户端在一个进程内(说白了就是访问自己进程内的另一个Service), 用这种方法访问Service是你最好的选择
你可以通过在Service中实现一个公共内部类继承Binder,并在Service的onBind()方法中返回这个内部类的一个实例。
在客户端中可以直接将获取到的IBinder类型转换为Service中定义的公共内部类,并通过它来与Service通信。

具体实现方法

本地服务类的实现:

public class LocalService extends Service {  public static final String TAG = "LocalService";  // 要传递给客户端的Binder实例。  private final IBinder mBinder = new LocalBinder();  private final Random mGenerator = new Random();  /**   * 继承Binder的类,必须为public。   * 通过getService()获取Service实例。   */  public class LocalBinder extends Binder {      LocalService getService() {          return LocalService.this;      }  }  /** 当被绑定时传回Binder实例 */  @Override  public IBinder onBind(Intent intent) {      return mBinder;  }  /** 测试用的函数 */  public void showLog(String message) {    Log.i(TAG, message);  }}

本地客户端绑定:

public class BindingActivity extends Activity {  LocalService mService;  boolean mBound = false;  @Override  protected void onCreate(Bundle savedInstanceState) {      super.onCreate(savedInstanceState);      setContentView(R.layout.main);  }  @Override  protected void onStart() {      super.onStart();      // 绑定Service      Intent intent = new Intent(this, LocalService.class);      bindService(intent, mConnection, Context.BIND_AUTO_CREATE);      mService.showLog("call from client.");  }  @Override  protected void onStop() {      super.onStop();      // Unbind from the service      if (mBound) {          unbindService(mConnection);          mBound = false;      }  }  /** 实现ServiceConnection */  private ServiceConnection mConnection = new ServiceConnection() {      @Override      public void onServiceConnected(ComponentName className,              IBinder service) {          // 将IBinder转换为LocalBinder          LocalService.LocalBinder binder = (LocalService.LocalBinder) service;          mService = binder.getService();          mBound = true;      }      @Override      public void onServiceDisconnected(ComponentName arg0) {          mBound = false;      }  };}

2. 使用Messenger

如果客户端访问的Service在其他进程中,你可以通过Messenger来提供/获取服务接口。
Service可以实现一个Handler来处理不同的消息,Messenger可以通过IBinder将Handler传递给客户端,从而实现消息传递。同时客户端也可以使用Messenger让服务端传消息过来。
这是最简单的跨进程交流的办法,因为Messenger将消息队列保持在一个线程之内,服务端同一时间只能处理一个请求,你不需要考虑复杂的线程同步问题。

具体实现

服务进程实现:

public class MessengerService extends Service {  public static final String TAG = "MessengerService";  static final int MSG_SAY_HELLO = 1;  /**   * 用来处理消息的Handler   */  class IncomingHandler extends Handler {      @Override      public void handleMessage(Message msg) {          switch (msg.what) {              case MSG_SAY_HELLO:                  Log.i(TAG, "hello from client process.");                  break;              default:                  super.handleMessage(msg);          }      }  }  /**   * 将Handler实例传给Messenger   */  final Messenger mMessenger = new Messenger(new IncomingHandler());  /**   * 当被绑定时传回包含Messenger的IBinder。   */  @Override  public IBinder onBind(Intent intent) {      return mMessenger.getBinder();  }}

客户进程实现:

public class ActivityMessenger extends Activity {  static final int MSG_SAY_HELLO = 1;  Messenger mService = null;  boolean mBound = false;  /**   * 实现ServiceConnection   */  private ServiceConnection mConnection = new ServiceConnection() {      public void onServiceConnected(ComponentName className, IBinder service) {          /* 当绑定建立时获取服务端的Messenger */          mService = new Messenger(service);          mBound = true;      }      public void onServiceDisconnected(ComponentName className) {          /* 当连接断开后释放Messenger,防止内存泄露 */          mService = null;          mBound = false;      }  };  public void sayHello(View v) {      if (!mBound) return;      Message msg = Message.obtain(null, MSG_SAY_HELLO, 0, 0);      try {          mService.send(msg);      } catch (RemoteException e) {          e.printStackTrace();      }  }  @Override  protected void onCreate(Bundle savedInstanceState) {      super.onCreate(savedInstanceState);      setContentView(R.layout.main);  }  @Override  protected void onStart() {      super.onStart();      bindService(new Intent(this, MessengerService.class), mConnection,          Context.BIND_AUTO_CREATE);  }  @Override  protected void onStop() {      super.onStop();      if (mBound) {          unbindService(mConnection);          mBound = false;      }  }}

3. 使用AIDL

AIDL将消息压缩成原始数据流然后在进程之间传递,Messenger的实现就是基于AIDL。 仅仅当你的Service需要同时处理多个请求时你才需要自己实现AIDL,否则你应该使用Messenger实现跨进程交流
如果要实现AIDL,你要创建一个.aidl文件,Android ADK tool会自动创建一个虚类来提供处理IPC消息的接口,你可以通过实现接口来处理消息。
再一次提示:大部分应用并不适合直接实现AIDL,只有当Service必须同时处理多个请求时,你才直接实现它。

鉴于AIDL并不推荐自己实现,而且实现起来较为复杂,本文暂不提供实现办法。有兴趣的同学可以参考以下两个网站,他们都提供了比较具体的实现办法:

AIDL进程间传递自定义类型参数

Android Interface Definition Language (AIDL) (需翻墙)


被绑定Service的生命周期

如果Service只是由bindService启动的,那么无须关注它的生命周期,当所有对Service的绑定断开之后,系统会自动杀死Service。

但是当Service是由startService启动的,则情况就不一样了。这种情况下Service的onStartCommand会被执行,必须显式地调用stopService,service才会被杀死。这种情况下你可以让Service判断是否有客户端在解除绑定后重新绑定,并实现onRebind进行相应操作(可以返回与onBind不一样的IBinder)。通过重写Service的onUnBind函数返回true来告诉Service是否要使用onRebind

下图来自Android API Guides - Bound Service,清晰地阐述了被绑定Service的生命周期:

这里写图片描述


几点提示

  1. 由于服务进程是有可能在中途被杀死的,在客户端保持mBound布尔值来判断是否连接仍然健康是必要的,否则你应该监控是否有DeadObjectException的发生。

  2. 绑定Service后一定要在onStop或者onDestroy中调用unBindService释放连接,否则会有内存泄露。 具体要和哪个生命周期进行搭配,取决于应用需要,参考Managing the Activity Lifecycle.

  3. 无论Service被多少个线程绑定,它都只会返回同一个IBinder实例。


参考文献

  1. Android API Guides - Bound Service

  2. Android API Guides - AIDL

  3. IBinder

  4. Android Process

  5. Handler

0 0
原创粉丝点击