转载

与Sevice实现双向通信(一)

经常有这样的应用场景,我们需要Client端调用Service完成一些事情,Service也可以通过回调通知客户端。

这是我写的第一个系列文章:

  1. 与Sevice实现双向通信(一)
  2. 与Sevice实现双向通信(二) (coming soon)
  3. 与Sevice实现双向通信(三) (coming soon)

这是系列文章的第一篇,本文主要实现一个基本的远程AIDL调用。

首先,大概来总结一下与Service的通信方式有很多种:

  1. 通过 BroadCastReceiver :这种方式是最简单的,只能用来交换简单的数据;
  2. 通过 Messager :这种方式是通过一个传递一个Messager给对方,通过这个它来发送 Message 对象。这种方式只能单向传递数据。可以是 ServiceActivity ,也可以是从 Activity 发送数据给 Service 。一个Messeger不能同时双向发送;
  3. 通过Binder来实现远程调用(IPC):这种方式是Android的最大特色之一,让你调用远程 Service 的接口,就像调用本地对象一样,实现非常灵活,写起来也相对复杂。

本文最重点谈一下怎么使用AIDL实现Service端和Client端的双向通信(或者叫"调用")。

首先定义一个AIDL接口如下:

// IRemoteService.aidl package com.race604.servicelib;  interface IRemoteService {       int someOperate(int a, int b); } 

这里只定义了一个简单的接口 someOperate() ,输入参数 ab ,返回一个 int 值。

Service的实现如下:

// RemoteService.java package com.race604.remoteservice; import ... public class RemoteService extends Service {    private static final String TAG = RemoteService.class.getSimpleName();  private final IRemoteService.Stub mBinder = new IRemoteService.Stub() {   @Override   public int someOperate(int a, int b) throws RemoteException {    Log.d(TAG, "called RemoteService someOperate()");    return a + b;   }  };  @Override  public IBinder onBind(Intent intent) {   return mBinder; // 注意这里返回binder  } }  

这里,在 RemoteService 里面实现一个 IRemoteService.Stub 接口的 Binder ,并且在onBind()中返回此 Binder 对象。 在 AndroidManifest.xmlRemoteService 的申明如下:

<service     android:name=".RemoteService"   android:enabled="true"   android:exported="true" >    <intent-filter>       <action android:name="com.race604.servicelib.IRemoteService" />   </intent-filter> </service>   

这里 android:exported="true" 表示可以让其他进程绑定,这里还有一个 <action android:name="com.race604.servicelib.IRemoteService" /> ,这里是为了让后面的 Client 通过此Action来绑定。

Client的调用方法如下:

package com.race604.client; import ... public class MainActivity extends ActionBarActivity implements View.OnClickListener {  private static final String TAG = MainActivity.class.getSimpleName();  private IRemoteService mService;  private ServiceConnection mServiceConnection = new ServiceConnection() {   @Override   public void onServiceConnected(ComponentName name, IBinder service) {    Toast.makeText(MainActivity.this, "Service connected", Toast.LENGTH_SHORT).show();    mService = IRemoteService.Stub.asInterface(service);   }   @Override   public void onServiceDisconnected(ComponentName name) {    Toast.makeText(MainActivity.this, "Service disconnected", Toast.LENGTH_SHORT).show();    mService = null;   }  };  @Override  protected void onCreate(Bundle savedInstanceState) {   super.onCreate(savedInstanceState);   setContentView(R.layout.activity_main);   findViewById(R.id.bind).setOnClickListener(this);   findViewById(R.id.unbind).setOnClickListener(this);   findViewById(R.id.call).setOnClickListener(this);  }  private void callRemote() {   if (mService != null) {    try {     int result = mService.someOperate(1, 2);     Toast.makeText(this, "Remote call return: " + result, Toast.LENGTH_SHORT).show();    } catch (RemoteException e) {     e.printStackTrace();     Toast.makeText(this, "Remote call error!", Toast.LENGTH_SHORT).show();    }   } else {    Toast.makeText(this, "Service is not available yet!", Toast.LENGTH_SHORT).show();   }  }  @Override  public void onClick(View v) {   switch (v.getId()) {    case R.id.bind:     Intent intent = new Intent(IRemoteService.class.getName());     bindService(intent, mServiceConnection, BIND_AUTO_CREATE);     break;    case R.id.unbind:     unbindService(mServiceConnection);     break;    case R.id.call:     callRemote();     break;   }  } }  

在客户端,使用Context.bindService()函数,绑定到远程的Service。注意到这里的 Intent intent = new Intent(IRemoteService.class.getName()); ,和上面的 Service 申明的Action一致。 BIND_AUTO_CREATE 这个Flag从名字就能看出,表示如果Bind的时候,如果还没有Service的实例,就自动创建。

这里有一个地方需要注意,在Android 5.0以后,就不允许使用非特定的Intent来绑定Service了,需要使用如下方法:

Intent intent = new Intent(IRemoteService.class.getName());   intent.setClassName("com.race604.remoteservice", "com.race604.remoteservice.RemoteService");   // 或者setPackage() // intent.setPackage("com.race604.remoteservice"); bindService(intent, mServiceConnection, BIND_AUTO_CREATE);   

到这里就基本实现了一个完整的Client调用远程Service的实例了。

源代码可以参考这个 Commit 。

正文到此结束
Loading...