Android AIDL双向通信

来源:互联网 发布:知左手边那个字怎么读 编辑:程序博客网 时间:2024/06/06 05:53

定义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.aidlpackage com.dream.aidl;// Declare any non-default types here with import statementsimport 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服务回调客户端

首先贴一下项目目录:
这里写图片描述

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

// IdCardInfoCallBack.aidlpackage 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.aidlpackage com.dream.aidl;// Declare any non-default types here with import statementsimport 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<IdCardInfoCallBack> 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进行操作            }        }    };