Android面经(14/30)AIDL全解析
牛客高级系列专栏:
安卓(安卓系统开发也要掌握)
- 想通关安卓面试,请看(承诺免费售后答疑):《150道安卓高频面试题目录及答案链接》
- 想通关安卓系统面试,请看:《140道安卓系统Framework面试题目录及答案链接》
- 想进阶安卓开发,请看(承诺免费售后答疑):《Android进阶知识体系解析_15大安卓进阶必备知识点》
- 想了解安卓APP完整开发流程,请看(承诺免费售后答疑):《安卓APP完整开发流程》
- 想掌握安卓App性能优化,请看(承诺免费售后答疑):《安卓性能优化讲解和实战专栏》
- 想掌握Gradle语法和配置,制作Gradle插件,请看(承诺免费售后答疑):《安卓Gradle语法解析和实践大全》
嵌入式
- 想通关嵌入式面试,请看: 《111道嵌入式面试题目录及答案链接》
- 想多掌握几个嵌入式项目,请看:《6个嵌入式项目交流分享(附源码)》
本人是2020年毕业于广东工业大学研究生:许乔丹,有国内大厂CVTE和世界500强企业安卓开发经验,该专栏整理本人对常见安卓高频开发面试题的理解;
网上安卓资料千千万,笔者将继续维护专栏,一杯奶茶价格不止提供答案解析,承诺提供专栏内容免费技术答疑,直接咨询即可。助您提高安卓面试准备效率,为您面试保驾护航!
正文开始⬇
如果需要实现跨进程通讯,AIDL是非常好的选择,在日常开发中接触到AIDL的机会非常多,个人在面试中暂时没被问过,但实际开发中无论是在应用程序中还是系统服务中,都使用过AIDL。我们看看面试官可能会问什么吧:
- 请介绍什么是AIDL⭐⭐⭐⭐⭐
- 有几种AIDL文件?⭐⭐⭐
- 一个程序AIDL文件的数量? ⭐⭐
- 你有没有使用过AIDL?谈谈你如何实现的? ⭐⭐⭐⭐
- 知道AIDL语法中的定向tag吗?⭐⭐⭐
- 你能不能简单描述AIDL实现的原理?⭐⭐⭐
看完以下的解析,一定可以让面试官眼前一亮。
目录
- 1、什么是AIDL
- 1.1 AIDL支持的基础数据
- 1.2 两种AIDL文件
- 1.2.1 定义parcalable对象
- 1.2.2 定义接口
- 1.3 定向Tag
- 2、使用AIDL实现跨进程通讯
- 2.1 AIDL文件的数量
- 2.2 应用程序中使用AIDL
- 2.2.1 parcalable对象对象定义和实现
- 2.2.2 AIDL接口定义文件
- 2.2.3 服务端
- 2.2.4 客户端
- 2.2.5 几个重要类和方法的解析
- 2.2.6 AIDL实现原理
- 2.2.6.1 客户端
- 2.2.6.2 服务端
- 3、系统服务中使用AIDL(引导)
1、什么是AIDL
AIDL全称Android Interface Definition Language,也就是Android接口定义语言。因为AIDL的文件内容都是只有定义,而没有真正的实现。设计了这门语言是为了方便实现跨进程通讯,AIDL文件的后缀是“.aidl“。
当然AIDL只是一种辅助工具,可以让我们少写代码同时少出错。如果不用AIDL,一样可以实现跨进程通讯,只是比较麻烦。AIDL 的原理是Binder,真正有跨进程通讯能力的也是 Binder。
1.1 AIDL支持的基础数据
AIDL作为跨进程的方式之一,数据传输功能并不可少,AIDL默认支持的数据类型有:
- Java中的八种基本数据类型,包括 int、long、float、double、byte,short,boolean,char
- String 类型
- CharSequence类型
- List类型:List中的所有元素必须是AIDL支持的类型之一,或者是一个其他AIDL生成的接口,或者是AIDL是定义的parcelable对象(见1.2小节),List可以使用泛型
- Map类型:Map中的所有元素必须是AIDL支持的类型之一,或者是一个其他AIDL生成的接口,或者是AIDL是定义的parcelable对象(见1.2小节),Map是不支持泛型
1.2 两种AIDL文件
1.2.1 定义parcalable对象
如果传输的对象是自定义的类,难道AIDL无法支持?显然官方也考虑到该问题,AIDL第一种文件类型是用于定义parcelable对象(AIDL 只能传送继承 Parcelable 接口的类)。如需要传输一个Person类,需要在同个包下定义以下2个文件:
- Step1:需要先定义Person.aidl文件
package com.android.aidl.demo;
// Declare any non-default types here with import statements
// parcelable使用小写字母
parcelable Person;
- Step2:然后再编写Person的实现类:
public class Person implements Parcelable {
public int id;
public String name;
public Person(int id, String name) {
this.id = id;
this.name = name;
}
...
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(id);
dest.writeString(name);
}
}
1.2.2 定义接口
AIDL另一种文件用于定义方法接口,以提供给其他进程调用本进程的能力。
// IMyService.aidl
package com.android.aidl.demo;
import com.android.aidl.demo.Person; // 1
// Declare any non-default types here with import statements
interface IMyService {
void addPerson(in Person person); // 2
List<Person> getPersonList();
}
其中,有一个知识点,Person实现类Person.java文件和定义接口的IMyService.aidl文件虽然在同个包里,但IMyService.aidl文件还是必须手动导入Person类,如[注释1]
所示。
编写IMyService.aidl文件后,在Android Studio编译一下工程,就会在下图路径自动编程生成IMyService.java文件:
1.3 定向Tag
仔细看[注释2]
,会发现相比于普通函数参数,这里多了一个‘in’符号,这是AIDL的语法之一,称之为“定向Tag”,共包含3种定向Tag:in、out、inout。这三个tag是用于控制客户端和服务端数据流向,且数据流向的主体以客户端传入服务端的对象为标准。具体看看如下代码:
void addPerson(in Person person);
void addPerson(out Person person);
void addPerson(inout Person person);
//调用
Person person = new Person("xurui");
addPerson(person);
此时数据流向的主体就是Person对象,在此假设Person对象里面就一个字符变量“name”,三个tag具体作用如下:
- in:数据只能从客户端流向服务端,服务端会收到客户端传入的一个完整的对象,即收到一个Person对象,对象内部的name属性的值为“xurui”,但此时服务端对该Person对象进行的修改不会同步返回到客户端;
- out:数据只能由服务端流向客户端,服务端只能收到客户端传入的对象的空值对象,即服务端收到的Person对象里面的name属性是空值,而非“xurui”,但服务端对接收到的空对象的任何修改都会同步返回到客户端;
- inout:数据在客户端和服务端双向流通;服务端可以收到客户端传入一个Person对象,里面的name属性数值为“xurui”,此时服务端对该对象做的任何修改也会同步返回给客户端。
2、使用AIDL实现跨进程通讯
在日常开发中,一般使用到AIDL的情况就是自定义了一个Service,想提供该Service的能力供其他进程或者其他模块调用。而自定义Service最常见的方法有两种:
- 通过Android Studio创建一个包含Service的应用程序;
- 创建自定义系统服务;
所以本节以创建自定义应用程序和自定义系统服务为例,讲解如果AIDL的使用方法。
2.1 AIDL文件的数量
在实现跨进程通讯时,如果AIDL文件仅包含AIDL所支持的基础数据类型,则只需要1个AIDL文件。但如果像1.2.1小节的图片所示,需要支持自定义的Person类,则需要1+1=2个AIDL文件,即有N个非默认支持的基础数据类型,就需要N+1个AIDL文件。
2.2 应用程序中使用AIDL
在应用程序中使用AIDL,常见的就是一个Activity绑定一个Service,并调用Service提供的函数方法。无论Activity和Service在同个应用程序,还是分别在两个应用程序,对AIDL的使用都是大同小异的。不过有一点要注意,如果是同在一个应用程序,那么只需要一份AIDL文件。如果Activity和Service在两个程序,则两个程序各要有一份AIDL文件。我们以同在一个应用程序进行讲解。主要包括四大部分:parcalable对象定义和实现、AIDL接口定义文件、服务端、客户端;
2.2.1 parcalable对象对象定义和实现
本案例程序框架图和parcalable对象定义和实现(Person类),都在1.2.1小节详细说过,这里不重复叙述。
2.2.2 AIDL接口定义文件
本案例AIDL接口定义文件也在1.2.2小节介绍了,即IMyService.aidl文件,这里也不重复。
2.2.3 服务端
直接上代码:
public class MyPersonServer extends Service {
class MyServiceNative extends IMyService.Stub { //3
List<Person> myPersonList = new ArrayList<>();
@Override
public void addPerson(Person person) {
myPersonList.add(person);
}
@Override
public List<Person> getPersonList() {
return myPersonList;
}
@Override
public boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
return super.onTransact(code, data, reply, flags);
}
}
private MyServiceNative mMyServiceNative = new MyServiceNative();
@Override
public IBinder onBind(Intent intent) {
return mMyServiceNative; //4
}
}
在MyPersonServer.java代码中,主要做了两件事。
- 在
[注释3]
创建MyServiceNative内部类继承了Android Studio自动生成的IMyService.java文件里的Stub类,并重新实现了IMyService.aidl文件定义的两个接口addPerson()和getPersonList(); - 在
[注释4]
,通过onBind()返回MyServiceNative的实例。
2.2.4 客户端
客户端代码就在Activity里实现:
public class ClientActivity extends AppCompatActivity implements View.OnClickListener {
Button btnBind;
Button btnAddPerson;
Button btnGetSize;
TextView tvResult;
IMyService myService;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btnBind = (Button) findViewById(R.id.btn_bind);
btnAddPerson = (Button) findViewById(R.id.btn_add_person);
btnGetSize = (Button) findViewById(R.id.btn_get_size);
btnBind.setOnClickListener(this);
btnAddPerson.setOnClickListener(this);
btnGetSize.setOnClickListener(this);
tvResult = (TextView) findViewById(R.id.txt_result);
}
@Override
protected void onDestroy() {
unbindService(mConn);
super.onDestroy();
}
private ServiceConnection mConn = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
myService = IMyService.Stub.asInterface(service); // 5
}
@Override
public void onServiceDisconnected(ComponentName name) {
myService = null;
}
};
@Override
public void onClick(View v) {
switch (v.getId()) {
剩余60%内容,订阅专栏后可继续查看/也可单篇购买
#提供免费售后答疑!!花一杯奶茶的钱获得安卓面试答疑服务,稳赚不赔# Android发展已经很多年,安卓资料网上千千万,本专栏免费提供专栏内容技术答疑!!私聊当天必回。在阅读过程或者其他安卓学习过程有疑问,都非常欢迎私聊交流。