AIDL初探:夸进程的通信和跨进程的回调机制

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/yus201120/article/details/53202517

最近一个项目涉及到跨进程的操作,我们的应用ClientApp需要用到底层的数据,但是这个数据data是在一个不断监听接收底层的服务ServiceA里面,痛苦的是这个服务是在另外一个进程里面。虽然我们可以用万能的广播机制,但这会造成系统非常大的负担。另外一种方法可以用ContentProvider,很多时候还是挺好用的,但是实时性不够,两个进程之间也缺乏交互性,所以我决定用AIDL来实现这一功能。
1、AIDL基本介绍
AIDl是Android中专门针对跨进程而设计的一种机制。AIDL全称是Android Interface Definition Language(安卓接口定义语言),使用它的语法和Java的风格很相似。这是我写一个aidl文件:

package com.zhonghong.airconditione.aidl;
import com.zhonghong.airconditione.aidl.NotifyCallBack;
interface HealthyServer{
    int[] getHealthyInfo();
    int[] getWheelPressure();
    void registerCallBack(NotifyCallBack callback);
    void unregisterCallBack(NotifyCallBack callback);
}

项目结构如下:
这里写图片描述

大家可以看到我在这个定义了一系列的接口,就像我们平时用到的回调接口一样,只是和java不同的是返回值前面不能有其他的修饰字符。同时注意以下几点:
a、AIDL支持的java中8种基本数据类型:byte、short、int、float、double、char、boolean;
String类型;
CharSequence类型;
List类型,当然list里面的元素必须是AIDL所支持的;
Map类型,同理;
b、如果想要自定义类型,那么就需要实现Parcelable接口让对象序列化;
c、如图所示,即使两个aidl文件是在同一个包下面,引用其他的非默认支持的类型时也要导包。
d、参数其实是分三种的,一种是只能输入参数 in,例如:void setSomething(in int data),默认是in参数;一种是输出参数 out,当客户端用这种参数时,服务端不会获取数据,但是当这个对象在服务端改变的时候,客户端的数据也会跟着改变。另外一种就是输入输出参数 inout。

2、使用AIDL
定义完AIDL文件后,系统会在gen文件里自动生成相关的java接口,如下:
这里写图片描述
在我的服务端里面,我就会根据这个接口生成一个binder返回给客户端:

public class MyIBinder extends HealthyServer.Stub{

        @Override
        public int[] getHealthyInfo() throws RemoteException {
            return carHealthyData;
        }

        @Override
        public int[] getWheelPressure() throws RemoteException {
            return wheelPressureData;
        }

        public void setActivity(MainActivity activity){
            mainActivity = activity;
        }

        public void setHandler(Handler handler){
            mHandler = handler;
        }

        public AcStutas getAcStatues(){
            return mAcStutas;
        }
        public void setNull(){
            mainActivity=null;
            mHandler = null;
        }

        @Override
        public void registerCallBack(NotifyCallBack callback) throws RemoteException {
            /**此处是服务端获得回调对象 */
            mCallBacks.register(callback);
        }

        @Override
        public void unregisterCallBack(NotifyCallBack callback) throws RemoteException {
            mCallBacks.unregister(callback);
        }
    }

在客户端这边,我们想要使用其他进程提供的AIDL服务,那么也需要有和服务端相同的.adil文件,同样也在gen文件里生成相应的接口,然后去绑定服务端获取binder。客户端代码如下:

private ServiceConnection conn = new ServiceConnection() {

        @Override
        public void onServiceDisconnected(ComponentName name) {
            try {
                mServer.unregisterCallBack(callback);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }

        @SuppressLint("NewApi")
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
        /** 这句最重要,就是获取到binder*/
            mServer = HealthyServer.Stub.asInterface(service);
            try {
            /** 这里注册回调*/
                mServer.registerCallBack(callback);
            } catch (RemoteException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            refresh();

            if(healthyData==null){
                shareHealthy();
            }

            if(wheelPressure==null){
                shareWheel();
            }
        }
    };

这里的绑定动作和一般的绑定服务没什么区别,在这里我只说明一个小问题,我们的服务Service可以被多个客户端绑定,当第一个客户端绑定成功并且返回binder之后,其他客户端绑定该服务时同样也只会返回这个binder。下面我们说说这里的跨进程回调吧。
3、跨进程的回调机制:
a、首先,我们再aidl文件中定义这个回调接口:

package com.zhonghong.airconditione.aidl;
interface NotifyCallBack{
    void notifychanged(int event);
}

b、在服务端我们需要用到一个系统提供的泛型容器

private RemoteCallbackList<NotifyCallBack> mCallBacks = new RemoteCallbackList<NotifyCallBack>();

然后在服务端接收回调对象的地方,把这个对象放入容器:

@Override
        public void registerCallBack(NotifyCallBack callback) throws RemoteException {

            mCallBacks.register(callback);
        }

        @Override
        public void unregisterCallBack(NotifyCallBack callback) throws RemoteException {
            mCallBacks.unregister(callback);
        }

c、在客户端我们去实现这个回调接口,并在绑定服务成功后去设置这个回调:

NotifyCallBack callback = new NotifyCallBack.Stub() {

        @Override
        public void notifychanged(int event) throws RemoteException {
            handler.sendEmptyMessage(0x01);
        }
    };

/** 这个是在ServiceConnection里的绑定成功的回调方法*/
public void onServiceConnected(ComponentName name, IBinder service) {
            mServer = HealthyServer.Stub.asInterface(service);
            try {
                mServer.registerCallBack(callback);
            } catch (RemoteException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            refresh();

            if(healthyData==null){
                shareHealthy();
            }

            if(wheelPressure==null){
                shareWheel();
            }
        }

d、当服务端有底层的数据发送上来的时候,它就会调用回调函数通知有新的数据更新,但是这个和一般的调用不同,需要有特定的方式才能都跨进程通讯:

try {
        int len = mCallBacks.beginBroadcast();
        Log.i(TAG, "len:"+len);
        for(int i =0;i<len;i++){    
                                    mCallBacks.getBroadcastItem(i).notifychanged(COM_MSG_5E0H);
                };
            } catch (RemoteException e) {
                e.printStackTrace();
            }
            Log.i(TAG, "finish braodcast!");
            mCallBacks.finishBroadcast();

记住发送完信息后,需要关闭信息通道。

展开阅读全文

没有更多推荐了,返回首页