// Find the service manager
sServiceManager = ServiceManagerNative.asInterface(Binder.allowBlocking(BinderInternal.getContextObject()));
return sServiceManager;

}


这里使用的是单例模式,没有加线程保护,因为这个接口并不给应用使用,以及应用也不能直接操作`addService()``getService()`等接口,所以`getIServiceManager()`可以是认为运行在主线程中,**没错,我们与ServiceManager的通信也是采用Binder,只是ServiceManager的Binder是有点特殊**,我们先看`ServiceManagerNative.asInterface()`这个方法### [ServiceManagerNative.asInterface()](
)

public static IServiceManager asInterface(IBinder obj) {
if (obj == null) {
return null;
}

// ServiceManager is never local
return new ServiceManagerProxy(obj);

}


直接创建了一个`ServiceManagerProxy`对象### [ServiceManagerProxy初始化](
)

public ServiceManagerProxy(IBinder remote) {
mRemote = remote;
mServiceManager = IServiceManager.Stub.asInterface(remote);
}


1.  `mRemote = remote;`这个是老的方式,gityuan的[Binder系列7-framework层分析](
),就是使用这种方式
2.  `mServiceManager = IServiceManager.Stub.asInterface(remote);`是通过AIDL方式进行通信,显得更加简洁一点这里说明一点**AIDL并不等于Binder通信,它只是让Binder通信变得更加简单,就如同Retrofit和okhttp的关系**,AIDL生成的java代码在out目录下,所以想要分析的话得要先编译过Android源码才行,具体的路径是`out/soong/.intermediates/frameworks/base/framework-minus-apex/android_common/javac/shard30/classes/android/os/IServiceManager.class`使用AndroidStudio,IDEA或者反编译工具可以打开查看,如果想直接看的话我把它拷贝了一份[IServiceManager.java](
)### [IServiceManager.Stub.asInterface()](
)

public static IServiceManager asInterface(IBinder obj) {
if (obj == null) {
return null;
} else {
IInterface iin = obj.queryLocalInterface(“android.os.IServiceManager”);
return (IServiceManager)(iin != null && iin instanceof IServiceManager ? (IServiceManager)iin : new IServiceManager.Stub.Proxy(obj));
}
}


逻辑很简单1.  如果是相同进程,直接返回Binder对象,由于ServiceManager是单独处于一个进程,这里不会是相同进程,至于本地Service是怎么连接到的,我们稍后再讨论
2.  如果不是相同进程,则创建`IServiceManager.Stub.Proxy`### [IServiceManager.Stub.Proxy初始化](
)

Proxy(IBinder remote) {
this.mRemote = remote;
}


这个和ServiceManagerProxy老的初始化方式是不是一模一样,再来看看`IServiceManager.Stub.Proxy.addService()`

public void addService(String name, IBinder service, boolean allowIsolated, int dumpPriority) throws RemoteException {
Parcel _data = Parcel.obtain();
Parcel _reply = Parcel.obtain();

try {_data.writeInterfaceToken("android.os.IServiceManager");_data.writeString(name);_data.writeStrongBinder(service);_data.writeInt(allowIsolated ? 1 : 0);_data.writeInt(dumpPriority);boolean _status = this.mRemote.transact(3, _data, _reply, 0);if (!_status && IServiceManager.Stub.getDefaultImpl() != null) {IServiceManager.Stub.getDefaultImpl().addService(name, service, allowIsolated, dumpPriority);return;}_reply.readException();
} finally {_reply.recycle();_data.recycle();
}

}


