本故事纯属虚构,若有不通顺的逻辑请轻喷。❤️html
犬夜叉和奈落大决战以后,四魂之玉、食骨之井消失,谁也不知道去了哪,而犬夜叉和阿篱再次被分割到两个世界。java
因而犬夜叉拜托一位研究世界宇宙的法师——积木,来帮助他找到阿篱。android
时间转眼来到了2021年,积木终于发现了这个世界的秘密。。面试
其实咱们所在的整个宇宙叫作Android宇宙,犬夜叉和阿篱所处的两个世界实际上是两个进程,两个世界能够经过食骨之井相链接。服务器
因此想让犬夜叉从新联系到阿篱,必须再找到当年的食骨之井。架构
“犬夜叉,我终于找到了”ide
“找到什么了?是阿篱吗?阿篱找到了????”工具
“没有,不过我找到了关键的东西——食骨之井”学习
“在哪,快带我去”ui
因而,积木法师带着犬夜叉来到一间屋子里:
这间屋子门面上写着《内核屋》
三个大字,犬夜叉一个箭步飞了进去,在里面果真找到了当年那个食骨之井
,可是又有点不同,由于它被改了名,旁边一个破碎的板子上写着——Binder井
。板子上还同时刻有使用说明:
Binder井
这口井联系这两个世界,你看到的也许不是真实的,请慎用!
如需使用,请找到当年遗落的四魂之玉,如今它叫SM之玉(ServiceManager)
。
找到SM之玉,内心默念你想联系的那个世界那我的,若是她在那个世界的SM之玉碎片中留下了地址,那么你就能找到她。
“积木法师,你知道SM之玉吗,哪里能够找到它”,犬夜叉问到。
“说到SM之玉,还要从宇宙的起源提及,Android宇宙建立初期,诞生了第一个有人的世界(用户进程),叫作Init世界
,而SM之玉就是由这个世界建立的。
SM之玉建立后,启动了Binder井
,成为了他的守护神。
可是它的真身存在于单独的世界中,没法得到。为了让人们可以使用到它,它特地在每一个世界都留下了本身的碎片(代理)。”
“在哪在哪,快告诉我”。
“第0街道(句柄值固定为0)”,积木法师指了一个方向说到。
犬夜叉急忙去第0街道找到了SM之玉的碎片,而后回到Binder井
旁边,内心默念道:
“
SM之玉,
求求你帮我找到阿篱吧。
”
突然,Binder
井刮出一阵狂风,一个虚影出如今了犬夜叉的面前。
是阿篱~
“阿篱,你能听到我说话吗?”
“犬夜叉,我能听到,没想到还能看到你”,阿篱的虚影说到。
“我想你了,阿篱...”
故事结束了。
再帮你们理一下故事梗概,其实也就是Binder
的工做流程:
固然,故事毕竟是故事,并不能彻底说清楚。
因此下面就完整看看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: 我是犬夜叉,我收到了你说的:犬夜叉...是我
代码比较简单,服务器端新建一个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整个流程:
通过了上述AIDL的例子,你们是否是对Binder又进一步了解了呢?
在java层面,其实Binder
就是一个实现了IBinder
接口的类。
真正跨进程的部分仍是在客户端发起远程调用请求以后,系统底层封装好,交给服务端的时候。而这个系统底层封装,其实就是发生在Linux内核
中。
而在内核中完成这个通讯关键功能的仍是Binder
,此次不是Binder
类了,而是Binder驱动
。
驱动你能够理解为一种硬件接口,能够帮助操做系统来控制硬件设备。
Binder驱动
被添加运行到Linux内核空间
,这样,两个不一样进程就能够经过访问内核空间来完成数据交换:把数据传给Binder驱动
,而后处理好再交给对方进程,完成跨进程通讯。
而刚才经过AIDL
的例子咱们能够知道,客户端在请求服务端通讯的时候,并非直接和服务端的某个对象联系,而是用到了服务端的一个代理对象,经过对这个代理对象操做,而后代理类会把方法对应的code、传输的序列化数据、须要返回的序列化数据
交给底层,也就是Binder驱动。(这也解释了为何犬夜叉看到的是阿篱的虚影,而不是真身😝)
而后Binder驱动把对应的数据交给服务器端,等结果计算好以后,再由Binder驱动
把数据返回给客户端。
到这里,可能就会有朋友会发现有点不对劲,刚才不是说还有个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开发艺术探索》
Binder学习指南
Android进程间通讯(IPC)机制Binder简要介绍和学习计划
Android系统的Binder机制之一——Service Manager
有一块儿学习的小伙伴能够关注下❤️ 个人公众号——码上积木,天天剖析一个知识点,咱们一块儿积累知识,造成完总体系架构。公众号回复111可得到面试题《思考与解答》以往期刊。