文章http://blog.csdn.net/shizhonghuo19870328/article/details/53192870 介绍了怎样用aidl 来实现跨进程通信。
但是此种架构只能实现从客户端到服务端的传递。 没有服务端向客户端传递的功能。
并且直接在服务端实现aidl interface, 不利于程序解耦。
下面介绍一种实现进程件双向通信的方法。
有两个模块, 模块VoiceService 和XCVoiceService。 XCVoiceService 是服务端,VoiceService是客户端。
一 客户端调用服务端的方法
1. VoiceService 新建一个 aild 文件 IVoiceService.aidl。
interface IVoiceService {
boolean isConnected();
boolean attachTextObserver(int pid, in ITextObserver observer);
}
2. 在XCVoiceService 中实现此接口。 并向ServiceManager 中注册。
public class VoiceConnector extends IVoiceService.Stub
在服务端向ServiceManager注册voiceConnector的Binder 对象。
public class VoiceService extends Service {
private VoiceConnector mBinder;
public void onCreate() {
super.onCreate();
mBinder = VoiceConnector.getInstance(this);
ServiceManagerHelper.addService(ContextHelper.ECARX_VOICE_SERVICE, mBinder);
}
}
public static void addService(String name, IBinder service) {
ServiceManager.addService(name, service, false);
}
protected IVoiceService getService() {
synchronized (serviceLock) {
if (mService != null) {
return mService;
}
IBinder b = ServiceManager.getService(ContextHelper.ECARX_VOICE_SERVICE);
if (null == b) {
Intent serviceIntent = new Intent();
serviceIntent.setAction(IntentHelper.ECARX_ACTION_VOICE_SERVICE_STARTED);
serviceIntent.addCategory(IntentHelper.ECARX_CATEGORY_VOICE_SERVICE_STARTED);
mContext.startService(serviceIntent);
for (int i = 0; i < 1; i++) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
b = ServiceManager.getService(ContextHelper.ECARX_VOICE_SERVICE);
if (b != null) {
mService = IVoiceService.Stub.asInterface(b);
break;
}
}
} else {
mService = IVoiceService.Stub.asInterface(b);
}
return mService;
}
}
这样,VoiceService 就可以向XCVoiceService 传递了。
二 从服务端XCVoiceService 向 客户端VoiceService的传递。
其实aidl 方式 没有提供从服务端向客户端的传递。
所以我们需要在aidl 中从客户端向服务端注册观察者(Observer)。
服务端用反射的方法, 回调给客户端,实现向客户端的回调。
1. 看IVoiceService.aidl的attachTextObserver 方法, 即是向服务端注册观察者的方法。
比如 观察者有如下定义, 我在次定义的也会是一个aidl. oneway 表示当服务端用反射方法向观察者传递的时候。 服务端不需要等待返回值。
貌似oneway 的aidl, 定义的方法必须是void。(我用int 标志方法, 编译报错。 错误为服务端可能得不到返回值).
下面定义的方法就是服务端XCVoiceService会反射回调的方法。
oneway interface ITextObserver {
/*
*处理文本
*/
void processText(in String text);
}
public boolean attachTextObserver(ITextObserver observer) {
IVoiceService service = getService();
int pid=Binder.getCallingPid();
if (service != null) {
try {
return service.attachTextObserver(pid, observer);
} catch (RemoteException e) {
LOGE("Dead object in attachtTextObserver, resetService" + e.getMessage());
resetService();
}
}
return false;
}
private Map mTextObservers=new HashMap();
public synchronized boolean attachTextVoiceObserver(int pid,final ITextObserver observer)
throws RemoteException{
synchronized (mTextObservers) {
IBinder b = observer.asBinder();
GLog.w(TAG, "_attachTextObserver in: " + b.toString() + "PID is" +pid);
if (!mTextObservers.containsKey(pid)) {
b.linkToDeath(new TextObserverDeathRecipient(pid,observer), 0);
mTextObservers.put(pid, observer);
GLog.w(TAG, "_attachTextObserver put mTextObservers, PID : " + pid);
}
return true;
}
}
private class TextObserverDeathRecipient implements IBinder.DeathRecipient{
private ITextObserver mObserver;
private int pid;
TextObserverDeathRecipient(int pid,ITextObserver observer){
mObserver=observer;
this.pid=pid;
}
public void binderDied() {
try {
ServiceProcessor.this._detachTextObserver(pid,mObserver);
GLog.w(TAG, "binderDied: PID is " + pid);
mObserver = null;
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
3. 服务端XCVoiceService 用Java 反射的方法反馈给客户端。
TextObserverInvoke(pid,"processText", String.class,speedText);
private boolean TextObserverInvoke(int pid, String methodName, Object... typesAndArgs){
int len = typesAndArgs.length;
if (len % 2 != 0) {
return false;
}
Method method = null;
Class>[] par = new Class[len / 2];
Object[] args = new Object[len / 2];
try {
for (int i = 0; i < len / 2; i++) {
par[i] = (Class>) typesAndArgs[i];
args[i] = typesAndArgs[len / 2 + i];
}
method = ITextObserver.class.getMethod(methodName, par);
} catch (NoSuchMethodException e1) {
e1.printStackTrace();
return false;
}
synchronized (mTextObservers) {
Iterator> it = mTextObservers.entrySet().iterator();
while (it.hasNext()) {
Map.Entry entry=it.next();
int mPid=entry.getKey();
if (mPid==pid) {
try {
ITextObserver observer = entry.getValue();
method.invoke(observer, args);
} catch (IllegalArgumentException e) {
e.printStackTrace();
break;
} catch (IllegalAccessException e) {
e.printStackTrace();
break;
} catch (InvocationTargetException e) {
GLog.e(TAG, "--------Text Observer method invoking got exception--------");
e.printStackTrace();
continue;
} catch (Exception e) {
GLog.e(TAG, "--------Text Observer method invoking got exception--------");
e.printStackTrace();
continue;
}
}
}
}
return true;
}
这里还有一个 小窍门。 因为一个服务端回被注册很多个观察者,那么反馈给观察者的时候 , 怎么判断反馈给那个观察者呢》
如果在注册的观察者,都有自己的标识位当然好。 如果没有,可以用 Binder.getcallingpid()方法将观察者的线程号用作标识位。
这在android 开发中时一种常见的做法。
如果不理解Java反射的使用方法, 请自行查阅相关文档。
这样就实现了进程间的双向通信。