和老方式也基本一样,所以新方式只是自动生成了这部分代码,减少了代码量,让IPC看起来就是一个方法调用,但最终都是调用`IBinder.transact()`方法进行IPC,接下来回到`ServiceManager.getIServiceManager()`方法中,上面的只能算作是一些代码技巧而已,接下来就是Binder的核心,获取ServiceManager的`IBinder`对象。### [BinderInternal.getContextObject()](
)这是一个native方法,对应到`android_util_Binder.cpp`的`android_os_BinderInternal_getContextObject()`### [android\_os\_BinderInternal\_getContextObject()](
)这里做了两件事情:1.  通过调用`ProcessState.getContextObject(NULL)`获取sp<IBinder>,注意这里传递的参数NULL,即要获取的是IServiceManager的Binder,这一步留作native Binder解析流程中详细阐述
2.  调用`javaObjectForIBinder()`将IBinder转为java对象,即BinderProxy对象,转换的过程就是将IBinder的指针(long类型)存储在BinderProxy的mNativeData中### [javaObjectForIBinder()](
)1.  检查是不是Binder类型,这一步是判断是不是传入的是`JavaBBinder`类型2.  创建BinderProxyNativeData类型的指针,并初始化对应的字段3.  将其IBinder的智能指针引用放到BinderProxyNativeData.mObject当中4.  通过`CallStaticObjectMethod()`调用`BinderProxy.getInstance()`并传递对应的参数,这里又引出了一个问题,\*\*我们都知道调用一个类的静态方法是会触发类的加载,可以看到这里直接传的是`gBinderProxyOffsets.mClass`,说明BinderProxy.class对象已经加载完成了,那么这个`gBinderProxyOffsets`又是怎么初始化的呢?\*\*答案是在虚拟机启动的时候就加载完成了,具体的调用栈大致如下```//AndroidRuntime.cpp,这部分是将函数指针进行绑定,使用宏的目的是为了方便DEBUG,可以定义不同的结构体,进行不同的初始化操作#define REG_JNI(name)      { name }struct RegJNIRec {int (*mProc)(JNIEnv*);};extern int register_android_os_Binder(JNIEnv* env);//申明函数,使用静态链接链入static const RegJNIRec gRegJNI[] = {...REG_JNI(register_android_os_Binder),//宏展开就是结构体的初始化操作{register_android_os_Binder}...};//AndroidRuntime.cpp,这部分在进程启动时调用AndroidRuntime::start(const char* className, const Vector<String8>& options, bool zygote)int AndroidRuntime::startReg(JNIEnv* env)static int register_jni_procs(const RegJNIRec array[], size_t count, JNIEnv* env)//android_util_Binder.cppint register_android_os_Binder(JNIEnv* env)static int int_register_android_os_BinderInternal(JNIEnv* env) ```
5.  下面的代码没有看懂,但是关系不是很大如此一来返回的就是`BinderProxy`的对象实例,它也是实现了`IBinder`的接口### [BinderProxy.getInstance()](
)方法签名如下`private static BinderProxy getInstance(long nativeData, long iBinder)`,是一个私有的静态方法,返回的是BinderProxy的对象实例,再来对比一下`CallStaticObjectMethod(gBinderProxyOffsets.mClass, gBinderProxyOffsets.mGetInstance, (jlong) nativeData, (jlong) val.get())`,应该会觉得很相似。前面两个参数`gBinderProxyOffsets.mClass`和`gBinderProxyOffsets.mGetInstance`代表的分别是BinderProxy.class对象以及getInstance的方法名字,具体的可以看`static int int_register_android_os_BinderProxy(JNIEnv* env)`函数是怎么初始化gBinderProxyOffsets的字段的;后面两个参数就是getInstance的两个参数,将指针转成jlong类型传入到`BinderProxy.getInstance()`当中。接下来看一下getInstance中的逻辑1.  看看有没有缓存的BinderProxy对象,有则直接返回,这里可以缓存BinderProxy的原因是BinderProxy<==>Binder是一一对应的,所以对于一个Binder服务来说,只需要要一个BinderProxy即可,所以可以是全局的
2.  如果没有,则创建一个BinderProxy实例,并存入到缓存中
3.  之后的BinderProxy的初始化只做了一件事,将传递过来的`BinderProxyNativeData`的指针对应的jlong,存入到mNativeData中,实现了BinderProxy和native binder的关联以上就是获取IServiceManager整个过程,需要记住一点**就是IServiceManager的所有接口,最终都是调用`BinderProxy.transact()`方法中**### [ServiceManager.addService()](
)回到最开始添加服务的位置,方法签名如下

