前言

本故事纯属虚构,如有不通顺的逻辑请轻喷。️

《犬夜叉2021》

第一章:我还能找到你吗,阿篱

犬夜叉和奈落大决战之后,四魂之玉、食骨之井消失,谁也不知道去了哪,而犬夜叉和阿篱再次被分割到两个世界

于是犬夜叉拜托一位研究世界宇宙的法师——积木,来帮助他找到阿篱。

时间转眼来到了2021年,积木终于发现了这个世界的秘密。。

其实我们所在的整个宇宙叫做Android宇宙,犬夜叉和阿篱所处的两个世界其实是两个进程,两个世界可以通过食骨之井相连接。

所以想让犬夜叉重新联系到阿篱,必须再找到当年的食骨之井。

第二章:食骨之井改名Binder井?

“犬夜叉,我终于找到了”

“找到什么了?是阿篱吗?阿篱找到了????”

“没有,不过我找到了关键的东西——食骨之井”

“在哪,快带我去”

于是,积木法师带着犬夜叉来到一间屋子里:

这间屋子门面上写着《内核屋》三个大字,犬夜叉一个箭步飞了进去,在里面果然找到了当年那个食骨之井,但是又有点不一样,因为它被改了名,旁边一个破碎的板子上写着——Binder井。板子上还同时刻有使用说明:

Binder井

这口井联系这两个世界,你看到的也许不是真实的,请慎用!

如需使用,请找到当年遗落的四魂之玉,现在它叫SM之玉(ServiceManager)

找到SM之玉,心里默念你想联系的那个世界那个人,如果她在那个世界的SM之玉碎片中留下了地址,那么你就能找到她。

“积木法师,你知道SM之玉吗,哪里可以找到它”,犬夜叉问到。

第三章:四魂之玉——ServiceManager

“说到SM之玉,还要从宇宙的起源说起,Android宇宙创建初期,诞生了第一个有人的世界(用户进程),叫做Init世界,而SM之玉就是由这个世界创建的。

SM之玉创建后,启动了Binder井,成为了他的守护神。

但是它的真身存在于单独的世界中,无法获得。为了让人们能够使用到它,它特意在每个世界都留下了自己的碎片(代理)。”

“在哪在哪,快告诉我”。

“第0街道(句柄值固定为0)”,积木法师指了一个方向说到。

第四章:阿篱,我想你了

犬夜叉急忙去第0街道找到了SM之玉的碎片,然后回到Binder井旁边,心里默念道:



SM之玉,

求求你帮我找到阿篱吧。

忽然,Binder井刮出一阵狂风,一个虚影出现在了犬夜叉的面前。

是阿篱~

“阿篱,你能听到我说话吗?”

“犬夜叉,我能听到,没想到还能看到你”,阿篱的虚影说到。

“我想你了,阿篱...”

故事End

故事结束了。

再帮大家理一下故事梗概,其实也就是Binder的工作流程:

  • 阿篱(服务端) 为了让犬夜叉(客户端) 找到她,在四魂之玉(ServiceManager) 上留下了他们世界(进程) 的地址。
  • 犬夜叉在第0街道(句柄为0) 找到了四魂之玉碎片(ServiceManager代理)
  • 通过四魂之玉碎片,犬夜叉看到了阿篱的虚影(服务端代理),并通过虚影告诉了阿篱,想她了(通信)。

当然,故事毕竟是故事,并不能完全说清楚。

所以下面就完整看看Binder的工作流程和原理~

代码实现犬夜叉的需求

首先,我们使用AIDL来实现刚才故事中的场景——让犬夜叉和阿篱两个不同进程的人说上话:

//IMsgManager.aidl

