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

}

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

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

四、客户端

将服务端的两个 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”);
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_registerListener = findViewById(R.id.btn_registerListener);
Button btn_unregisterListener = findViewById(R.id.btn_unregisterListener);
Button btn_operation = findViewById(R.id.btn_operation);
View.OnClickListener clickListener = new View.OnClickListener() {
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.btn_registerListener: {
if (iOperationManager != null) {
try {
iOperationManager.registerListener(completedListener);
} catch (RemoteException e) {
e.printStackTrace();
}
}
break;
}
case R.id.btn_unregisterListener: {
if (iOperationManager != null) {
try {
iOperationManager.unregisterListener(completedListener);
} catch (RemoteException e) {
e.printStackTrace();
}
}
break;
}
case R.id.btn_operation: {
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 {
iOperationManager.operation(parameter1, parameter2);
} catch (RemoteException e) {
e.printStackTrace();
}
}
break;
}
}
}
};
btn_registerListener.setOnClickListener(clickListener);
btn_unregisterListener.setOnClickListener(clickListener);
btn_operation.setOnClickListener(clickListener);
}

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

}

运行结果如下所示:

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

六、正确使用 AIDL 回调接口

在上面的代码中我提供了一个按钮用于解除回调函数,但当点击按钮时,Logcat 却会打印出如下信息

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

该回调没有被注册过?但在注册回调函数和解除回调函数时,使用的都是同个对象啊!其实,这是因为回调函数被序列化了的原因,Binder 会把客户端传过来的对象序列化后转为一个新的对象传给服务端,即使客户端使用的一直是同个对象,但对服务端来说前后两个回调函数其实都是两个完全不相关的对象,对象的跨进程传输本质上都是序列化与反序列化的过程

为了能够无误地注册和解除注册回调函数,系统为开发者提供了 RemoteCallbackList,RemoteCallbackList 是一个泛型类,系统专门提供用于删除跨进程回调函数,支持管理任意的 AIDL 接口,因为所有的 AIDL 接口都继承自 IInterface,而 RemoteCallbackList 对于泛型类型有限制

public class RemoteCallbackList

RemoteCallbackList 在内部有一个 ArrayMap 用于 保存所有的 AIDL 回调接口

ArrayMap<IBinder, Callback> mCallbacks = new ArrayMap<IBinder, Callback>();

其中 Callback 封装了真正的远程回调函数,因为即使回调函数经过序列化和反序列化后会生成不同的对象,但这些对象的底层 Binder 对象是同一个。利用这个特征就可以通过遍历 RemoteCallbackList 的方式删除注册的回调函数了
此外,当客户端进程终止后,RemoteCallbackList 会自动移除客户端所注册的回调接口。而且 RemoteCallbackList 内部自动实现了线程同步的功能,所以我们使用它来注册和解注册时,不需要进行线程同步

以下就来修改代码,改为用 RemoteCallbackList 来存储 AIDL 接口

//声明
private RemoteCallbackList callbackList;

注册接口和解除注册接口

@Override
public void registerListener(IOnOperationCompletedListener listener) throws RemoteException {
callbackList.register(listener);
Log.e(TAG, “registerListener 注册回调成功”);
}

@Override
public void unregisterListener(IOnOperationCompletedListener listener) throws RemoteException {
callbackList.unregister(listener);
Log.e(TAG, “unregisterListener 解除注册回调成功”);
}

遍历回调接口

//在操作 RemoteCallbackList 前,必须先调用其 beginBroadcast 方法

最后

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助

因此我收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点!不论你是刚入门Android开发的新手,还是希望在技术上不断提升的资深开发者,这些资料都将为你打开新的学习之门

如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

[外链图片转存中…(img-y6LVjEDN-1715903583582)]

[外链图片转存中…(img-vVNKyOYc-1715903583583)]

[外链图片转存中…(img-Nkdjz5Jj-1715903583584)]

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点!不论你是刚入门Android开发的新手,还是希望在技术上不断提升的资深开发者,这些资料都将为你打开新的学习之门

如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

  • 27
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值