底层学习---Android-IPC机制(三)AIDL(1)

二、语法

AIDL 的语法十分简单,与Java语言基本保持一致,需要记住的规则有以下几点:

  1. AIDL文件以 .aidl 为后缀名
  2. AIDL支持的数据类型分为如下几种:
  • 八种基本数据类型:byte、char、short、int、long、float、double、boolean
  • String,CharSequence
  • 实现了Parcelable接口的数据类型
  • List 类型。List承载的数据必须是AIDL支持的类型,或者是其它声明的AIDL对象
  • Map类型。Map承载的数据必须是AIDL支持的类型,或者是其它声明的AIDL对象
  1. AIDL文件可以分为两类。一类用来声明实现了Parcelable接口的数据类型,以供其他AIDL文件使用那些非默认支持的数据类型。还有一类是用来定义接口方法,声明要暴露哪些接口给客户端调用,定向Tag就是用来标注这些方法的参数值
  2. 定向Tag。定向Tag表示在跨进程通信中数据的流向,用于标注方法的参数值,分为 in、out、inout 三种。其中 in 表示数据只能由客户端流向服务端, out 表示数据只能由服务端流向客户端,而 inout 则表示数据可在服务端与客户端之间双向流通。此外,如果AIDL方法接口的参数值类型是:基本数据类型、String、CharSequence或者其他AIDL文件定义的方法接口,那么这些参数值的定向 Tag 默认是且只能是 in,所以除了这些类型外,其他参数值都需要明确标注使用哪种定向Tag
  3. 明确导包。在AIDL文件中需要明确标明引用到的数据类型所在的包名,即使两个文件处在同个包名下

现在,我来模拟一种 IPC 的流程
服务端**(com.czy.aidl_server)向外提供了进行数学计算的能力(其实就是对两个整数进行相乘)。客户端(com.czy.aidl_client)**需要进行计算时就将数据(包含了一个整数值的序列化类)传递给服务端进行运算,运算结果会返回给客户端。注意,服务端和客户端是两个不同的应用,因此自然也是处于不同的进程中,以此来进行 IPC

三、服务端

服务端是提供运算操作能力的一方,所以除了需要设定运算参数的格式外,还需要提供运算方法
此处,用 Parameter 类作为运算参数

/**

  • 作者:leavesC
  • 时间:2019/4/4 10:46
  • 描述:包含一个进行运算操作的 int 类型数据
    */
    public class Parameter implements Parcelable {

private int param;

public Parameter(int param) {
this.param = param;
}

public int getParam() {
return param;
}

public void setParam(int param) {
this.param = param;
}

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

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

protected Parameter(Parcel in) {
this.param = in.readInt();
}

public static final Parcelable.Creator CREATOR = new Parcelable.Creator() {
@Override
public Parameter createFromParcel(Parcel source) {
return new Parameter(source);
}

@Override
public Parameter[] newArray(int size) {
return new Parameter[size];
}
};

}

相对应的 AIDL 文件

package leavesc.hello.aidl_server;

parcelable Parameter;

此外,还需要一个向外暴露运算方法的 AIDL 接口

package leavesc.hello.aidl_server;

import leavesc.hello.aidl_server.Parameter;

interface IOperationManager {

//接收两个参数,并将运算结果返回给客户端
Parameter operation(in Parameter parameter1 , in Parameter parameter2);

}

然后,在 Service 中进行实际的运算操作,并将运算结果返回

/**

  • 作者:叶应是叶
  • 时间:2018/3/18 17:35
  • 描述:https://github.com/leavesC
    */
    public class AIDLService extends Service {

private static final String TAG = “AIDLService”;

private IOperationManager.Stub stub = new IOperationManager.Stub() {
@Override
public Parameter operation(Parameter parameter1, Parameter parameter2) throws RemoteException {
Log.e(TAG, “operation 被调用”);
int param1 = parameter1.getParam();
int param2 = parameter2.getParam();
return new Parameter(param1 * param2);
}
};

public AIDLService() {
}

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

}

这样,服务端的接口就设计好了,文件目录如下所示

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

