进程通信之 AIDL 解析(二)

1.AIDL的简介

AIDL (Android Interface Definition Language) 是一种接口定义语言,用于生成可以在Android设备上两个进程之间进行进程间通信(Interprocess Communication, IPC)的代码。如果在一个进程中(例如Activity)要调用另一个进程中(例如Service)对象的操作,就可以使用AIDL生成可序列化的参数,来完成进程间通信。

AIDL能够实现进程间通信,其内部是通过Binder机制来实现的

AIDL支持的数据类型:

  • Java 编程语言中的所有基本数据类型(如 int、long、char、boolean 等等)
  • String和CharSequence
  • Parcelable:实现了Parcelable接口的对象
  • List:其中的元素需要被AIDL支持,另一端实际接收的具体类始终是
  • ArrayList,但生成的方法使用的是 List 接口
  • Map:其中的元素需要被AIDL支持,包括key和value,另一端实际接收的具体类始终 是 HashMap,但生成的方法使用的是 Map 接口

其他注意事项:

  • 在AIDL中传递的对象,必须实现Parcelable序列化接口;
  • 在AIDL中传递的对象,需要在类文件相同路径下,创建同名、但是后缀为.aidl的文件,并在文件中使用parcelable关键字声明这个类;
  • 跟普通接口的区别:只能声明方法,不能声明变量;
  • 所有非基础数据类型参数都需要标出数据走向的方向标记。可以是 in、out 或 inout,基础数据类型默认只能是 in,不能是其他方向。

2.AIDL的使用

下面是项目的目录结构

image
其中在aidl文件下创建相同的包结构,在bean目录下创建和实体类一样的aidl文件,并用 parcelable关键字声明这个类
package com.kx.studyview.aidl.bean;

parcelable MessageModel ;
MessageReceiver,用于实现服务端传递给客户端消息的监听,类似接口回调的方式。
package com.kx.studyview.aidl;
import com.kx.studyview.aidl.bean.MessageModel ;
interface MessageReceiver {
  void onMessageReceived(in MessageModel receivedMessage);
}
MessageSender,主要用于实现客户端与服务端的交互
// MessageSender.aidl
package com.kx.studyview.aidl;
import com.kx.studyview.aidl.bean.MessageModel ;
import com.kx.studyview.aidl.MessageReceiver ;
interface MessageSender {
  void sendMessage(in MessageModel messageModel);
  void registerReceiveListener(MessageReceiver messageReceiver);
  void unregisterReceiveListener(MessageReceiver messageReceiver);
}

在Build之后,系统会为我们生成MessageSender.java文件

public interface MessageSender extends android.os.IInterface {
    /**
     * Local-side IPC implementation stub class.
     */
    public static abstract class Stub extends android.os.Binder implements com.kx.studyview.aidl.MessageSender {
       //  代表当前Binder的唯一标识,一般为完整路径
        private static final java.lang.String DESCRIPTOR = "com.kx.studyview.aidl.MessageSender";

        /**
         * Construct the stub at attach it to the interface.
         */
        public Stub() {
            this.attachInterface(this, DESCRIPTOR);
        }

       将服务端的Binder对象转换为客户端所需要的的AIDL接口类型对象,如果客户端和服务端位于同一进 
       程,则返回服务端的Stub对象本身,当两者处于不同进程时,返回的是系统封装后的Stub.proxy对象
        public static com.kx.studyview.aidl.MessageSender asInterface(android.os.IBinder obj) {
            if ((obj == null)) {
                return null;
            }
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            if (((iin != null) && (iin instanceof com.kx.studyview.aidl.MessageSender))) {
                return ((com.kx.studyview.aidl.MessageSender) iin);
            }
            return new com.kx.studyview.aidl.MessageSender.Stub.Proxy(obj);
        }

        @Override
        public android.os.IBinder asBinder() {
            return this;
        }

        @Override
        public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {
            switch (code) {
                case INTERFACE_TRANSACTION: {
                    reply.writeString(DESCRIPTOR);
                    return true;
                }
                case TRANSACTION_sendMessage: {
                    data.enforceInterface(DESCRIPTOR);
                    com.kx.studyview.aidl.bean.MessageModel _arg0;
                    if ((0 != data.readInt())) {
                        _arg0 = com.kx.studyview.aidl.bean.MessageModel.CREATOR.createFromParcel(data);
                    } else {
                        _arg0 = null;
                    }
                    this.sendMessage(_arg0);
                    reply.writeNoException();
                    return true;
                }
                case TRANSACTION_registerReceiveListener: {
                    data.enforceInterface(DESCRIPTOR);
                    com.kx.studyview.aidl.MessageReceiver _arg0;
                    _arg0 = com.kx.studyview.aidl.MessageReceiver.Stub.asInterface(data.readStrongBinder());
                    this.registerReceiveListener(_arg0);
                    reply.writeNoException();
                    return true;
                }
                case TRANSACTION_unregisterReceiveListener: {
                    data.enforceInterface(DESCRIPTOR);
                    com.kx.studyview.aidl.MessageReceiver _arg0;
                    _arg0 = com.kx.studyview.aidl.MessageReceiver.Stub.asInterface(data.readStrongBinder());
                    this.unregisterReceiveListener(_arg0);
                    reply.writeNoException();
                    return true;
                }
            }
            return super.onTransact(code, data, reply, flags);
        }

        不在一个进程时返回的代理
        private static class Proxy implements com.kx.studyview.aidl.MessageSender {
            private android.os.IBinder mRemote;

            Proxy(android.os.IBinder remote) {
                mRemote = remote;
            }

            @Override
            public android.os.IBinder asBinder() {
                return mRemote;
            }

            public java.lang.String getInterfaceDescriptor() {
                return DESCRIPTOR;
            }