public static void addService(String name, IBinder service, boolean allowIsolated,int dumpPriority){
try {
getIServiceManager().addService(name, service, allowIsolated, dumpPriority);
} catch (RemoteException e) {
Log.e(TAG, “error in addService”, e);
}
}


在获取到`IserviceManager`之后的`addService()`则会调用到AIDL生成的方法当中### [IServiceManager.Proxy.addService()](
)这个方法的代码在上面有贴出,逻辑如下1.  获取Parcel对象,分为两种,传递数据的data和获取回复的reply
2.  写入两个String对象,分别是token和服务名称
3.  写入Binder对象,这里我们在下面进行讨论
4.  调用`IBinder.transact()`方法,之前获取IServiceManager的时候就说过返回的是`BinderProxy`的对象实例,所以看`BinderProxy.transact()`方法### [BinderProxy.transact()](
)1.  判断是不是异步的Binder,这里有个变量`mWarnOnBlocking`,还记得之前在调用`BinderInternal.getContextObject()`之后还做了一个操作`Binder.allowBlocking(BinderInternal.getContextObject())`,这里会将`mWarnOnBloking`置为false,所以这块逻辑一般都是走不到
2.  是否添加Trace,主要是性能跟踪
3.  接下来的一堆操作不是很理解是要干嘛的,最后是调用到`transactNative()`,这是个native方法,方法签名如下`transactNative(int code, Parcel data, Parcel reply, int flags)`,对应`android_util_Binder.cpp`的`android_os_BinderProxy_transact()`### [android\_os\_BinderProxy\_transact()](
)1.  判断dataObj是否为NULL,所以即使不传任何数据,也要在调用`transact()`之前调用`Parcel.obtain()`获取Parcel对象
2.  将java端的Parcel对象转为native的Parcel,包括data和reply,转换的方式其实和BinderProxy很类似,之后我们再来讨论
3.  之后调用`getBPNativeData()`将`BinderProxy.mNativeData`转为指向`BinderProxyNativeData`的指针,从而获取到sp<IBinder>
4.  之后调用native 的Binder的`transact()`方法进行,这部分留到native 的解析的时候再说### IServiceManager.getService()其实是一样的逻辑,都是通过`BinderProxy.tansact()`进行传递到 JNI再到native binder,包括开头的Demo中客户端调用`sayHello()`向服务进行通信,是不是和`addService()`很像,其中的逻辑是一模一样的。`getService()`和`addService()`唯一区别是对于reply的处理,`getService()`需要获取native传回来的IBinder,我们接下来讨论Parcel的时候看一下Parcel和Parceable
----------------### 为什么要Parcelable这里涉及到对象的深拷贝和浅拷贝,由于java对象都是通过引用来使用的,引用说白了还是指针。问题就在于此,浅拷贝只是将对象中的数据一字节一字节的拷贝,如果对象含有另一对象的引用,就会造成拷贝出来的对象指向同一个对象,这还只是其一,对于远程调用来说,如果只是浅拷贝,拷贝过来的对象中的引用根本就是null(进程的内存隔离)。解决的方法就是深拷贝,所谓深拷贝就是将对象中引用不断的进行“解析”,怎么“解析”呢?不要忘记了Java中的8个基本类型,所有的java对象都是由这8个基本类型组成,“解析”之后就变成一个个**有序**的字节序列,说它有序是因为“解析”的过程要按照一定的顺序,不然就不能“反解析”了,“反解析“的过程就是将有序的字节序列变成对象的过程,这个“解析”的过程就是序列化,“反解析”的过程就是反序列化Java中的序列话需要实现Serializeable以及设置一个serialVersionUID,不过由于Serializeable会产生大量的临时对象,所以Android序列化使用的都是Parceable### Parcel的作用Parcel作用是将需要传递的所有的对象都序列化,变成有序字节序列(这也是Parceable的createFromParcel()和writeToParcel()写入顺序和读取顺序不能乱的原因),传递给Binder驱动,然后从Binder驱动读取数据,反序列化成对象,返回给调用者或者等待者。Parcel支持的类型大致如下:1.  Java的6种基本类型,除了char和short,没有看到这两个的接口,之外都支持
2.  String或者CharSequence
3.  Parceable的对象
4.  Binder对象
5.  一些常见的集合类和上述类型的数组### [Parcel.writeInt()](
)`writeByte()`,`writeBoolean()`这两种类型最终调用的也是`writeInt()`,以这个方法为例,看一下Parcel的过程,`writeInt()`直接调用了`nativeWriteInt()`方法### **[android\_os\_Parcel\_writeInt()](
)**这个方法在`android_os_Parcel.cpp`中,直接将native Parcel对象的指针转换过来,调用`writeInt32()`将值写入,这里是由于Java中的int都是有符号的32位整数,于是这里引出了一个问题,这个`Parcel`对象是怎么初始化的?回顾一下前面Parcel对象是咋获取的`Parcel data = Parcel.obain()`### [Parcel.obtain()](
)## 总结**其实要轻松掌握很简单,要点就两个:**1.  找到一套好的视频资料,紧跟大牛梳理好的知识框架进行学习。
2.  多练。 (视频优势是互动感强,容易集中注意力)**你不需要是天才,也不需要具备强悍的天赋,只要做到这两点,短期内成功的概率是非常高的。**对于很多初中级Android工程师而言,想要提升技能,往往是自己摸索成长,不成体系的学习效果低效漫长且无助。> 以上就是总结的关于在面试的一些总结,希望对大家能有些帮助,除了这些面试中需要注意的问题,当然最重要的就是刷题了,这里放上我之前整理的一份超全的**面试专题PDF**,大家有兴趣的可以自行领取或者私信我:还有 **高级架构技术进阶脑图、Android开发面试专题资料**,高级进阶架构资料 帮助大家学习提升进阶,也节省大家在网上搜索资料的时间来学习,也可以分享给身边好友一起学习。**[CodeChina开源项目:《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》](https://codechina.csdn.net/m0_60958482/android_p7)****【Android核心高级技术PDF文档,BAT大厂面试真题解析】**![](https://img-blog.csdnimg.cn/img_convert/68f77fa1d30e2d9712d53868d4d26e74.png)面试专题资料**,高级进阶架构资料 帮助大家学习提升进阶,也节省大家在网上搜索资料的时间来学习,也可以分享给身边好友一起学习。**[CodeChina开源项目:《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》](https://codechina.csdn.net/m0_60958482/android_p7)****【Android核心高级技术PDF文档,BAT大厂面试真题解析】**[外链图片转存中...(img-6vHchh2M-1630398178969)]> 这里只是整理出来的部分面试题,后续会持续更新,希望通过这些高级面试题能够降低面试Android岗位的门槛,让更多的Android工程师理解Android系统,掌握Android系统。喜欢的话麻烦点击一个喜欢在关注一下~

Binder源码阅读指南之java层,作为Android开发程序员相关推荐

  1. java经典源码 阅读_公开!阿里甩出“源码阅读指南”,原来源码才是最经典的学习范例...

    我们为啥要阅读源码? 为什么面试要问源码?为什么我们Java程序员要去看源码?相信大多数程序员看到源码第一感觉都是:枯燥无味,费力不讨好!要不是为了"涨薪"我才不去看这个鬼东西!但 ...

  2. 源码阅读(34):Java中线程安全的Queue、Deque结构——ArrayBlockingQueue(4)

    (接上文<源码阅读(33):Java中线程安全的Queue.Deque结构--ArrayBlockingQueue(3)>) 2.3.3.3.forEachRemaining() 方法 f ...

  3. 源码阅读(32):Java中线程安全的Queue、Deque结构——ArrayBlockingQueue(2)

    (接上文<源码阅读(31):Java中线程安全的Queue.Deque结构--ArrayBlockingQueue(1)>) 本篇内容我们专门分析ArrayBlockingQueue中迭代 ...

  4. 【源码+图片素材】Java王者荣耀游戏开发_开发Java游戏项目【王者荣耀】1天搞定!!!腾讯游戏_Java课程设计_Java实战项目_Java初级项目

    王者荣耀是当下热门手游之一,小伙伴们是否想过如何制作一款属于自己的王者荣耀游戏呢? 本课程讲解了一个王者荣耀游戏的详细编写流程,即使你是刚入门Java的新手,只要你简单掌握了该游戏所需要的JavaSE ...

  5. echarts源码打包_Echarts源码阅读指南

    前言 由于echarts是基于zrender进行实现的,所以要解读echarts源码,首先要对zrender有大致的了解,在这篇文章中,我将对zrender的整体结构进行大致的解读. 介绍 zrend ...

  6. [附源码]计算机毕业设计基于Java的员工管理系统Springboot程序

    项目运行 环境配置: Jdk1.8 + Tomcat7.0 + Mysql + HBuilderX(Webstorm也行)+ Eclispe(IntelliJ IDEA,Eclispe,MyEclis ...

  7. Vue3源码阅读指南——计算属性(effectcomputed)

    在阅读Vue3响应式数据部分的源代码时,effect和computed部分的确有着其设计精巧之处.其代码实现是在packages/reactivity/effect.ts和packages/react ...

  8. Mybatis源码阅读(一):Mybatis初始化1.3 —— 解析sql片段和sql节点

    *************************************优雅的分割线 ********************************** 分享一波:程序员赚外快-必看的巅峰干货 如 ...

  9. Vuex源码阅读分析

    Vuex源码阅读分析 Vuex是专为Vue开发的统一状态管理工具.当我们的项目不是很复杂时,一些交互可以通过全局事件总线解决,但是这种观察者模式有些弊端,开发时可能没什么感觉,但是当项目变得复杂,维护 ...

最新文章

  1. 将csv文件导入到mysql数据库
  2. MySQL常用存储引擎之Innodb
  3. 解决win2003安装exchangeServer后关机慢的方法
  4. html一张图片用两种滤镜,HTML图片CSS滤镜—灰度效果
  5. 写了 20-50 年的代码,才明白的那些真理
  6. 生活随笔:郊外骑单车
  7. LeetCode ——24. 两两交换链表中的节点
  8. 微服务架构及其最重要的10个设计模式
  9. 虚拟机安装教程win10_Parallels Desktop如何安装windowns系统?PD虚拟机安装win10系统详细教程
  10. 中职计算机创新杯说课比赛课件,2017年全国中等职业学校“创新杯”  教师信息化教学说课大赛总结...
  11. unity 3d开发的大型网络游戏
  12. 雷锋网巴展见闻录:5G 手机也许会迟到,但不会缺席 | MWC 2019
  13. 尚硅谷的Netty介绍(一)
  14. Diagnosing OSGi uses conflicts
  15. php scada,scada系统是什么
  16. Windows修改MySQL用户密码
  17. 以旧换新,iPhone5s免费拿
  18. 中国灾害预警爬取解析
  19. 如何使用maven给Java打包
  20. python练习小项目_小商店购物结算

热门文章

  1. Kafka | Kafka的消费再均衡是指什么?
  2. 腾讯云部署hexo博客系统
  3. swiper 滑动图片垂直居中
  4. 信息源按加工深度划分_按文献信息资源内容加工深度可以将文献划分为哪些类型,并简要解释?...
  5. 我们国家都有哪些信息安全标准
  6. python读取word文档结构图_Word 有什么技巧,让你相见恨晚?
  7. 使用安卓手机上的shh软件ConnectBot管理您的Linux服务器
  8. 茅台酒养成记:质量是对消费者最长久而衷心的承诺
  9. 啊哈添柴挑战Java1826. 顺序输出(简单)
  10. 生命早期肠道微生物组和疫苗功效