Android AIDL双向通信

定义AIDL服务

1.创建.aidl文件
2.SDK生成对应.java文件和Stub内部类
3.通过Service子类将接口暴露给外界

1. 创建.aidl文件

用Java编程语言来构造.aidl文件。每个.aidl文件必须定义一个带方法声明的接口。

AIDL支持以下数据类型:

1.Java基本类型,即int、long、char等;
2.String;
3.CharSequence;
4.List
    List中的所有元素都必须是AIDL支持的数据类型、其他AIDL接口或你之前声明的Parcelable实现类。
5.Map
    Map中的所有元素都必须是AIDL支持的数据类型、其他AIDL接口或你之前声明的Parcelable实现类。
6.其他类型,必须要有import语句,即使它跟.aidl是同一个包下。

AIDL中的方法和变量

• 方法可有零、一或多个参数,可有返回值或void。

• 所有非基本类型的参数都需要标签来表明这个数据的去向:

1.in,表示此变量由客户端设置;
2.out,表示此变量由服务端设置;
3.inout,表示此变量可由客户端和服务端设置;
4.基本类型只能是in。

• 只expose方法,不会expose静态变量。

服务端——服务(Manager.aidl)

// Manager.aidl
package com.dream.aidl;

// Declare any non-default types here with import statements
import com.dream.aidl.IdCardInfoCallBack;

interface Manager {

     void readCard(String serialPort);//开启读卡

     void clearCard();//清除读卡内容

     void stopReadCard();//停止读卡

}

2. SDK生成对应.java文件和Stub内部类

•当编译APP时,SDK工具会将项目/src/main/aidl目录下的.aidl文件一个个在项目/build/generated/source/aidl目录下生成IBinder接口.java文件。两个文件名一样,只是后缀不同。如Manager.aidl生成Manager.java。

•Stub内部类

▫ .aidl文件编译后生成的.java文件中自动生成的内部类。

▫ public static abstract声明。

▫ extends android.os.Binder。

▫ 实现.aidl文件中定义的接口,且声明其所有方法。

•实现Stub内部类要注意

▫ 对于传过来的调用,无法保证是在主线程中执行的。Service必须要考虑多线程和线程安全。

▫ 默认情况下,RPC都是异步的。**避免在主线程中调用AIDL**,不然可能会导致ANR。

▫ 不能给调用方回抛异常。

3. 通过Service子类将接口暴露给外界

需要在服务端创建一个Service子类,并在onBind()中返回Stub内部类。

/**
 * 服务端调用读卡的服务
 */
public class ReadCardService extends Service {

    @Override
    public IBinder onBind(Intent intent) {
        return mStub;
    }

    /**
     * 实现aidl中的方法
     * 不同方法调用不同读卡方法
     */
    private Manager.Stub mStub = new Manager.Stub() {

        @Override
        public void readCard(String serialPort) throws RemoteException {
            //开启读卡
                }

        @Override
        public void clearCard() throws RemoteException {
            //清除读卡          
        }

        @Override
        public void stopReadCard() throws RemoteException {
            //停止读卡
        }

    };

}

AndroidManifest.xml 配置,设置exported为true、自定义action名等。

<service
    android:name=".ReadCardService"
    android:enabled="true"
    android:exported="true"
    android:process=":remote">
    <intent-filter>
        <action android:name="com.dream.idcard">action>
    intent-filter>
service>

调用AIDL服务

• 若客户端组件和服务分开在不同APP,那么客户端所在APP的/src/main/aidl目录下必须要有一份.aidl副本。

• 开启AIDL服务。

Intent intent = new Intent();
//android 5.0以后直设置action不能启动相应的服务,需要设置packageName或者Component。
intent.setAction("com.dream.idcard");
intent.setComponent(new ComponentName("com.dream.readidcard", "com.dream.readidcard.ReadCardService"));
//绑定服务
bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE);

• 客户端建立连接后,要将接受到的AIDL服务端iBinder转换为客户端所在APP的/src/main/aidl中的Manager.aidl类型。