interface IMsgManager {
String getMsg();
void tell(in String msg);
} //阿篱
public class AliWorldService extends Service { private static final String TAG = "lz1"; @Nullable
@Override
public IBinder onBind(Intent intent) {
return mBinder;
} private Binder mBinder = new IMsgManager.Stub() {
@Override
public String getMsg() throws RemoteException {
String tellMsg="犬夜叉...是我";
Log.e(TAG, "阿篱:" + tellMsg);
return tellMsg;
} @Override
public void tell(String msg) throws RemoteException {
Log.e(TAG, "我是阿篱,我收到了你说的:" + msg);
}
};
} <service
android:name=".binder.AliWorldService"
android:process=":aliworld">
</service> //犬夜叉
public class QycWorldActivity extends Activity {
private static final String TAG = "lz1"; @Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_qyc); Intent i = new Intent(this, AliWorldService.class);
bindService(i, mConnection, Context.BIND_AUTO_CREATE);
} private ServiceConnection mConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
IMsgManager msgManager = IMsgManager.Stub.asInterface(service);
try {
String tellMsg="阿篱,是你吗";
Log.e(TAG, "犬夜叉:" + tellMsg);
msgManager.tell(tellMsg); String msg = msgManager.getMsg();
Log.e(TAG, "我是犬夜叉,我收到了你说的:" + msg); } catch (RemoteException e) {
e.printStackTrace();
}
} @Override
public void onServiceDisconnected(ComponentName name) { }
}; @Override
protected void onDestroy() {
super.onDestroy();
unbindService(mConnection);
} }

运行,打印结果:

E/lz1: 犬夜叉:阿篱,是你吗
E/lz1: 我是阿篱,我收到了你说的:阿篱,是你吗
E/lz1: 阿篱:犬夜叉...是我
E/lz1: 我是犬夜叉,我收到了你说的:犬夜叉...是我

AIDL原理

代码比较简单,服务器端新建一个Binder对象并传到onBind方法中,客户端bindservice之后,获取到服务端的代理接口,就可以进行方法的调用了。

AIDL其实只是一个帮助我们实现进程间通信的工具,它会根据我们写的AIDL文件代码,生成相应的java接口代码,其内部也是通过Binder实现的。

我们可以通过build——generated——aidl_source_output_dir——debug——out文件路径找到AIDL为我们生成的接口类。代码如下:

public interface IMsgManager extends android.os.IInterface {

    public static abstract class Stub extends android.os.Binder implements com.example.studynote.binder.IMsgManager {

        //1
private static final java.lang.String DESCRIPTOR = "com.example.studynote.binder.IMsgManager"; public Stub() {
this.attachInterface(this, DESCRIPTOR);
} //2
public static com.example.studynote.binder.IMsgManager asInterface(android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof com.example.studynote.binder.IMsgManager))) {
return ((com.example.studynote.binder.IMsgManager) iin);
}
return new com.example.studynote.binder.IMsgManager.Stub.Proxy(obj);
} @Override
public android.os.IBinder asBinder() {
return this;
} //4
@Override
public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {
java.lang.String descriptor = DESCRIPTOR;
switch (code) {
case INTERFACE_TRANSACTION: {
reply.writeString(descriptor);
return true;
}
case TRANSACTION_getMsg: {
data.enforceInterface(descriptor);
java.lang.String _result = this.getMsg();
reply.writeNoException();
reply.writeString(_result);
return true;
}
case TRANSACTION_tell: {
data.enforceInterface(descriptor);
java.lang.String _arg0;
_arg0 = data.readString();
this.tell(_arg0);
reply.writeNoException();
return true;
}
default: {
return super.onTransact(code, data, reply, flags);
}
}
} private static class Proxy implements com.example.studynote.binder.IMsgManager {
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;
} //3
@Override
public java.lang.String getMsg() throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
java.lang.String _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
mRemote.transact(Stub.TRANSACTION_getMsg, _data, _reply, 0);
_reply.readException();
_result = _reply.readString();
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
} @Override
public void tell(java.lang.String msg) 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.writeString(msg);
mRemote.transact(Stub.TRANSACTION_tell, _data, _reply, 0);
_reply.readException();
} finally {
_reply.recycle();
_data.recycle();
}
}
} static final int TRANSACTION_getMsg = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_tell = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
} public java.lang.String getMsg() throws android.os.RemoteException; public void tell(java.lang.String msg) throws android.os.RemoteException;
}

代码比较长,我们依次来分析下:

  • DESCRIPTOR。Binder的唯一标示。

在Stub类的构造方法中,就是通过attachInterface方法将当前的Binder和这个唯一标示进行了绑定。

  • asInterface()。将服务端的Binder对象转换成客户端所需的接口类型对象。

这个方法是客户端调用的,在这个方法中,会通过queryLocalInterface(DESCRIPTOR)方法,传入唯一标示,来获取对应的Binder。

如果是服务端和客户端在同一个进程,那么就会返回服务端的Binder对象,也就是Stub对象本身,然后就直接调用对象的方法了。

