跨进程的双向通信

文章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);
}


3. 客户端 VoiceService 获得 VoiceConnector 的Binder 对象。

    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);

}

  2. 在客户端 VoiceService向服务端 XCVoiceService注册观察者

   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;
    }


3. 服务端保存观察者对象

    

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;
        }
    }

这里 IBinder.linkToDeath 是客户端的观察者被销毁或者不存在时调用, 服务端将观察者在服务端释放。
    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反射的使用方法, 请自行查阅相关文档。


这样就实现了进程间的双向通信。

你可能感兴趣的:(跨进程的双向通信)