private ServiceConnection serviceConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
            try {
                mStub = Manager.Stub.asInterface(iBinder);
            } catch (RemoteException e) {
                e.printStackTrace();
            }

        }

        @Override
        public void onServiceDisconnected(ComponentName componentName) {         
            mStub = null;
        }
    };

• 可像普通对象一样调用mStub 。注意,AIDL服务默认是运行在主线程中,若里面有耗时操作,应该在子线程中调用AIDL。

AIDL服务回调客户端

首先贴一下项目目录:
Android AIDL双向通信_第1张图片

1.创建回调使用的aidl:IdCardInfoCallBack.aidl

// IdCardInfoCallBack.aidl
package com.dream.aidl;
/**
 * Created by mx on 2017-11-8.
 * 读卡信息回调
 */
import com.dream.aidl.IdCardInfo;

interface IdCardInfoCallBack {
    void CallBackInfo(in IdCardInfo idCardInfo);
}

2.Manager.aidl提供给客户端注册、注销的方法。

// Manager.aidl
package com.dream.aidl;

// Declare any non-default types here with import statements
import com.dream.aidl.IdCardInfoCallBack;

interface Manager {

     void readCard(String serialPort);//开启读卡

     void clearCard();//清除读卡内容

     void stopReadCard();//停止读卡

     void registerCallBack(in IdCardInfoCallBack cb);//注册回调接口

     void unregisterCallBack(in IdCardInfoCallBack cb);//注销回调接口
}

3.实现类:ReadCardService

/**
 * 服务端调用读卡的服务
 */
public class ReadCardService extends Service {

   /**
     * 读卡回调
     */
    OnReadCardListener onReadCardListener = new OnReadCardListener() {
        @Override
        public void onReadCardSuccess(IdCardInfo idCardInfo) {
            sendResponse(idCardInfo);
        }
    };

    //我们在服务端通知客户端消息的时候 也害怕 服务端 会异常销毁 导致客户端收不到消息
    //好在谷歌早就为我们考虑到这种情况  提供了RemoteCallbackList 来完成对应的功能
    //避免我们再重复一遍上述的过程
    private RemoteCallbackList mCallBacks = new RemoteCallbackList<>();

    /**
     * 实现aidl中的方法
     * 不同方法调用不同读卡方法
     */
    private Manager.Stub mStub = new Manager.Stub() {

        @Override
        public void readCard(String serialPort) throws RemoteException {
            //开启读卡
        }

        @Override
        public void clearCard() throws RemoteException {
            //清除读卡
        }

        @Override
        public void stopReadCard() throws RemoteException {
            //停止读卡
        }

        @Override
        public void registerCallBack(IdCardInfoCallBack cb) throws RemoteException {
            if (null != cb) {
                //注册回调
                mCallBacks.register(cb);
            }
        }

        @Override
        public void unregisterCallBack(IdCardInfoCallBack cb) throws RemoteException {
            if (null != cb) {
                //注销回调
                mCallBacks.unregister(cb);
            }
        }

    };

    @Override
    public IBinder onBind(Intent intent) {
        return mStub;
    }

    /**
     * 发送读取的身份证信息
     *
     * @param idCardInfo
     */
    private void sendResponse(IdCardInfo idCardInfo) {
        // 以广播的方式进行客户端回调
        int len = mCallBacks.beginBroadcast();
        for (int i = 0; i < len; i++) {
            try {
                mCallBacks.getBroadcastItem(i).CallBackInfo(idCardInfo);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }
        // 记得要关闭广播
        mCallBacks.finishBroadcast();
    }


    @Override
    public void onDestroy() {
        super.onDestroy();
        mCallBacks.kill();//销毁回调资源 否则要内存泄露
    }
}

4.客户端调用

//在建立连接时候调用    注册AIDL回调
 mStub.registerCallBack(idCardInfoCallBack);


 /**
     * AIDL服务端返回数据
     */
    protected IdCardInfoCallBack idCardInfoCallBack = new IdCardInfoCallBack.Stub() {

        @Override
        public void CallBackInfo(IdCardInfo idCardInfo) throws RemoteException {
            if (null != idCardInfo) {
                //对UI进行操作
            }
        }
    };

你可能感兴趣的:(android)