            @Override
            public void sendMessage(com.kx.studyview.aidl.bean.MessageModel messageModel) throws android.os.RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    if ((messageModel != null)) {
                        _data.writeInt(1);
                        messageModel.writeToParcel(_data, 0);
                    } else {
                        _data.writeInt(0);
                    }
                    mRemote.transact(Stub.TRANSACTION_sendMessage, _data, _reply, 0);
                    _reply.readException();
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
            }

            @Override
            public void registerReceiveListener(com.kx.studyview.aidl.MessageReceiver messageReceiver) throws android.os.RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    _data.writeStrongBinder((((messageReceiver != null)) ? (messageReceiver.asBinder()) : (null)));
                    mRemote.transact(Stub.TRANSACTION_registerReceiveListener, _data, _reply, 0);
                    _reply.readException();
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
            }

            @Override
            public void unregisterReceiveListener(com.kx.studyview.aidl.MessageReceiver messageReceiver) throws android.os.RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    _data.writeStrongBinder((((messageReceiver != null)) ? (messageReceiver.asBinder()) : (null)));
                    mRemote.transact(Stub.TRANSACTION_unregisterReceiveListener, _data, _reply, 0);
                    _reply.readException();
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
            }
        }
        用于区分不同方法名的 code
        static final int TRANSACTION_sendMessage = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
        static final int TRANSACTION_registerReceiveListener = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
        static final int TRANSACTION_unregisterReceiveListener = (android.os.IBinder.FIRST_CALL_TRANSACTION + 2);
    }

    public void sendMessage(com.kx.studyview.aidl.bean.MessageModel messageModel) throws android.os.RemoteException;

    public void registerReceiveListener(com.kx.studyview.aidl.MessageReceiver messageReceiver) throws android.os.RemoteException;

    public void unregisterReceiveListener(com.kx.studyview.aidl.MessageReceiver messageReceiver) throws android.os.RemoteException;
}

其中Stub 的 onTransact() 运行在服务端的Binder线程池中,当客户端发起请求时,服务端根据code可以确定客户端所请求的目标方法时什么。

Proxy#sendMessage

        @Override
          public void sendMessage(com.kx.studyview.aidl.bean.MessageModel messageModel) throws android.os.RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    if ((messageModel != null)) {
                        _data.writeInt(1);
                        messageModel.writeToParcel(_data, 0);
                    } else {
                        _data.writeInt(0);
                    }
                    mRemote.transact(Stub.TRANSACTION_sendMessage, _data, _reply, 0);
                    _reply.readException();
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
            }
首先创建此方法的所需的输入型对象_data 和输出型对象_reply,然后将messageModel信息序列化写入_data中(如果messageModel不为空), _data.writeInt(1)和_data.writeInt(0)用于在Stub 的 onTransact()方法中判断是否进行反序列化。接着调用transact方法来发起RPC(远程过程调用)请求,同时当前线程挂起。
    mRemote.transact(Stub.TRANSACTION_sendMessage, _data, _reply, 0);
然后服务端的onTransact方法会被调用。根据不同的code区分不同的方法,
             case TRANSACTION_sendMessage: {
                   data.enforceInterface(DESCRIPTOR);
                   com.kx.studyview.aidl.bean.MessageModel _arg0;
                   if ((0 != data.readInt())) {
                       _arg0 = com.kx.studyview.aidl.bean.MessageModel.CREATOR.createFromParcel(data);
                   } else {
                       _arg0 = null;
                   }
                   this.sendMessage(_arg0);
                   reply.writeNoException();
                   return true;
               }
当 data.writeInt(1)时,进行反序列化操作,生成与客户端发送消息内容一样的对象(不同的对象,内容一样),data.writeInt(0)时,返回空,代表客户端发送的是null。
 _arg0 = com.kx.studyview.aidl.bean.MessageModel.CREATOR.createFromParcel(data);
反序列化完成后,调用sendMessage方法,这个方法的实现是在服务端
this.sendMessage(_arg0);
在定义接口方法时,主要的执行过程都是一样的,只不过区别在于定义的方法是否有返回值,是否有参数之分。当有返回值的时候,就会向onTransact参数中的reply对象写入返回值。有参数的时候,就会从data中的取出目标所需的参数。
  void sendMessage(in MessageModel messageModel);
  void registerReceiveListener(MessageReceiver messageReceiver);
  void unregisterReceiveListener(MessageReceiver messageReceiver);

如果onTransact方法返回false,则客户端的请求就会失败,我们可以利用这个特性来做权限验证,因为我们也不希望随便一个进程都能远程调用我们的服务。

包名验证

             /**
             * 包名验证方式
             */
            String packageName = null;
            String[] packages = getPackageManager().getPackagesForUid(getCallingUid());
            if (packages != null && packages.length > 0) {
                packageName = packages[0];
            }
            if (packageName == null || !packageName.startsWith("com.kx.studyview.aidl")) {
                LogUtils.e("MessageService 进程onTransact  "  + "拒绝调用:" + packageName);
                return false;
            }

权限验证

       //  在AndroidManifest中自定义的权限
       <permission
        android:name="com.kx.studyview.aidl.permission.REMOTE_SERVICE_PERMISSION"
        android:protectionLevel="normal" />
       <uses-permission android:name="com.kx.studyview.aidl.permission.REMOTE_SERVICE_PERMISSION" />

     if(checkCallingOrSelfPermission("com.kx.studyview.aidl.permission.REMOTE_SERVICE_PERMISSION") == PackageManager.PERMISSION_DENIED) {
             return false;
      }
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,036评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,046评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,411评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,622评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,661评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,521评论 1 304
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,288评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,200评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,644评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,837评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,953评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,673评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,281评论 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,889评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,011评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,119评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,901评论 2 355

推荐阅读更多精彩内容