如果在不同进程,也就是我们一般的跨进程情况,就会返回封装后的Stub.Proxy这个代理对象。

  • Proxy.getMsg/tell

接着就看看代理类里面的方法,也就是我们在客户端(Activity)中实际调用的方法。

这其中有两个比较重要的对象 :_data对象_reply对象,都是Parcel类型的,这里会对数据进行一个序列化操作,这样才能进行跨进程传输。

如果方法传有参数,就会把参数写到_data对象,然后调用transact方法发起远程调用请求(RPC),同时当前线程挂起,等待服务端执行完请求。

    mRemote.transact(Stub.TRANSACTION_getMsg, _data, _reply, 0);

可以看到,传入了一个int类型的code——TRANSACTION_getMsg,这个code其实就是为了要确定调用的是哪个方法。

等请求结束后,当前线程继续,会从_reply对象中取出返回结果。

整个IPC流程就结束了。那服务器到底是在哪里进行任务执行的呢?继续看onTransact方法。

  • onTransact

onTransact方法就是服务端要做的事了,运行在服务端的Binder线程池中。

当客户端发起远程调用请求后,会通过系统底层封装,其实也就是内核层的Binder驱动,然后交给服务端的onTransact方法。

在该方法中,首先通过code知晓是哪个方法,然后就执行目标方法,并将序列化结果写到reply中,RPC过程结束,交给客户端处理。

case TRANSACTION_getMsg: {
data.enforceInterface(descriptor);
java.lang.String _result = this.getMsg();
reply.writeNoException();
reply.writeString(_result);
return true;
}

最后画张图总结下AIDL整个流程:

Binder

经过了上述AIDL的例子,大家是不是对Binder又进一步了解了呢?

在java层面,其实Binder就是一个实现了IBinder接口的类。

真正跨进程的部分还是在客户端发起远程调用请求之后,系统底层封装好,交给服务端的时候。而这个系统底层封装,其实就是发生在Linux内核中。

而在内核中完成这个通信关键功能的还是Binder,这次不是Binder类了,而是Binder驱动

驱动你可以理解为一种硬件接口,可以帮助操作系统来控制硬件设备。

Binder驱动被添加运行到Linux内核空间,这样,两个不同进程就可以通过访问内核空间来完成数据交换:把数据传给Binder驱动,然后处理好再交给对方进程,完成跨进程通信。

而刚才通过AIDL的例子我们可以知道,客户端在请求服务端通信的时候,并不是直接和服务端的某个对象联系,而是用到了服务端的一个代理对象,通过对这个代理对象操作,然后代理类会把方法对应的code、传输的序列化数据、需要返回的序列化数据交给底层,也就是Binder驱动。(这也解释了为什么犬夜叉看到的是阿篱的虚影,而不是真身)

然后Binder驱动把对应的数据交给服务器端,等结果计算好之后,再由Binder驱动把数据返回给客户端。

ServiceManager

到这里,可能就会有朋友会发现有点不对劲,刚才不是说还有个ServiceManager吗?这里AIDL通信中咋没有呢,漏了啊??

ServiceManager其实是为了管理系统服务而设置的一种机制,每个服务注册在ServiceManager中,由ServiceManager统一管理,我们可以通过服务名在ServiceManager中查询对应的服务代理,从而完成调用系统服务的功能。所以ServiceManager有点类似于DNS,可以把服务名称和具体的服务记录在案,供客户端来查找。

在我们这个AIDL的案例中,能直接获取到服务端的Service,也就直接能获取到服务端的代理类IMsgManager,所以就无需通过ServiceManager这一层来寻找服务了。

而且ServiceManager本身也运行在一个单独的进程,所以它本身也是一个服务端,客户端其实是先通过跨进程获取到ServiceManager的代理对象,然后通过ServiceManager代理对象再去找到对应的服务。

ServiceManager就像我们刚才AIDL中的Service一样,是可以直接找到的,他的句柄永远是0,是一个“众所周知”的句柄,所以每个APP程序都可以通过binder机制在自己的进程空间中创建一个ServiceManager代理对象。

(这也解释了为什么四魂之玉在另外一个世界,其实就在另外一个进程,我们只能通过句柄值为0找到四魂之玉的碎片,其实也就是代理)

所以通过ServiceManager查找系统服务并调用方法的过程是进行了两次跨进程通信。

