名词解释:
Remote Service -- 远程服务,此服务运行在独立进程。
Local App -- 本地应用,其他想要使用远程服务的app,运行在另外的线程。
远程服务的使用,涉及到进程间通信IPC(Inter-Process Communication),在android中就需要用到AIDL(Android Interface Definition Language) ,关于AIDL的介绍可以参考下面几篇帖子:
官方文档:
博友文档:
既然讲到交互,那必须要分为两部分进行:
首先是Local App调用Remote Service
关于这一点,网上已经有大量的说明和范例,简单说明如下:
1,在Remote Service工程中创建aidl接口文件IRemoteService.aidl:
package com.walkbin.drill.aidl;interface IRemoteService{ boolean doSomething(String thing);}
在gen目录下,sdk会自动生成对应的java文件。
2,新建类RemoteService继承于Service,创建成员mBinder并实现之前在IRemoteService.aidl中定义的接口。
private final IRemoteService.Stub mBinder = new IRemoteService.Stub(){ @Override public boolean doSomething(String thing) throws RemoteException { Log.i(TAG, "~~~~i'm doing [" + thing + "]"); return false; } };在onBind方法中返回该mBinder。
@Override public IBinder onBind(Intent intent) { // TODO Auto-generated method stub Log.i(TAG, "~~~~onBind"); return mBinder; }
3,在对应的Androidmenifest.xml中注册RemoteService:
android:process=":rmtser" 提示系统将RemoteService放到进程com.walkbin.drill.remoteservice:rmtser中运行,这是一个独立的进程。 而<action android:name="com.walkbin.drill.remote_service"/>是对外暴露的名称,外部可以通过这个名字来访问service。
4,创建ClientApp项目,将RemoteService中定义的aidl文件复制过来,我们在Activity的onCreate中绑定RemoteService,并设置按键回调,在onDestroy中注意unBindRemoteService。
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Intent i = new Intent("com.walkbin.drill.remote_service"); bindService(i, mSerConn, BIND_AUTO_CREATE); Button btn = (Button)findViewById(R.id.btn_do_smt); btn.setOnClickListener(this); } @Override protected void onDestroy() { // TODO Auto-generated method stub unbindService(mSerConn); super.onDestroy(); }创建ServiceConnection
private IRemoteService mRemoteService; private static final String TAG = "ClientApp"; private ServiceConnection mSerConn = new ServiceConnection(){ @Override public void onServiceConnected(ComponentName name, IBinder service) { // TODO Auto-generated method stub Log.i(TAG, "~~~~onServiceConnected"); mRemoteService = IRemoteService.Stub.asInterface(service); } @Override public void onServiceDisconnected(ComponentName name) { // TODO Auto-generated method stub Log.i(TAG, "~~~~onServiceDisconnected"); mRemoteService = null; } };在按键的回调用,我们调用RemoteService中定义的doSomething方法:
@Override public void onClick(View v) { // TODO Auto-generated method stub switch(v.getId()){ case R.id.btn_do_smt:{ try { mRemoteService.doSomething("show me!!!"); } catch (RemoteException e) { // TODO Auto-generated catch block e.printStackTrace(); } }break; } }这样第一部分就完成了,ClientApp成功调用了RemoteService中的doSomething方法。其次让Remote Service能够调用Local App中的方法 因为仍然是跨进程的调用,所以依然要使用aidl方式来实现。 1,同样我们需要定义一个aidl文件来描述,新建IRemoteCB.aidl文件
package com.walkbin.drill.aidl;interface IRemoteCB{ void callSomething(String thing);}在ClientApp项目中的Activity类中实现如下:
private final IRemoteCB.Stub mCB = new IRemoteCB.Stub(){ @Override public void callSomething(String thing) throws RemoteException { // TODO Auto-generated method stub Log.i(TAG, "~~~~call back [" +thing +"]"); } };2,在IRemoteService.aidl中增加两个方法,用来注册和取消回调,更新如下:
package com.walkbin.drill.aidl;import com.walkbin.drill.aidl.IRemoteCB;interface IRemoteService{ boolean doSomething(String thing); void regCB(IRemoteCB cb); void unregCB(IRemoteCB cb);}
相应的要在RemoteService中实现新增的这两个接口。
将更新后的aidl文件同步到两个项目中,这里必须要保证aidl文件的包路径在两个项目中一致,否则运行时会抛异常。
@Override public void regCB(IRemoteCB cb) throws RemoteException { // TODO Auto-generated method stub Log.i(TAG, "~~~~regCB"); mCBList.register(cb); } @Override public void unregCB(IRemoteCB cb) throws RemoteException { // TODO Auto-generated method stub Log.i(TAG, "~~~~unregCB"); mCBList.unregister(cb); }其中mCBList是RemoteService新增的一个成员,定义为:
private RemoteCallbackListmCBList = new RemoteCallbackList ();
在ClientApp的activity中获取到mService之后调用regCB来注册回调方法。
@Override public void onServiceConnected(ComponentName name, IBinder service) { // TODO Auto-generated method stub Log.i(TAG, "~~~~onServiceConnected"); mRemoteService = IRemoteService.Stub.asInterface(service); try { mRemoteService.regCB(mCB); } catch (RemoteException e) { // TODO Auto-generated catch block e.printStackTrace(); } } @Override public void onServiceDisconnected(ComponentName name) { // TODO Auto-generated method stub Log.i(TAG, "~~~~onServiceDisconnected"); try { mRemoteService.unregCB(mCB); } catch (RemoteException e) { // TODO Auto-generated catch block e.printStackTrace(); } mRemoteService = null; }
再对mCBList的使用进行一次封装
private void doCB(String str) { int n = mCBList.beginBroadcast(); try { for (int i = 0; i < n; i++) { mCBList.getBroadcastItem(i).callSomething(str); } } catch (RemoteException e) { e.printStackTrace(); } mCBList.finishBroadcast(); }
这样RemoteService中就可以通过doCB来调用到ClientApp中的callSomething方法了!
我们暂且将doCB放到doSomething方法中去调用
@Override public boolean doSomething(String thing) throws RemoteException { Log.i(TAG, "~~~~i'm doing [" + thing + "]"); doCB(thing); return false; }
看一下log信息:
OK,我们已经看到call back的输出了,这表明ClientApp中的指定方法被成功调用。