四、客户端

将服务端的两个 AILD 文件以及 Parameter 类复制到客户端,保持文件路径(包名)不变

文件目录如下所示

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

指定服务端的包名和 Service 路径,绑定服务,向其传递两个待运算参数并将运算结果展示出来

/**

  • 作者:叶应是叶
  • 时间:2018/3/18 17:51
  • 描述:https://github.com/leavesC
  • 客户端
    */
    public class MainActivity extends AppCompatActivity {

private EditText et_param1;

private EditText et_param2;

private EditText et_result;

private IOperationManager iOperationManager;

private ServiceConnection serviceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
iOperationManager = IOperationManager.Stub.asInterface(service);
}

@Override
public void onServiceDisconnected(ComponentName name) {
iOperationManager = null;
}
};

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
bindService();
}

private void bindService() {
Intent intent = new Intent();
intent.setClassName(“com.czy.aidl_server”, “com.czy.aidl_server.AIDLService”);
bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE);
}

private void initView() {
et_param1 = findViewById(R.id.et_param1);
et_param2 = findViewById(R.id.et_param2);
et_result = findViewById(R.id.et_result);
Button btn_operation = findViewById(R.id.btn_operation);
btn_operation.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (TextUtils.isEmpty(et_param1.getText()) || TextUtils.isEmpty(et_param2.getText())) {
return;
}
int param1 = Integer.valueOf(et_param1.getText().toString());
int param2 = Integer.valueOf(et_param2.getText().toString());
Parameter parameter1 = new Parameter(param1);
Parameter parameter2 = new Parameter(param2);
if (iOperationManager != null) {
try {
Parameter resultParameter = iOperationManager.operation(parameter1, parameter2);
et_result.setText("运算结果: " + resultParameter.getParam());
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
});
}

@Override
protected void onDestroy() {
super.onDestroy();
if (serviceConnection != null) {
unbindService(serviceConnection);
}
}

}

运行结果如下所示

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

可以看到,得到了正确的运算结果了,这就完成了一次简单的 IPC :客户端将参数传递给了服务端,服务端接收参数并进行计算,并将计算结果返回给客户端

五、注册回调函数

在上一节的例子里的运算操作只是将参数进行乘法操作,当然能够很快获得返回值,但如果是要进行耗时操作,那这种方式就不太合适了,所以可以以注册回调函数的方式来获取运算结果。即客户端向服务端注册一个回调函数用于接收运算结果,而不用傻乎乎地一直等待返回值

因此,首先需要先声明一个 AIDL 接口 IOnOperationCompletedListener,用于传递运算结果

package com.czy.aidl_server;

import com.czy.aidl_server.Parameter;

interface IOnOperationCompletedListener {

void onOperationCompleted(in Parameter result);

}

IOperationManager 的**operation** 方法改为无返回值,新增注册回调函数和解除注册函数的方法

package com.czy.aidl_server;

import com.czy.aidl_server.Parameter;
import com.czy.aidl_server.IOnOperationCompletedListener;

interface IOperationManager {

void operation(in Parameter parameter1 , in Parameter parameter2);

void registerListener(in IOnOperationCompletedListener listener);

void unregisterListener(in IOnOperationCompletedListener listener);

}

operation 方法中让线程休眠五秒,模拟耗时操作,然后再将运算结果传递出去