APP进程——>ServiceManager进程——>系统服务进程(比如AactivityManagerService)

下面我们就拿ActivityManagerService举例看看怎么通过ServeiceManager获取到系统服务。

系统服务通信举例

熟悉APP启动流程的朋友都知道,startActivityForResult方法会转到mInstrumentation.execStartActivity方法中,而这里获取AMS服务的过程就用到了Binder机制:


//mInstrumentation.execStartActivity
int result = ActivityManager.getService()
.startActivity(whoThread, who.getBasePackageName(), intent,
intent.resolveTypeIfNeeded(who.getContentResolver()),
token, target != null ? target.mEmbeddedID : null,
requestCode, 0, null, options);
checkStartActivityResult(result, intent); public static IActivityManager getService() {
return IActivityManagerSingleton.get();
} private static final Singleton<IActivityManager> IActivityManagerSingleton =
new Singleton<IActivityManager>() {
@Override
protected IActivityManager create() {
final IBinder b = ServiceManager.getService(Context.ACTIVITY_SERVICE);
final IActivityManager am = IActivityManager.Stub.asInterface(b);
return am;
}
};

主要看这一句:ServiceManager.getService(Context.ACTIVITY_SERVICE)

这不就是ServiceManager吗,按照我们之前理解的那样,这里传入了一个name——Context.ACTIVITY_SERVICE,然后就能获取到对应服务的代理类,也就是IBinder对象,这里也就是对应的AMS的代理对象——IActivityManager

然后就可以对AMS进行一系列操作了。

这里的AMS服务其实对应的服务端,而我们调用的一方也就是APP本身的进程,就作为客户端。

剩下的问题就是,AMS是什么时候注册到ServiceManager的呢?答案在SystemServer中,之前的文章中说过,AMS的启动是在SystemServer中完成的,其实启动的同时也完成了在ServiceManager中的注册,这里贴下AMS的启动和注册服务代码,不熟悉的朋友可以翻翻SystemServer的源码或者我之前的文章。

    //SystemServer.java
private void startBootstrapServices() {
//... //启动AMS
mActivityManagerService = mSystemServiceManager.startService(
ActivityManagerService.Lifecycle.class).getService();
mActivityManagerService.setSystemServiceManager(mSystemServiceManager);
mActivityManagerService.setInstaller(installer); //为系统进程设置应用程序实例并启动。
mActivityManagerService.setSystemProcess();
} public void setSystemProcess() {
try {
ServiceManager.addService(Context.ACTIVITY_SERVICE, this, /* allowIsolated= */ true,
DUMP_FLAG_PRIORITY_CRITICAL | DUMP_FLAG_PRIORITY_NORMAL | DUMP_FLAG_PROTO);
}
}

到此,完整的Binder流程也介绍完了,再补一张Binder机制的流程图:

总结

所以Binder到底是什么呢?我想你心里已经有了答案,这里借用《Android开发艺术探索》书中的内容总结下,希望大家好好回味下~

直观的说,Binder是一个类,实现了IBinder接口。

从IPC(进程间通信)角度来说,Binder是Android中一种跨进程通信方式。

还可以理解为一种虚拟的物理设备,它的设备驱动是/dev/binder。

从Android FrameWork角度来说,Binder是ServiceManager连接各种Manager(ActivityManager,WindowManager等等)和响应ManagerService的桥梁。

从Android应用层来说,Binder是客户端和服务端进行通信的媒介。

Android体系架构

思维导图链接

参考

《Android开发艺术探索》

Binder学习指南

Android进程间通信(IPC)机制Binder简要介绍和学习计划

Android系统的Binder机制之一——Service Manager

拜拜

有一起学习的小伙伴可以关注下️ 我的公众号——码上积木,每天剖析一个知识点,我们一起积累知识,形成完整体系架构。公众号回复111可获得面试题《思考与解答》以往期刊。

