最近又重新看了点IPC的相关知识,以前看过不少Binder的知识,看到c++层思路就很模糊了,回头再看也算是一种学习。
IPC全称为:interprocess communication内部进程间通信,官方地址:http://developer.android.com/intl/zh-cn/guide/components/aidl.html,网上对这一块介绍的资料也着实不少,入门的话还是推荐看老罗的。
鉴于以前也知识看看相关知识,从来也没动手去做过,于是就照着官方文档开始动手创建Demo, 在按照官方文档实现之后,我察觉到有一个需求,官方只是说客户端可以调用远程服务端,那么远程服务端想要调用客户端可不可以呢?
为了尽快实现需求,那我就先将服务启动在本地,也就是说与Service与Activity处于同一进程,进行测试。它的结果是可行的,相关代码如下:
Activity相关代码:
<span style="font-family:Microsoft YaHei;font-size:14px;">public class MainActivity extends ActionBarActivity implements ServiceConnection, IServiceConnect { public final static String TAG = "MainActivity"; @Override protected void onCreate(Bundle savedInstanceState) { ... bindService(new Intent("com.example.aidl.RemoteService"), this, BIND_AUTO_CREATE); } @Override public void onServiceConnected(ComponentName name, IBinder service) { <span style="white-space:pre"> </span>Log.e(TAG, "MainActivity onServiceConnected " + name + " - " + service); if (service instanceof ServiceBinder) { ServiceBinder new_name = (ServiceBinder) service; // 获取服务的引用,使服务与Activity建立双向关系 RemoteService service2 = new_name.getService(); service2.setmServiceConnect(this); } } ... @Override public void callback() { // 这种模式只有在同一进程中的服务才可以被使用 Log.e(TAG, "MainActivity 被服务端调用"); } @Override protected void onDestroy() { super.onDestroy(); unbindService(this); } } </span>Service相关代码:
<span style="font-family:Microsoft YaHei;font-size:14px;">public class RemoteService extends Service implements Runnable { public final static String TAG = "RemoteService"; public IServiceConnect mServiceConnect; ... @Override public IBinder onBind(Intent intent) { Log.i(TAG, "RemoteService onBind"); return new ServiceBinder(this);//返回含有服务引用的内部类 } public static class ServiceBinder extends Binder { private RemoteService service; public ServiceBinder(RemoteService service) { super(); this.service = service; } public RemoteService getService() { return service; } } public void setmServiceConnect(IServiceConnect mServiceConnect) { this.mServiceConnect = mServiceConnect; new Handler().postDelayed(RemoteService.this, 3000);//当建立连接之后启动一个任务 } @Override public void run() { if (mServiceConnect != null) { mServiceConnect.callback();//调用Activity的相关方法,验证通信 } } } </span>
上述结果是可以在同一进程的Activity与Service间互相通信的,因为在onServiceConnected中回调的Binder对象则为刚刚Service返回的对象,在Activity中打印的Log如下:
那么跨进程间互相通信可不可以呢?
我们在Service的Manifest文件中给Service添加Process属性,属性值为:remote:
<span style="font-family:Microsoft YaHei;font-size:14px;"><service android:name="com.example.aidl.RemoteService" android:process=":remote" > <intent-filter> <action android:name="com.example.aidl.RemoteService" /> <category android:name="android.intent.category.DEFAULT" /> </intent-filter> </service></span>好了,这下启动服务之后,它就属于远程服务了,它与Activity不属于同一个进程,在Android中一个进程则是一个虚拟机。
如果我再按照刚才的方法进行测试,服务是启动了,但是Activity收到的Binder对象却是这样的:
这里是说,返回的组件名称为RemoteService,但是返回的Binder对象则为BinderProxy,这个BinderProxy则为RemoteService远程服务在本地的代理,熟悉AIDL的相关信息的人,肯定知道Proxy知道是什么。
那么我们在这里可不可以从Proxy对象中把远程的RemoteService对象拿到呢,我们看一下调试信息:
我们看到在Proxy中的并没有RemoteService的引用,嗯,因为IPC通信,是跨进程的,我们都是通过代理与服务打交道的,所以...
那么回到标题,说说远程服务与本地服务的特点与差异:
其实都是服务,提供的功能一致,只是一个在本地,一个在其它进程,所以,服务与客户端打交道的时候就有了不同,要谨慎对待。