/**

  • 作者:叶应是叶
  • 时间:2018/3/18 17:35
  • 描述:https://github.com/leavesC
    */
    public class AIDLService extends Service {

private static final String TAG = “AIDLService”;

private CopyOnWriteArrayList copyOnWriteArrayList;

private IOperationManager.Stub stub = new IOperationManager.Stub() {
@Override
public void operation(Parameter parameter1, Parameter parameter2) throws RemoteException {
try {
Log.e(TAG, “operation 被调用,延时5秒,模拟耗时计算”);
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
int param1 = parameter1.getParam();
int param2 = parameter2.getParam();
Parameter result = new Parameter(param1 * param2);
for (IOnOperationCompletedListener listener : copyOnWriteArrayList) {
listener.onOperationCompleted(result);
}
Log.e(TAG, “计算结束”);
}

@Override
public void registerListener(IOnOperationCompletedListener listener) throws RemoteException {
Log.e(TAG, “registerListener”);
if (!copyOnWriteArrayList.contains(listener)) {
Log.e(TAG, “注册回调成功”);
copyOnWriteArrayList.add(listener);
} else {
Log.e(TAG, “回调之前已注册”);
}
}

@Override
public void unregisterListener(IOnOperationCompletedListener listener) throws RemoteException {
Log.e(TAG, “unregisterListener”);
if (copyOnWriteArrayList.contains(listener)) {
copyOnWriteArrayList.remove(listener);
Log.e(TAG, “解除注册回调成功”);
} else {
Log.e(TAG, “该回调没有被注册过”);
}
}
};

public AIDLService() {
copyOnWriteArrayList = new CopyOnWriteArrayList<>();
}

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

}

客户端这边一样要修改相应的 AIDL 文件
新增两个按钮用于注册和解除注册回调函数,并在回调函数中展示运算结果

/**

  • 作者:叶应是叶
  • 时间:2018/3/18 17:51
  • 描述:https://github.com/leavesC
  • 客户端
    */
    public class MainActivity extends AppCompatActivity {

private static final String TAG = “MainActivity”;

private EditText et_param1;

private EditText et_param2;

private EditText et_result;

private IOperationManager iOperationManager;

private IOnOperationCompletedListener completedListener = new IOnOperationCompletedListener.Stub() {
@Override
public void onOperationCompleted(Parameter result) throws RemoteException {
et_result.setText("运算结果: " + result.getParam());
}
};

private ServiceConnection serviceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
iOperationManager = IOperationManager.Stub.asInterface(service);
}

@Override
public void onServiceDisconnected(ComponentName name) {
iOperationManager = null;
}
};

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
bindService();
}

private void bindService() {
Intent intent = new Intent();
intent.setClassName(“com.czy.aidl_server”, “com.czy.aidl_server.AIDLService”);

结尾

最后小编想说:不论以后选择什么方向发展,目前重要的是把Android方面的技术学好,毕竟其实对于程序员来说,要学习的知识内容、技术有太多太多,要想不被环境淘汰就只有不断提升自己,从来都是我们去适应环境,而不是环境来适应我们!

当程序员容易,当一个优秀的程序员是需要不断学习的,从初级程序员到高级程序员,从初级架构师到资深架构师,或者走向管理,从技术经理到技术总监,每个阶段都需要掌握不同的能力。早早确定自己的职业方向,才能在工作和能力提升中甩开同龄人。

想要拿高薪实现技术提升薪水得到质的飞跃。最快捷的方式,就是有人可以带着你一起分析,这样学习起来最为高效,所以为了大家能够顺利进阶中高级、架构师,我特地为大家准备了一套高手学习的源码和框架视频等精品Android架构师教程,保证你学了以后保证薪资上升一个台阶。

当你有了学习线路,学习哪些内容,也知道以后的路怎么走了,理论看多了总要实践的。

高级UI,自定义View

UI这块知识是现今使用者最多的。当年火爆一时的Android入门培训,学会这小块知识就能随便找到不错的工作了。

不过很显然现在远远不够了,拒绝无休止的CV,亲自去项目实战,读源码,研究原理吧!

《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》点击传送门,即可获取!
、架构师,我特地为大家准备了一套高手学习的源码和框架视频等精品Android架构师教程,保证你学了以后保证薪资上升一个台阶。

当你有了学习线路,学习哪些内容,也知道以后的路怎么走了,理论看多了总要实践的。

[外链图片转存中…(img-vZS1qctQ-1715135734405)]

高级UI,自定义View

UI这块知识是现今使用者最多的。当年火爆一时的Android入门培训,学会这小块知识就能随便找到不错的工作了。

不过很显然现在远远不够了,拒绝无休止的CV,亲自去项目实战,读源码,研究原理吧!

[外链图片转存中…(img-tjVGEzP3-1715135734406)]

《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》点击传送门,即可获取!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值