Android AIDL

作者: Anne_胖达 | 来源:发表于2016-04-06 21:39 被阅读879次

    Android AIDL

    AIDL的中文名是Android接口定义语言,用来实现进程之间的数据的共享传递。

    AIDL支持的数据类型

    基本数据类型(int、long、char、boolean、double等);
    String和CharSequence;
    List:只支持ArrayList,里面的每个元素都必须能够被AIDL支持;
    Map:只支持HashMap,里面的每个元素都必须能够被AIDL支持;
    Parcelable:所有实现了Parcelable接口的对象;
    AIDL:所有的AIDL接口本身也可以在AIDL文件中使用。
    除此之外,自定义的Parcelable对象和AIDL对象必须要显式import进来

    AIDL接口的创建

    需要创建一个包含.aidl后缀的文件,在其中声明一些接口

    // IService.aidl
    package com.apps.ipc;
    
    // Declare any non-default types here with import statements
    import com.apps.request.GetAppDetailRequest;
    import com.apps.ipc.ICallback;
    
    interface IService {
        /**
         * Demonstrates some basic types that you can use as parameters
         * and return values in AIDL.
         */
        /**
        void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
                double aDouble, String aString);
        */
    
        int getAppDetail(in GetAppDetailRequest params,ICallback callback);
    
    }
    
    // ICallback.aidl
    package com.apps.ipc;
    
    // Declare any non-default types here with import statements
    import android.os.Bundle;
    
    interface ICallback {
        void onResult(in Bundle result);
    
        void onError(String code,String desc);
    }
    

    如果AIDL文件中引用到了自定义的Parcelable对象,必须要建一个与其同名的AIDL文件,并在其中声明为Parcelable类型

    // IGetAppDetailRequest.aidl
    package com.apps.request;
    
    // Declare any non-default types here with import statements
    
    parcelable GetAppDetailRequest;
    

    注:

    AIDL中除了基本数据类型,其他类型的参数必须标上方向:in、out或inout
    in表示输入型参数
    out表示输出型参数
    inout表示输入输出参数

    远程服务端Service的实现

    首先需要对服务端的AIDL接口进行实现,然后在Service的onBind方法将其返回

    public class IPCService extends Service {
    
        private ServiceStub serviceStub;
    
        @Nullable
        @Override
        public IBinder onBind(Intent intent) {
            return serviceStub;
        }
    
        @Override
        public void onCreate() {
            super.onCreate();
            serviceStub = new ServiceStub(this);
        }
    }
    
    public class ServiceStub extends IService.Stub {
        private Context context;
    
        public ServiceStub(Context context){
            this.context = context;
        }
    
        public ServiceStub() {
            super();
        }
    
        @Override
        public int getAppDetail(GetAppDetailRequest params, ICallback callback) throws RemoteException {
            App app = params.getApp();
            if (null == app || null == callback){
                return  Constant.AIDL_PARAMS_ERROR;
            }
    
            String id = app.getId();
            String version = app.getVersion();
            if (TextUtils.isEmpty(id) || TextUtils.isEmpty(version)){
                return Constant.AIDL_PARAMS_ERROR;
            }
            getAppDetailFromNetWork(id,version,callback);
            return Constant.AIDL_SUCCESS;
        }
    
        //模拟从网络获取数据
        private void getAppDetailFromNetWork(String id,String version,ICallback callback) throws RemoteException{
            //假设id为48,version为01.00.01能获得正确数据
            String app_id = "48";
            String app_version = "01.00.01";
            if (id.equals(app_id) && version.equals(app_version)){
                Bundle bundle = new Bundle();
                bundle.putParcelable(Constant.CALLBACK_KEY,newAppDetailResult(id,version));
                callback.onResult(bundle);
            }else{
                callback.onError(Constant.CALLBACK_ERROR_CODE,Constant.CALLBACK_ERROR_DESC);
            }
        }
    
        private GetAppDetailResult newAppDetailResult(String id, String version){
            AppDetail appDetail = new AppDetail();
            App app = new App(id,version);
            appDetail.setApp(app);
            appDetail.setDesc("a apk about aidl");
            appDetail.setDownloadTimes(48);
            appDetail.setName("ipc");
            GetAppDetailResult getAppDetailResult = new GetAppDetailResult();
            getAppDetailResult.setAppDetail(appDetail);
            return  getAppDetailResult;
        }
    
    }
    

    注:

    由于AIDL方法是在服务端的Binder线程池执行,因此当多个客户端同时访问时,会存在多线程操作,所以需要在AIDL方法处理线程同步。

    在AndroidManifest.xml文件中需要对Service进行配置

    <service android:name=".IPCService"
             android:enabled="true">
             <intent-filter>
                  <action android:name="com.apps.ipc.IPCService"></action>
             </intent-filter>
    </service>
    

    客户端的实现

    服务端在编译后,会在app/build/generated/source/debug下生成对应AIDL接口的java文件,需要将其以及使用到的相关的Java类一并拷贝到客户端工程,必须确保服务端与客户端这些类的包名,否则服务端客户端无法正确反序列化对方传送的数据,造成程序无法正常运行。

    img_1.png img_2.png

    绑定远程服务端

    通过Context的bindService方法对远程服务端进行绑定,intent设置包名是为了解决Android5.0之后需要显示调用服务的问题。

    private void bindService(){
        String action = "com.apps.ipc.IPCService";
        String packageName = "com.apps.ipc";
        Intent intent = new Intent(action);
        intent.setPackage(packageName);
        bindService(intent,connection, Context.BIND_AUTO_CREATE);
    }
    

    ServiceConnection用于处理客户端与服务端的连接、断开处理。当绑定服务成功后会回调执行onServiceConnected方法,表示已经连接上服务端,然后将服务端返回的Binder对象转换成AIDL接口,之后就可以通过这个接口去调用服务端的方法。当服务端因异常或者主动断开后,会执行onServiceDisConnected方法,表示已经断开连接。

    private IService service;
    
    private ServiceConnection connection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            MainActivity.this.service = IService.Stub.asInterface(service);
            result.setText("Service is connected");
        }
    
        @Override
        public void onServiceDisconnected(ComponentName name) {
            MainActivity.this.service = null;
            result.setText("Service is disconnected");
        }
     };
    
    private ICallback.Stub callback = new ICallback.Stub(){
        @Override
        public IBinder asBinder() {
            return super.asBinder();
        }
    
        @Override
        public void onResult(Bundle bundle) throws RemoteException {
            bundle.setClassLoader(GetAppDetailResult.class.getClassLoader());
            GetAppDetailResult getAppDetailResult = bundle.getParcelable(Constant.CALLBACK_KEY);
            AppDetail appDetail = getAppDetailResult.getAppDetail();
            App app = appDetail.getApp();
            //定义了一个TextView用于显示结果
            result.setText("id:" + app.getId() + ",version:" + app.getVersion() + ",name:" + appDetail.getName()
                        + ",desc:" + appDetail.getDesc() + ",downlaodTimes:" + appDetail.getDownloadTimes());
    
        }
    
        @Override
        public void onError(String code, String desc) throws RemoteException {
            result.setText("error:" + code + ",message:" + desc);
        }
    };
    
    private void getAppDetail(String id,String version){
        if (null != service){
            GetAppDetailRequest getAppDetailRequest = new GetAppDetailRequest();
            getAppDetailRequest.setApp(new App(id,version));
            try {
                int ret = service.getAppDetail(getAppDetailRequest,callback);
                if (ret == Constant.AIDL_PARAMS_ERROR){
                    result.setText("request params error");
                }
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }
    }
    

    运行结果

    image1.png
    image2.png

    项目地址

    https://github.com/peace710/aidl

    Addition

    https://github.com/peace710/AgeraCore

    相关文章

      网友评论

      • 林闰土:楼主遇到过aidl文件参数不支持Bundle类型的情况吗?怎么处理的~
      • 恨自己不能小清新:Android Studio还能创建两个项目么 是两个model吧
        Anne_胖达:@恨自己不能小清新 是开了两个窗口,服务端客户端两个project
      • 键盘男:试过跨进程AIDL通信吗?
        Anne_胖达:@苦逼键盘男kkmike999 文章上没写明,项目上是两个project,一个服务端的,一个客户端的
        键盘男:@peace710 你还没在AndroidManifest.xml配置Service在新进程启动呀
        Anne_胖达:@苦逼键盘男kkmike999 我这个例子就是跨进程的AIDL :smile:

      本文标题:Android AIDL

      本文链接:https://www.haomeiwen.com/subject/acemlttx.html