《犬夜叉2021》我想通过Binder找到你的更多相关文章

  1. Android中的Binder机制的简要理解

    转载自:http://www.linuxidc.com/Linux/2012-07/66195.htm http://blog.csdn.net/sunxingzhesunjinbiao/articl ...

  2. 图解Android - Binder 和 Service

    在 Zygote启动过程 一文中我们说道,Zygote一生中最重要的一件事就是生下了 System Server 这个大儿子,System Server 担负着提供系统 Service的重任,在深入了 ...

  3. android的Binder通信机制java层浅谈-android学习之旅(88)

    1.Service Manager的Java代理对象 在Java层中,Service Manager的代理对象类型为ServiceManagerProxy.它继承并且实现了IServiceManage ...

  4. Android : 跟我学Binder --- (4) 驱动情景分析

    目录: Android : 跟我学Binder --- (1) 什么是Binder IPC?为何要使用Binder机制? Android : 跟我学Binder --- (2) AIDL分析及手动实现 ...

  5. IPC之binder机制

    我们知道,在Android系统中,每一个应用程序都运行在独立的进程中,这也保证了当其中一个程序出现异常而不会影响另一个应用程序的正常运转.在许多情况下,我们activity都会与各种系统的servic ...

  6. Binder机制-简单用法(一)

    Binder算是android里面比较难懂的部分了,但是非常重要,基本上,当我们深入到进程交互的阶段,Binder都是一个绕不开的槛,所以我也希望帮助大家更浅显地了解到这个知识点.笔者想通过3篇博文简 ...

  7. 跟面试官讲Binder(零)

    面试的时候,面试官问你说,简单说一下Android的Binder机制,你会怎么回答? 我想,我会这么说. 在Android启动的时候,Zygote进程孵化出第一个子进程叫SystemServer,而在 ...

  8. Android native进程间通信实例-binder篇之——简单的单工通信

    网上找了很多binder相关文章,大部分都是在跟踪binder实现源代码,然后再把框架代码贴出来,看着实在费力. 这篇文章从实际出发,直接用一个案例下手,后续想了解binder相关原理的话,可以参考& ...

  9. 盘点 2021|「避坑宝典」为大家分享一下笔者在 2021 年所遇到“匪夷所思”的 Bug 趣事(上)

    正版内容:https://xie.infoq.cn/article/3145cd5f525fe26ce9d574c8d 2021尾声想跟大家说的话 虚则实之 引用 https://xie.infoq. ...

随机推荐

  1. 转:locality sensitive hashing

    Motivation The task of finding nearest neighbours is very common. You can think of applications like ...

  2. HBuilder云端打包+个推

    1.个推上登记应用. 应用名称和应用标识,在HBuilder的云端打包配置中获取. 应用证书:必需要有苹果开发者账号,并且加入了"iOS Developer Program".加入 ...

  3. 二叉排序树(BST)

    二叉排序树(BST) 二叉排序树,又称二叉查找树(BST) 左子树结点值<根节点值<右子树结点值 如果用中序遍历来遍历一棵二叉排序树的话,可以得到一个递增的有序数列 左根右 二叉排序树的查 ...

  4. Linux下常用日志说明

    /var/log/message ------------------------------------------系统启动后的信息和错误日志 /var/log/secure ----------- ...

  5. JavaSE03-运算符&分支语句

    1.运算符 1.1 算术运算符 1.1.1 运算符和表达式 运算符:对常量或者变量进行操作的符号 表达式:用运算符把常量或者变量连接起来符合java语法的式子就可以称为表达式. 不同运算符连接的表达式 ...

  6. Service Cloud 零基础(二)Knowledge浅谈

    本篇参考:https://trailhead.salesforce.com/content/learn/projects/set-up-salesforce-knowledge https://tra ...

  7. springMVC基础讲解

    一.初识三层架构: 在讲解springMVC之前,先来了解一下什么是三层架构.我们的开发架构一般都是基于两种形式,一种是C/S架构(客户端/服务器),另一种是B/S架构(浏览器服务器).在javaEE ...

  8. AddressSanitizer —— ASAN分析内存错误

    简介 AddressSanitizer 是一个性能非常好的C/C++ 内存错误探测工具. 它由编译器的插桩模块和替换了malloc函数的运行时库组成. 这个工具可以探测如下这些类型的错误: 对堆.栈和 ...

  9. 从源码角度学习Java动态代理

    前言 最近,看了一下关于RMI(Remote Method Invocation)相关的知识,遇到了一个动态代理的问题,然后就决定探究一下动态代理. 这里先科普一下RMI. RMI 像我们平时写的程序 ...

  10. .Net Core使用IdentityServer4

    官方文档https://identityserver4.readthedocs.io/en/latest/ 参考https://www.cnblogs.com/i3yuan/p/13843082.ht ...