名词解释:
Remote Service -- 远程服务,此服务运行在独立进程。
Local App -- 本地应用,其他想要使用远程服务的app,运行在另外的线程。
远程服务的使用,涉及到进程间通信IPC(Inter-Process Communication),在android中就需要用到AIDL(Android Interface Definition Language) ,关于AIDL的介绍可以参考下面几篇帖子:
官方文档:http://developer.android.com/guide/components/aidl.html
博友文档:http://blog.youkuaiyun.com/saintswordsman/article/details/5130947
http://blog.youkuaiyun.com/android_tutor/article/details/6427680
既然讲到交互,那必须要分为两部分进行:
首先是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:
<service android:name="com.walkbin.drill.remoteservice.RemoteService"
android:enabled="true"
android:process=":rmtser"
android:exported="true">
<intent-filter >
<action android:name="com.walkbin.drill.remote_service"/>
</intent-filter>
</service>
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 RemoteCallbackList<IRemoteCB> mCBList = new RemoteCallbackList<IRemoteCB>();
在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中的指定方法被成功调用。