互联网大厂对于 “Binder” 会问什么?会问的有多深?
我相信大家和我一样都很好奇。虽然“Binder”中的很多技术一部分只有互联网大厂中项目才能用的到,但是不甘于当咸鱼的我还是对BAT怀揣着向往,想在明年的春招中一举拿下 offer,所以年末的复习总结主要是以面试相关的技术知识点,和我有同样想法的朋友,可以参考我在Github花几个月整理 《Android面试核心知识点》。
面试参考
本篇,主要以我整理的面试题的方式来讲解 Binder机制,如觉写的可以,请留下你的点赞。
- 分析进程间通信方式
- binder里面的proxy类采用了动态代理。核心方法:mRotate.transact(Stub.TRANSACTION_xxx,_data,_reply,flags);
- Binder有什么优势?(字节跳动真题)
- Binder是如何做到一次拷贝的?(腾讯真题)
- MMAP的原理讲解;(腾讯真题)
- 描述AIDL生成的Java类细节;(字节跳动真题)
- 四大组件底层的通信机制;(字节跳动真题)
- 为什么Intent不能传递大数据?(阿里真题)
进程间通信方式
进程间通信方式有蛮多种,比如通过共享内存、Socket、管道啊等等,但是我们作为安卓开发并不会采用这些(而且这块我也不懂,后续如果需要再研究呗),安卓中使用的是binder。至于区别,如下图。
用binder的优势:
- 1.拷贝只需要一次,虽然有不需要拷贝的通信机制,但是就会涉及到信息资源时刻同步,这个是很耗性能的,而且控制极其复杂,易用性也差
- 2.安全,因为为每个app分配UID(可以看作是应用的身份证)。
内存划分
想知道怎么传输数据的,需要先了解内存划分
Binder与传统IPC如何传输数据的呢?
binder与传统ipc(进程间通信机制)对比
传统ipc传输数据流程
一次拷贝:服务端一块空间(虚拟内存)跟内核空间(虚拟内存)通过MMP映射到同一块物理内存
无需拷贝:发送端、接收端、共享区域映射到同一块物理内存.(会涉及到各种信息资源同步,导致控制复杂、易用性差)
上层通过AIDL、底层通过Binder实现安卓端的进程通信,AIDL实现进程间通信流程
哈哈,这张图是从我自己画的,感觉诠释进程间通信很透彻了,上层是AIDL,底层是Binder,客户端的proxy类发送数据,通过ServiceManager(本身也是Service)类去找到合适的服务端Service,一个服务(Service)就需要对应一个AIDL,服务端的Service的Stub接收数据,然后服务端的Proxy发送数据到客户端等\color{#FF0000}{上层是AIDL,底层是Binder,客户端的proxy类发送数据,通过ServiceManager(本身也是Service)类去找到合适的服务端Service,一个服务(Service)就需要对应一个AIDL,服务端的Service的Stub接收数据,然后服务端的Proxy发送数据到客户端等}上层是AIDL,底层是Binder,客户端的proxy类发送数据,通过ServiceManager(本身也是Service)类去找到合适的服务端Service,一个服务(Service)就需要对应一个AIDL,服务端的Service的Stub接收数据,然后服务端的Proxy发送数据到客户端等 整条线路就完整了。
客户端如何拿到AIDL句柄,通过这个句柄如何调用服务端,服务端怎么处理
Demo实例
- 1.工程结构
服务端
客户端
- 2.代码
如何新建aidl文件我就不说了,这个是基础。
服务端
//1.ILiaoAidlInterface
package com.xm.aidl_server;
// Declare any non-default types here with import statements
import com.xm.aidl_server.Person;
interface ILiaoAidlInterface {
List<Person> getPersonList();
void addPerson(in Person person);
}
--------------------------------
//2.Person aidl文件
package com.xm.aidl_server;
parcelable Person;
-------------------
//3.Person对应的实体类
package com.xm.aidl_server;
import android.os.Parcel;
import android.os.Parcelable;
//1.Android开发:什么是Parcel? http://blog.csdn.net/nkmnkm/archive/2011/05/28/6451699.aspx
//2.android开发之Parcelable使用详解 https://blog.csdn.net/u012702547/article/details/47151001/
public class Person implements Parcelable {
private String name;
private int grade;
public Person(String name, int grade) {
this.name = name;
this.grade = grade;
}
protected Person(Parcel in) {
this.name = in.readString();
this.grade = in.readInt();
}
public static final Creator<Person> CREATOR = new Creator<Person>() {
@Override
public Person createFromParcel(Parcel in) {
return new Person(in);
}
@Override
public Person[] newArray(int size) {
return new Person[size];
}
};
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(name);
dest.writeInt(grade);
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", grade=" + grade +
'}';
}
}
-------------------------
//4.服务端app对应的服务(Service)类
package com.xm.aidl_server;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;
import java.util.ArrayList;
import java.util.List;
import androidx.annotation.Nullable;
public class MyService extends Service {
private List<Person> personList;
@Nullable
@Override
public IBinder onBind(Intent intent) {
personList = new ArrayList<>();
return iBinder;
}
private IBinder iBinder = new ILiaoAidlInterface.Stub() {
@Override
public List<Person> getPersonList() throws RemoteException {
return personList;
}
@Override
public void addPerson(Person person) throws RemoteException {
personList.add(person);
}
};
}
--------------------------------
//5.启动服务端app的Service
public class MainActivity extends AppCompatActivity {
private Intent intent;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
intent = new Intent(this, MyService.class);
startService(intent);
}
@Override
protected void onDestroy() {
super.onDestroy();
stopService(intent);
}
}
上面就是服务端所有代码了,本来想截图放的 但是担心观看这篇博客的人能快速上手就都贴出来了。
客户端
这块代码是从服务端拷贝过来的,要想实现进程间通信,这块代码必须保持一致!!!
Button btn = findViewById(R.id.btn);
//1.第一步 绑定服务端service
bindService();
//第二步 设置按钮点击事件
btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
try {
iLiaoAidlInterface.addPerson(new Person("姓名",7));
List<Person> personList = iLiaoAidlInterface.getPersonList();
Log.i("MainActivity", "onClick: "+personList.toString());
} catch (RemoteException e) {
e.printStackTrace();
}
}
});
---------------------------------------
//2.bindService()方法
private void bindService() {
Intent intent = new Intent();
//todo 为啥不能直接通过intent带参数 为啥非得这种方式 全包名、类名
intent.setComponent(new ComponentName("com.xm.aidl_server","com.xm.aidl_server.MyService"));
//todo 三个参数都得介绍
//service 指的是服务端service
//
//参数三:flags: 为0,表示客户端可以发送到服务端,服务端也可以返回 为1,表示客户端发送到服务端,但是服务端不可以返回
//_data:是客户端发送到服务端 _reply:是服务端可以发送到客户端
bindService(intent,connection,0);
}
----------------------------------------------
// 3.内部类connection
ServiceConnection connection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
//todo
iLiaoAidlInterface = ILiaoAidlInterface.Stub.asInterface(service);
}
@Override
public void onServiceDisconnected(ComponentName name) {
//todo
iLiaoAidlInterface = null;
}
};
输出
//场景1:服务端Service如果没有开启 点击客户端按钮 日志如下
2020-12-26 23:42:32.821 10001-10001/com.xm.aidl_client W/System.err: at com.xm.aidl_client.MainActivity$1.onClick(MainActivity.java:33)
2020-12-26 23:42:54.203 10001-10001/com.xm.aidl_client W/System.err: at com.xm.aidl_client.MainActivity$1.onClick(MainActivity.java:33)
2020-12-26 23:43:08.919 10001-10001/com.xm.aidl_client W/System.err: at com.xm.aidl_client.MainActivity$1.onClick(MainActivity.java:33)
-----------------------------------------------------------
//场景2:服务端Service如果开启 点击客户端按钮 日志如下
2020-12-26 23:44:59.928 13635-13635/com.xm.aidl_client I/MainActivity: onClick: [Person{name='姓名', grade=7}]
2020-12-26 23:45:01.761 13635-13635/com.xm.aidl_client I/MainActivity: onClick: [Person{name='姓名', grade=7}, Person{name='姓名', grade=7}]
2020-12-26 23:45:04.672 13635-13635/com.xm.aidl_client I/MainActivity: onClick: [Person{name='姓名', grade=7}, Person{name='姓名', grade=7}, Person{name='姓名', grade=7}]
A进程访问B进程时的几种状态
- 1.进程B未启动
- 2.进程B启动了,但是Service没有创建
- 3.进程B启动了,Service创建了,但是Service没有绑定过,回调onBind()
- 4.进程B启动了,Service创建了,但是Service绑定过,回调onRebind()