Android IPC机制之 - AIDL

本博文其实是Android 深入浅出AIDL(一)的转载版,但添加了自己的思考

Android IPC机制之 - AIDL

A[Android]
I[Interface]
D[Definition]
L[Language]
Android接口定义语言
它的作用是,方便系统为我们生成代码从而实现跨进程通讯,仅此而已。也就是说这个AIDL就是个可以快速实现跨进程通讯的工具

1.1 快速实现

  • 构造MessageBean作为传输数据的格式,构造MessageBean.aidl用于声明MessageBean,构造IDemandManager.aidl它是一个接口,规定了客户端与服务端数据传输的接口方法【声明方法】
  • 在服务端创建Service,并实现IDemandManager接口【创建服务】
  • 客户端绑定Service【绑定服务】
  • 客户端获得服务端IDemandManager接口实例,通过它调用服务端接口方法

1.2 实例目录

  • 在TestAIDL中创建两个可以单独运行的实例
    这里写图片描述
    其中aidlClient是客户端
    aidlService是服务端

  • 服务端
    这里写图片描述

  • 客户端
    这里写图片描述

1.3 运行效果

同时运行客户端和服务端,点击客户端上的绑定Service按钮时打印日志:

E/MainActivity: connection: 【(内容==首先,看到我要敬礼),(级别==1)】

1.4 服务端

  • 在main目录下创建aidl文件存放目录
    这里写图片描述

  • 创建了aidl文件还需要在build.gradle文件中android{}方法内添加aidl路径

sourceSets {
    main {
        java.srcDirs = ['src/main/java', 'src/main/aidl']
    }
}
  • 创建MessageBean.java实现了Parcelable接口,它是客户端与服务端通讯的数据格式
public class MessageBean implements Parcelable {
    public MessageBean() {
    }

    private String content;//需求内容
    private int level;//重要等级
    public String getContent() {
        return content;
    }
    public void setContent(String content) {
        this.content = content;
    }
    public int getLevel() {
        return level;
    }
    public void setLevel(int level) {
        this.level = level;
    }

    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeString(this.content);
        dest.writeInt(this.level);
    }

    public void readFromParcel(Parcel dest) {
        //注意,此处的读值顺序应当是和writeToParcel()方法中一致的
        this.content = dest.readString();
        this.level = dest.readInt();
    }
    public MessageBean(String content, int level) {
        this.content = content;
        this.level = level;
    }
    protected MessageBean(Parcel in) {
        this.content = in.readString();
        this.level = in.readInt();
    }
    public static final Creator<MessageBean> CREATOR = new Creator<MessageBean>() {
        @Override
        public MessageBean createFromParcel(Parcel source) {
            return new MessageBean(source);
        }

        @Override
        public MessageBean[] newArray(int size) {
            return new MessageBean[size];
        }
    };
    @Override
    public String toString() {
        return "【(内容==" + content + "),(级别==" + level + ")】";
    }
}
  • 创建MessageBean.aidl,它用于声明MessageBean.java
    因为AIDL语言的规范就是aidl文件,所以我们必须将MessageBean转为aidl文件,供其它aidl的调用与交互。
package com.example.cxy.testbinder;
parcelable MessageBean; // parcelable是小写
  • 创建IDemandManager.aidl,它是一个接口,规定了客户端与服务端数据传输的接口方法
package com.example.cxy.testbinder;

import com.example.cxy.testbinder.MessageBean;

