三、系统调用/内核态/用户态
抽象来看,操作系统中安全边界的概念就像环路的概念一样(前提是系统支持这种特性),一个环上持有一个特定的权限组,Intel 支持四层环,但是 Linux 只使用了其中的两环(0号环持有全部权限,3号环持有最少权限,1号和2号环未使用),系统进程运行在1号环,用户进程运行在3号环,如果一个用户进程需要其他高级权限,其必须从3号环过渡成0号环,过渡需要通过一个安全参数检查的网关,这种过渡被称为系统调用,在执行过程中会产生一定数量的计算开销。所以,用户空间要访问内核空间的唯一方式就是系统调用(System Call)
四、内核模块/驱动
通过系统调用,用户空间可以访问内核空间,它是怎么做到访问内核空间的呢?Linux的动态可加载内核模块机制解决了这个问题,模块是具有独立功能的程序,它可以被单独编译,但不能独立运行。这样,Android系统可以通过添加一个内核模块运行在内核空间,用户进程之间的通过这个模块作为桥梁,就可以完成通信了。在Android系统中,这个运行在内核空间的,负责各个用户进程通过Binder通信的内核模块叫做Binder驱动
五、简单的总结
将前面的所有概念连接起来理解就会非常好消化知识点:
1.Linux的虚拟内存机制导致内存的隔离,进而导致进程隔离
2.进程隔离的出现导致对内存的操作被划分为用户空间和内核空间
3.用户空间需要跨权限去访问内核空间,必须使用系统调用去实现
4.系统调用需要借助内核模块/驱动去完成
前三步决定了进程间通讯需要借助内核模块/驱动去实现,而Binder驱动就是内核模块/驱动中用来实现进程间通讯的
###为什么要用Binder
Linux提供有管道、消息队列、信号量、内存共享、套接字等跨进程方式,那为什么Android要选择Binder另起炉灶呢?
一、传输性能好
- Socket:是一个通用接口,导致其传输效率低,开销大
- 共享内存:虽然在传输时不需要拷贝数据,但其控制机制复杂
- Binder:复杂数据类型传递可以复用内存,需要拷贝1次数据
- 管道和消息队列:采用存储转发方式,至少需要拷贝2次数据,效率低
二、安全性高
-
传统的进程:通信方式对于通信双方的身份并没有做出严格的验证,只有在上层协议上进行架设
-
Binder机制:从协议本身就支持对通信双方做身份校检,因而大大提升了安全* 性
###Binder通信模型
首先在理解模型之前先熟悉这几个概念: -
Client进程:跨进程通讯的客户端(运行在某个进程)
-
Server进程:跨进程通讯的服务端(运行在某个进程)
Binder驱动:跨进程通讯的介质 -
ServiceManager:跨进程通讯中提供服务的注册和查询(运行在System进程)
这里只是个简单的模型而已,只需理解模型的通讯流程:
1.Server端通过Binder驱动在ServiceManager中注册
2.Client端通过Binder驱动获取ServiceManager中注册的Server端
3.Client端通过Binder驱动和Server端进行通讯
###Binder通信原理
理解完模型流程之后,开始理解模型的通讯原理:
1.Service端通过Binder驱动在ServiceManager的查找表中注册Object对象的add方法
2.Client端通过Binder驱动在ServiceManager的查找表中找到Object对象的add方法,并返回proxy对象的add方法,add方法是个空实现,proxy对象也不是真正的Object对象,是通过Binder驱动封装好的代理类的add方法
3.当Client端调用add方法时,Client端会调用proxy对象的add方法,通过Binder驱动去请求ServiceManager来找到Service端真正对象,然后调用Service端的add方法
###Binder对象和Binder驱动
- Binder对象:Binder机制中进行进程间通讯的对象,对于Service端为Binder本地对象,对于Client端为Binder代理对象
- Binder驱动:Binder机制中进行进程间通讯的介质,Binder驱动会对具有跨进程传递能力的对象做特殊处理,自动完成代理对象和本地对象的转换
由于Binder驱动会对具有跨进程传递能力的对象做特殊处理,自动完成代理对象和本地对象的转换,因此在驱动中保存了每一个跨越进程的Binder对象的相关信息,Binder本地对象(或Binder实体)保存在binder_node的数据结构,Binder代理对象(或Binder引用/句柄)保存在binder_ref的数据结构
###Java层的Binder
- Binder类:是Binder本地对象
- BinderProxy类:是Binder类的内部类,它代表远程进程的Binder对象的本地代理
- Parcel类:是一个容器,它主要用于存储序列化数据,然后可以通过Binder在进程间传递这些数据
- IBinder接口:代表一种跨进程传输的能力,实现这个接口,就能将这个对象进行跨进程传递
- IInterface接口:client端与server端的调用契约,实现这个接口,就代表远程server对象具有什么能力,因为IInterface接口的asBinder方法的实现可以将Binder本地对象或代理对象进行返回
Binder类和BinderProxy类都继承自IBinder,因而都具有跨进程传输的能力,在跨越进程的时候,Binder驱动会自动完成这两个对象的转换。IBinder是远程对象的基本接口,是为高性能而设计的轻量级远程调用机制的核心部分,但它不仅用于远程调用,也用于进程内调用。IBinder接口定义了与远程对象交互的协议,建议不要直接实现这个接口,而应该从Binder派生。Binder实现了IBinder接口,但是一般不需要直接实现此类,而是跟据你的需要由开发包中的工具生成,这个工具叫aidi。你通过aidi语言定义远程对象的方法,然后用aidi工具生成Binder的派生类,然后使用它
###AIDL
由于编译工具会给我们生成一个Stub的静态内部类,这个类继承了Binder, 说明它是一个Binder本地对象,它实现了IInterface接口,表明它具有远程Server承诺给Client的能力
一、服务端
在服务端中,我们只要实现Stub抽象类,和实现其方法即可
private IBinder myS = new IMyAidlInterface.Stub() {
@Override
public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {
}
@Override
public int add(int num1, int num2) throws RemoteException {
Log.i(“Hensen”, “从客户端发来的AIDL请求:num1->” + num1 + “::num2->” + num2);
return num1 + num2;
}
};
二、客户端
在客户端中,可以通过bindService的回调中获取AIDL接口
private ServiceConnection conn = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
iMyAidlInterface = IMyAidlInterface.Stub.asInterface(service);
}
@Override
public void onServiceDisconnected(ComponentName name) {
iMyAidlInterface = null;
}
};
public void add(View view) {
try {
int res = iMyAidlInterface.add(1, 2);
Log.i(“Hensen”, “从服务端调用成功的结果:” + res);
} catch (RemoteException e) {
e.printStackTrace();
}
}
梳理客户端的调用流程:
1.调用Stub.asInterface获取BinderProxy对象
2.调用BinderProxy对象的add方法
三、分析原理
1、Stub
Stub类继承自Binder,意味着这个Stub其实自己是一个Binder本地对象,然后实现了IMyAidlInterface接口,IMyAidlInterface本身是一个IInterface,因此他携带某种客户端需要的能力(这里是方法add)。此类有一个内部类Proxy,也就是Binder代理对象
/*
- This file is auto-generated. DO NOT MODIFY.
- Original file: D:\workspace5\Boke\app\src\main\aidl\com\handsome\boke\IMyAidlInterface.aidl
*/
package com.handsome.boke;
// Declare any non-default types here with import statements
public interface IMyAidlInterface extends android.os.IInterface {
/**
- Local-side IPC implementation stub class.
*/
public static abstract class Stub extends android.os.Binder implements com.handsome.boke.IMyAidlInterface {
private static final java.lang.String DESCRIPTOR = “com.handsome.boke.IMyAidlInterface”;
/**
- Construct the stub at attach it to the interface.
*/
public Stub() {
this.attachInterface(this, DESCRIPTOR);
}
/**
- Cast an IBinder object into an com.handsome.boke.IMyAidlInterface interface,
- generating a proxy if needed.
*/
public static com.handsome.boke.IMyAidlInterface asInterface(android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof com.handsome.boke.IMyAidlInterface))) {
return ((com.handsome.boke.IMyAidlInterface) iin);
}
return new com.handsome.boke.IMyAidlInterface.Stub.Proxy(obj);
}
@Override
public android.os.IBinder asBinder() {
return this;
}
@Override
public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {
switch (code) {
case INTERFACE_TRANSACTION: {
reply.writeString(DESCRIPTOR);
return true;
}
case TRANSACTION_basicTypes: {
data.enforceInterface(DESCRIPTOR);
int _arg0;
_arg0 = data.readInt();
long _arg1;
_arg1 = data.readLong();
boolean _arg2;
_arg2 = (0 != data.readInt());
float _arg3;
_arg3 = data.readFloat();
double _arg4;
_arg4 = data.readDouble();
java.lang.String _arg5;
_arg5 = data.readString();
this.basicTypes(_arg0, _arg1, _arg2, _arg3, _arg4, _arg5);
reply.writeNoException();
return true;
}
case TRANSACTION_add: {
data.enforceInterface(DESCRIPTOR);
int _arg0;
_arg0 = data.readInt();
int _arg1;
_arg1 = data.readInt();
int _result = this.add(_arg0, _arg1);
reply.writeNoException();
reply.writeInt(_result);
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
private static class Proxy implements com.handsome.boke.IMyAidlInterface {
private android.os.IBinder mRemote;
Proxy(android.os.IBinder remote) {
mRemote = remote;
}
@Override
public android.os.IBinder asBinder() {
return mRemote;
}
public java.lang.String getInterfaceDescriptor() {
return DESCRIPTOR;
}
/**
- Demonstrates some basic types that you can use as parameters
- and return values in AIDL.
*/
@Override
public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, java.lang.String aString) throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeInt(anInt);
_data.writeLong(aLong);
_data.writeInt(((aBoolean) ? (1) : (0)));
_data.writeFloat(aFloat);
_data.writeDouble(aDouble);
_data.writeString(aString);
mRemote.transact(Stub.TRANSACTION_basicTypes, _data, _reply, 0);
_reply.readException();
} finally {
_reply.recycle();
_data.recycle();
}
}
@Override
public int add(int num1, int num2) throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
int _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeInt(num1);
_data.writeInt(num2);
mRemote.transact(Stub.TRANSACTION_add, _data, _reply, 0);
_reply.readException();
_result = _reply.readInt();
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
}
总结
写到这里也结束了,在文章最后放上一个小小的福利,以下为小编自己在学习过程中整理出的一个关于Flutter的学习思路及方向,从事互联网开发,最主要的是要学好技术,而学习技术是一条慢长而艰苦的道路,不能靠一时激情,也不是熬几天几夜就能学好的,必须养成平时努力学习的习惯,更加需要准确的学习方向达到有效的学习效果。
由于内容较多就只放上一个大概的大纲,需要更及详细的学习思维导图的
还有高级UI、性能优化、架构师课程、NDK、混合式开发(ReactNative+Weex)微信小程序、Flutter全方面的Android进阶实践技术资料,并且还有技术大牛一起讨论交流解决问题。
《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》,点击传送门,即可获取!
学习效果。
由于内容较多就只放上一个大概的大纲,需要更及详细的学习思维导图的
还有高级UI、性能优化、架构师课程、NDK、混合式开发(ReactNative+Weex)微信小程序、Flutter全方面的Android进阶实践技术资料,并且还有技术大牛一起讨论交流解决问题。**
[外链图片转存中…(img-UnJvKve3-1714873980687)]
《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》,点击传送门,即可获取!