interface IDemandManager {
    MessageBean getDemand();
    void setDemandIn(in MessageBean msg); // 客户端->服务端
    void setDemandOut(out MessageBean msg); // 服务端->客户端
    void setDemandInOut(inout MessageBean msg); // 客户端<->服务端
}
  • AIDL文件与java文件的区别:

    1. AIDL文件的后缀是 .aidl,java文件的后缀是 .java。
    2. 自定义的Parcelable对象和AIDL对象必须要显式import进来,就算目标文件与当前正在编写的 .aidl 文件在同一个包下,在 Java 中,这种情况是不需要导包的。
    3. AIDL支持的数据类型:

      • Java中的八种基本数据类型:byte,short,int,long,float,double,boolean,char
      • String和CharSequence
      • List:只支持ArrayList,里面每个元素都必须能够被AIDL支持
      • Map:只支持HashMap,里面的每个元素都必须被AIDL支持,包括key和value
      • Parcelable:所有实现了Parcelable接口的对象
      • AIDL:所有的AIDL接口本身也可以在AIDL文件中使用
  • 我们创建IDemandManager接口用来实现传递方法,另外方法内如果有传输载体,就必须指明定向tag(in,out,inout)

    • in : 客户端数据对象流向服务端。服务端对该数据对象的修改不会影响到客户端。
    • out : 数据对象由服务端流向客户端。(客户端传递的数据对象此时服务端收到的对象内容为空,服务端可以对该数据对象修改,并传给客户端)
    • inout : 以上两种数据流向的结合体。(但是不建议用此tag,会增加开销)
  • 注意

    • xxx.aidl文件中不能存在同方法名不同参数的方法。
    • xxx.aidl文件中方法内如果有传输载体,就必须指明定向tag(in,out,inout)
    • 必须在build.gradle文件中android{}方法内指明java和aidl文件路径
      sourceSets {
          main {
              java.srcDirs = ['src/main/java', 'src/main/aidl']
          }
      }
  • 创建AIDLService,它继承了Service,通过new IDemandManager.Stub(){}实现了IDemandManager接口,作为向客户端委托的Binder实例

public class AIDLService extends Service {
    private final String TAG = AIDLService.class.getSimpleName();
    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return demandManager;
    }

    // Stub内部继承Binder,具有跨进程传输能力
    IDemandManager.Stub demandManager = new IDemandManager.Stub() {
        @Override
        public MessageBean getDemand() throws RemoteException {
            MessageBean demand = new MessageBean("首先,看到我要敬礼",1);
            return demand;
        }

        @Override
        public void setDemandIn(MessageBean msg) throws RemoteException { // 客户端数据流向服务端
            Log.e(TAG,"程序员:"+msg.toString());
        }

        @Override
        public void setDemandOut(MessageBean msg) throws RemoteException { // 服务端数据流向客户端
            Log.e(TAG,"程序员:"+msg.toString()); // msg内容一定为空
            msg.setContent("我不想听解释,下班前把所有工作都搞好!");
            msg.setLevel(5);
        }

        @Override
        public void setDemandInOut(MessageBean msg) throws RemoteException {
            Log.e(TAG,"程序员:"+msg.toString());
            msg.setContent("把用户交互颜色都改成粉色");
            msg.setLevel(3);
        }
    };
}

注意IBinder onBind(Intent intent){}一定要返回向客户端委托的Binder实例

  • 最后我们在AndroidManifest.xml文件中注册服务
<service
    android:name=".AIDLService"
    android:exported="true">
    <intent-filter>
        <action android:name="com.example.cxy.testbinder.AIDLService" />
        <category android:name="android.intent.category.DEFAULT" />
    </intent-filter>
</service>

设置了action和category过滤条件用于隐式绑定服务

在魅族的手机上,系统禁止了隐式方法启动服务的权限,所以务必在手机管家/权限管理/ 中,开启该项目的自启权限。

1.5 客户端

  • 拷贝服务端整个aidl文件夹
    将服务端创建的aidl文件夹拷贝至客户端app/src/main 目录下,并且在gradle.build中关联aidl路径。
sourceSets {
    main {
        java.srcDirs = ['src/main/java', 'src/main/aidl']
    }
}
  • 客户端绑定Service
public void BindService(View view) {
    Intent intent = new Intent();
    intent.setAction("com.example.cxy.testbinder.AIDLService");
    intent.setPackage("com.example.cxy.testbinder"); // aidl文件夹里面aidl文件的包名
    bindService(intent,connection, Context.BIND_AUTO_CREATE);
}
  • 关联对象,获得*.aidl接口实例,通过它调用服务端接口方法

服务绑定成功之后,我们通过IDemandManager.Stub.asInterface()方法,将IBinder接口数据转化为我们客户端需要的AIDL接口类型对象。

private ServiceConnection connection = new ServiceConnection() {
    @Override
    public void onServiceConnected(ComponentName name, IBinder service) {
        iDemandManager = IDemandManager.Stub.asInterface(service); // 得到该对象后,我们就可以用来进行进程间的方法调用和传输了
        try {
            Log.e(TAG, String.format("connection: %s", iDemandManager.getDemand().toString()));
        } catch (RemoteException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void onServiceDisconnected(ComponentName name) {

    }
};

至此我们就可以通过AIDL来进行跨进程通讯了。

点击查看源码

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值