Android Binder框架实现之Framework层服务注册过程源码分析



前言

  在前面的博客中Android Binder框架实现之Native层服务注册过程源码分析我们重点分析讲解了Android Native层的服务怎么注册的,但是我们知道Android中绝大部分的服务都是通过Java来实现的,那么Java层的服务是怎么注册到ServiceManager服务大管家中的呢,这个就是我们今天这个篇章要重点分析的了。通过前面Natvie层服务的注册过程中我们知道其大致可以分为三大步骤,而我们的Java层的服务注册过程也是如此,其三大步骤如下:

  • 注册服务请求的发送
  • 注册服务请求的处理
  • 注册服务请求的反馈

  而我们今天的篇章也将会围绕上述三个点来进行开展分析讲解,并且由于本篇章牵涉的知识点比较多,各位小伙伴们可要打起十分精神了!好了不多说了,直接开干!

  • 注意:本篇的介绍是基于Android 7.xx平台为基础的,其中涉及的代码路径如下:
framework/base/core/java/android/os/  ---IInterface.java  ---IServiceManager.java  ---ServiceManager.java  ---ServiceManagerNative.java(内含ServiceManagerProxy类)framework/base/core/java/android/os/  ---IBinder.java  ---Binder.java(内含BinderProxy类)  ---Parcel.javaframework/base/core/java/com/android/internal/os/  ---BinderInternal.javaframework/base/core/jni/  ---AndroidRuntime.cpp  ---android_os_Parcel.cpp  ---android_util_Binder.cpp  frameworks/native/libs/binder/BpBinder.cppframeworks/native/include/binder/IBinder.hframeworks/native/libs/binder/Binder.cppframeworks/native/include/binder/Parcel.hframeworks/native/libs/binder/Parcel.cppframeworks/base/core/jni/core_jni_helpers.hframeworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
  • 为了后续的书写方便会将ActivityManagerService简述为AMS,ServiceManager简述为SM,ServiceManagerProxy简述为SMP,ServiceManagerNative简述为SMN,以上特此申明!


一. Android Binder整体概述

  在正式开始Android Framework层服务注册过程的源码分析前,还是老规矩磨刀不误砍柴工,先让我们来点前期知识储备再上马,更加事倍功倍!老子不打没有准备的仗!


1.1 Android Binder整体架构

  通过前面的博客Android Binder框架实现之Native层服务注册过程源码分析我们知道了Android Binder的核心框架层实现是在C++层实现的,但是我们也知道Android的绝大部分开发者和从业人员都是以Java语言为主的,所以Android的妈咪谷歌也充分考虑到了这种情况在Android Framework层也实现了一套Binder机制。看到这里你是不是有点蒙蔽了,难不成Android还搞了两种Binder机制!当然不是,这里Android Framework层的Binder和Native层的Binder是相辅相成,协同合作从而完成Android Binder的千秋大业的。

  前面遗留了一个小的知识点即Android Framework层和Native 层的Binder是怎么协同关联起来的呢?那就是Java和C++连接的万能膏药JNI技术了,Framework层的Binder通过JNI来调用(C/C++)层的Binder框架,从而为上层应用程序提供服务。 通过前面的博客我们知道在Native Binder层中,Binder采用的是C/S架构,分为Bn端(Server)和Bp端(Client)。而Java层在命名与架构上和Native也非常相近,基本上是无缝衔接,同样也实现了一套类似的IPC通信架构供应用开发使用(当然最后都是通过Native层的Binder框架实现的)。这里Framework层的Binder从另外一个层面来说,就是怼底层Native BInder的二次封装。Android BInder的整体框架图如下所示:


1.2 Android Binder类图

1.2.1 Android Native Binder类图

  在前面的博客Android Binder框架实现之Native层服务注册过程源码分析中我们知道了Android Native Binder的涉及的类图关系如下(前方高能,请做好心理准备,要理清楚这其中的关系得花费一费时间的):

  虽然我们在前面的博客对Android Native层的Binder类图关系有了详细的介绍,这里我们还是简单简单的归纳下:

  • BpBinder和BBinder共同继承与IBinder类,BpBinder是客户端进程中的Binder代理对象,BBinder是服务进程中用于IPC通信的工具对象,BpBinder类通过IPCThreadState类来与Binder驱动交互,访问服务进程中的BBinder,BpBinder代表了客户进程,BBinder代表了服务进程,数据从BpBinder发送到BBinder的整个过程就是Android系统的整个Binder通信过程,BpBinder与BBinder负责IPC通信,和上层业务并无关系。

  • Android系统的Binder通信实现了RPC远程调用,BpXXX和BnXXX则负责RPC远程调用的业务,XXX就代表不同的服务业务。BpXXX和BnXXX都实现了IXXX接口,IXXX定义了业务接口函数,BpXXX则是客户进程对服务进程中的BnXXX的影子对象,客户进程在调用服务进程中的某个接口函数时,只需调用BpXXX中的对应函数即可,BpXXX屏蔽了进程间通信的整个过程,让远程函数调用看起来和本地调用一样,这就是Android系统的Binder设计思想。

  • 进程要与Binder驱动交互,必须通过ProcessState对象来实现,ProcessState在每个使用了Binder通信的进程中唯一存在,其成员变量mDriverFD保存来/dev/binder设备文件句柄。对于某个服务来说,可能同时接受多个客户端的RPC访问,因此Android系统就设计了一个Binder线程池,每个Binder线程负责处理一个客户端的请求,对于每个Binder线程都存在一个属于自己的唯一IPCThreadState对象,IPCThreadState对象封装来Binder线程访问Binder驱动的接口,同一个进程中的所有Binder线程所持有的IPCThreadState对象使用线程本地存储来保存。

1.2.2 Android Framework层Binder类图

  那么Android Framework层的Binder类图关系是啥样的呢!怎么说呢和Android Native层Binder类图关系基本上是一一对应的,但是又有所差别,其基本类图关系如下:

  是不是看到这个有点茫然无助的感觉,这里我们先简单介绍下(等在后续分析代码的时候可以将涉及到的相关类按图索翼,就理解起来容易了):

  • XXXManager:通过ServiceManager.getService方法可以获取到IXXXManagerService.Stub.Proxy对象,譬如我们通过该方法能获取到Android提供的的PMS,AMS,PKMS等相关服务的代理端。,然后我们可以通过该代理对象调用相关服务端完成指定的操作

  • IXXXManagerService.Stub.Proxy:其成员变量mRemote指向BinderProxy对象,IXXXManagerService.Stub.Proxy调用的相关方法最终是交由mRemote来完成相关操作的(注意这里的mRemote也是一个一个代理,真正完成相关的是在服务端)

  • IXXXManagerService.Stub:其方法asInterface()返回的是IXXXManagerService.Stub.Proxy对象,XXXManager便是借助IXXXManagerService.Stub类来找到IXXXManagerService.Stub.Proxy

  • Binder:其成员变量mObject和方法execTransact()用于native方法,和Native层Binder的BBinder对应

  • BinderInternal:内部有一个GcWatcher类,用于处理和调试与Binder相关的垃圾回收。

  • IBinder:接口中常量FLAG_ONEWAY:客户端利用binder跟服务端通信是阻塞式的,但如果设置了FLAG_ONEWAY,这成为非阻塞的调用方式,客户端能立即返回,服务端采用回调方式来通知客户端完成情况。另外IBinder接口有一个内部接口DeathDecipient(死亡通告)。

  • BinderProxy: 该类实现了IBinder接口,和Android Native Binder层的BpBinder是对应关系,最后借助BpBinder完成Binder间的数据传输

是不是上面的描述的描述还是有点抽象,那么以我们Framework层的ServiceManager(Java层的服务大管家)为例,将相关的实现带入类图(通常涉及到具体的实现会对Proxy和Stub类进行一下封装)。


1.3 Android Binder的层级关系

  通过前面1.1章节我们知道Android Framework层和Native 层Binder之间是通过JNI串联起来的,那么这两者之间的层级关系如何呢?我这边小小的归纳总结了一下,可以用如下的两个图来表示:



1.4 Android Binder的类层级构架

  如果对Android Binder涉及的类图关系也进行一次分层划分,那么整个Binder从kernel至,Native,JNI,Framework层所涉及的全部类可以使用如下gityuan大神的图归纳总结一下:



二. Android Framework层Binder框架的初始化

  通过前面博客Android之init进程启动源码分析我们可知Android Native Binder的大管家servicemanager进程及其相关的Native Binder Service进程是在init进程解析相关的init.xx.rc文件中的service块时启动的。那么我们的Android Framework Binder框架又是何时启动的呢?也许对Android启动过程有一定了解的小伙伴们会说是在system_server进程启动过程中,此时会启动相关核心服务并将其加入ServiceManager中。其实不然在Android Zygote进程启动源码分析指南中,我们知道Zygote进程会调用AndroidRuntime::startReg函数注册一系列的JNI函数,而这其中就包括我们的Android Framework层的Binder框架层相关的JNI,此时就标志着Framework层BInder框架的启动,而不是到了system_server进程才开始启动的。至于为什么要在此处注册,这个就留个小伙伴们自行思考了?


2.1 AndroidRuntime::startReg

//AndroidRuntime.cppstatic const RegJNIRec gRegJNI[] = {...REG_JNI(register_android_os_Binder),//详见章节2.2...}int AndroidRuntime::startReg(JNIEnv* env){...    if (register_jni_procs(gRegJNI, NELEM(gRegJNI), env) < 0) {        env->PopLocalFrame(NULL);        return -1;    }...    return 0;}

  这个流程没有过多好讲的,就是注册一系列的JNI方法,其中gRegJNI是一个数组保存了一系列的待注册的JNI方法列表,而这其中就包括我们今天的主人公Framework Binder相关的JNI方法register_android_os_Binder。


2.2 register_android_os_Binder

  这里顺带说一句,如果对JNI有不熟悉的小伙伴,强烈建议先阅读一下JNI/NDK入门指南,带你开启JNI世界的大门!

//android_util_Binder.cppint register_android_os_Binder(JNIEnv* env){//注册Binder类的JNI方法if (int_register_android_os_Binder(env) < 0)//详见章节2.3        return -1;//注册BinderInteral类的JNI方法    if (int_register_android_os_BinderInternal(env) < 0)//详见章节2.4        return -1;//注册BinderProxy类的JNI方法    if (int_register_android_os_BinderProxy(env) < 0)//详见章节2.5        return -1;...    return 0;}

  register_android_os_Binder函数比较简单,就是调用另外的函数继续进行相关的Binder的JNI方法的注册。
在继续分析相关的函数前,我们来先看一下frameworks/base/core/jni/core_jni_helpers.h中对JNI环境提供的基本函数进行二次封装的相关工具函数集合(为啥要先介绍,因为后续会用到这些工具函数),如下:

//frameworks/base/core/jni/core_jni_helpers.h/**查找对应Java类*/static inline jclass FindClassOrDie(JNIEnv* env, const char* class_name) {    jclass clazz = env->FindClass(class_name);    LOG_ALWAYS_FATAL_IF(clazz == NULL, "Unable to find class %s", class_name);    return clazz;}/**返回类的实例(非静态)域的属性ID*/static inline jfieldID GetFieldIDOrDie(JNIEnv* env, jclass clazz, const char* field_name,                                       const char* field_signature) {    jfieldID res = env->GetFieldID(clazz, field_name, field_signature);    LOG_ALWAYS_FATAL_IF(res == NULL, "Unable to find static field %s", field_name);    return res;}/**返回Java类或者接口实例非静态方法的方法ID*/static inline jmethodID GetMethodIDOrDie(JNIEnv* env, jclass clazz, const char* method_name,                                         const char* method_signature) {    jmethodID res = env->GetMethodID(clazz, method_name, method_signature);    LOG_ALWAYS_FATAL_IF(res == NULL, "Unable to find method %s", method_name);    return res;}/**返回类的静态域的属性ID*/static inline jfieldID GetStaticFieldIDOrDie(JNIEnv* env, jclass clazz, const char* field_name,                                             const char* field_signature) {    jfieldID res = env->GetStaticFieldID(clazz, field_name, field_signature);    LOG_ALWAYS_FATAL_IF(res == NULL, "Unable to find static field %s", field_name);    return res;}/**返回类的静态方法ID*/static inline jmethodID GetStaticMethodIDOrDie(JNIEnv* env, jclass clazz, const char* method_name,                                               const char* method_signature) {    jmethodID res = env->GetStaticMethodID(clazz, method_name, method_signature);    LOG_ALWAYS_FATAL_IF(res == NULL, "Unable to find static method %s", method_name);    return res;}/**基于局部引用创建一个全局引用*/template <typename T>static inline T MakeGlobalRefOrDie(JNIEnv* env, T in) {    jobject res = env->NewGlobalRef(in);    LOG_ALWAYS_FATAL_IF(res == NULL, "Unable to create global reference.");    return static_cast<T>(res);}/**注册Java类对应的JNI方法*/static inline int RegisterMethodsOrDie(JNIEnv* env, const char* className,                                       const JNINativeMethod* gMethods, int numMethods) {    int res = AndroidRuntime::registerNativeMethods(env, className, gMethods, numMethods);    LOG_ALWAYS_FATAL_IF(res < 0, "Unable to register native methods.");    return res;}

2.3 int_register_android_os_Binder

  小伙伴们,这个标题没有错,不要怀疑我是把2.2的标题给复制了一篇粘贴过来了。

//android_util_Binder.cppconst char* const kBinderPathName = "android/os/Binder";static int int_register_android_os_Binder(JNIEnv* env){//kBinderPathName = "android/os/Binder",用来查找kBinderPathName路径所属的类,即我们的Binder.javajclass clazz = FindClassOrDie(env, kBinderPathName);//关于该函数的功能参见2.2//将Java层的Binder类保存到mClass中gBinderOffsets.mClass = MakeGlobalRefOrDie(env, clazz);//关于该函数的功能参见2.2//将Java层execTransact()方法保存到mExecTransact变量    gBinderOffsets.mExecTransact = GetMethodIDOrDie(env, clazz, "execTransact", "(IJJI)Z");//关于该函数的功能参见2.2//将Java层的mObject属性保存到mObject中gBinderOffsets.mObject = GetFieldIDOrDie(env, clazz, "mObject", "J");//关于该函数的功能参见2.2    return RegisterMethodsOrDie(        env, kBinderPathName,        gBinderMethods, NELEM(gBinderMethods));}

  有了前面章节2.2最后关于JNI工具函数的的知识铺垫int_register_android_os_Binder函数应该理解起来就很简单了,主要干了两件事情:

  • 将Java层Binder类的相关信息保存到gBinderOffsets结构体中,供后面Framework层Binder框架使用(关于结构体gBinderOffsets详见2.3.1)
  • 调用RegisterMethodsOrDie函数,动态注册Java层Binder类的对应的JNI方法(具体注册了那些JNI方法详见2.3.2)

2.3.1 bindernative_offsets_t

//android_util_Binder.cppstatic struct bindernative_offsets_t{    jclass mClass;//   保存Java层Binder类信息    jmethodID mExecTransact;// 保存Java层Binder类execTransact()方法ID    jfieldID mObject;//保存Java层Binder类mObject属性域ID} gBinderOffsets;

  这里的gBinderOffsets是全局静态结构体变量,该结构体主要的功能是用来设计保存Java层Binder类本身以及其成员方法execTransact()和成员属性mObject的,这为JNI层访问Java层提供通道。另外通过查询获取Java层Binder信息后保存到gBinderOffsets,而不是每次在需要使用Binder的时候再来进行相关的查找这样能大幅的提高效率,这是由于每次查询需要花费较多的CPU时间,尤其是频繁访问时,但是我们这里提前使用额外的结构体来保存这些信息,是以空间换时间的方法来达到提升效率(现在的Android设备存储空间都比较大,所以这些开销基本可以忽略不计)。

2.3.2 gBinderMethods

//jni.htypedef struct {    const char* name;//Java本地方法名称    const char* signature;//Java本地方法签名    void*       fnPtr;//对应的JNI函数} JNINativeMethod;//android_util_Binder.cppstatic const JNINativeMethod gBinderMethods[] = {     /* name, signature, funcPtr */    { "getCallingPid", "()I", (void*)android_os_Binder_getCallingPid },    { "getCallingUid", "()I", (void*)android_os_Binder_getCallingUid },    { "clearCallingIdentity", "()J", (void*)android_os_Binder_clearCallingIdentity },    { "restoreCallingIdentity", "(J)V", (void*)android_os_Binder_restoreCallingIdentity },    { "setThreadStrictModePolicy", "(I)V", (void*)android_os_Binder_setThreadStrictModePolicy },    { "getThreadStrictModePolicy", "()I", (void*)android_os_Binder_getThreadStrictModePolicy },    { "flushPendingCommands", "()V", (void*)android_os_Binder_flushPendingCommands },    { "init", "()V", (void*)android_os_Binder_init },    { "destroy", "()V", (void*)android_os_Binder_destroy },    { "blockUntilThreadAvailable", "()V", (void*)android_os_Binder_blockUntilThreadAvailable }};

  这里的gBinderMethods是一个全局的结构体数组,通过RegisterMethodsOrDie将该结构体数组中的本地方法和JNI函数建立一一对应的关系,通俗的来讲就是JNI的动态注册。

  经过上面的操作以后建立了Java层到JNI层的访问通道,进而为打通Java层和Native层之间的通道做好了前期的准备,此时也标志着Framework层的Binder和Native 层Binder之间的连接桥梁已经开始进行施工了。


2.4 int_register_android_os_BinderInternal

//android_util_Binder.cppconst char* const kBinderInternalPathName = "com/android/internal/os/BinderInternal";static int int_register_android_os_BinderInternal(JNIEnv* env){    jclass clazz = FindClassOrDie(env, kBinderInternalPathName);    gBinderInternalOffsets.mClass = MakeGlobalRefOrDie(env, clazz);    gBinderInternalOffsets.mForceGc = GetStaticMethodIDOrDie(env, clazz, "forceBinderGc", "()V");    return RegisterMethodsOrDie(        env, kBinderInternalPathName,        gBinderInternalMethods, NELEM(gBinderInternalMethods));}

  有了前面的分析,这里的流程基本就是依葫芦画瓢了,int_register_android_os_BinderInternal函数主要干了如下两件事情:

  • 将Java层BinderInternal类的相关信息保存到gBinderInternalOffsets结构体中,供后面Framework层Binder框架使用(关于结构体gBinderInternalOffsets详见2.4.1)
  • 调用RegisterMethodsOrDie函数,动态注册Java层BinderInternal类的对应的JNI方法(具体注册了那些JNI方法详见2.4.2)

2.4.1 binderinternal_offsets_t

//android_util_Binder.cppstatic struct binderinternal_offsets_t{        jclass mClass;//保存Java层BinderInternal类信息    jmethodID mForceGc;//保存Java层BinderInternal类forceBinder()方法ID} gBinderInternalOffsets;

  这里的gBinderInternalOffsets是全局静态结构体变量,该结构体主要的功能是用来设计保存Java层BinderInternal类本身以及其成员方法forceBinder()信息。

2.4.2 gBinderInternalMethods

  这里的gBinderInternalMethods也是一个全局的结构体数组,保存的是BinderInternal类本地方法以及对应的JNI方法对应关系表,如下所示:

//jni.htypedef struct {    const char* name;//Java本地方法名称    const char* signature;//Java本地方法签名    void*       fnPtr;//对应的JNI函数} JNINativeMethod;//android_util_Binder.cppstatic const JNINativeMethod gBinderInternalMethods[] = {     /* name, signature, funcPtr */    { "getContextObject", "()Landroid/os/IBinder;", (void*)android_os_BinderInternal_getContextObject },    { "joinThreadPool", "()V", (void*)android_os_BinderInternal_joinThreadPool },    { "disableBackgroundScheduling", "(Z)V", (void*)android_os_BinderInternal_disableBackgroundScheduling },    { "setMaxThreads", "(I)V", (void*)android_os_BinderInternal_setMaxThreads },    { "handleGc", "()V", (void*)android_os_BinderInternal_handleGc }};

  分析至此,Framework层的BinderInternal类和JNI之间的关系已经建立,进而为后续Android Framewrok层Binder和Native层Binder关系的建立夯实了坚定的基础。


2.5 int_register_android_os_BinderProxy

//android_util_Binder.cppconst char* const kBinderProxyPathName = "android/os/BinderProxy";static int int_register_android_os_BinderProxy(JNIEnv* env){     //gErrorOffsets保存了Error类信息    jclass clazz = FindClassOrDie(env, "java/lang/Error");    gErrorOffsets.mClass = MakeGlobalRefOrDie(env, clazz);    //gBinderProxyOffsets保存了BinderProxy类的信息    //其中kBinderProxyPathName = "android/os/BinderProxy"    clazz = FindClassOrDie(env, kBinderProxyPathName);    gBinderProxyOffsets.mClass = MakeGlobalRefOrDie(env, clazz);    gBinderProxyOffsets.mConstructor = GetMethodIDOrDie(env, clazz, "", "()V");    gBinderProxyOffsets.mSendDeathNotice = GetStaticMethodIDOrDie(env, clazz, "sendDeathNotice",            "(Landroid/os/IBinder$DeathRecipient;)V");    gBinderProxyOffsets.mObject = GetFieldIDOrDie(env, clazz, "mObject", "J");    gBinderProxyOffsets.mSelf = GetFieldIDOrDie(env, clazz, "mSelf",                                                "Ljava/lang/ref/WeakReference;");    gBinderProxyOffsets.mOrgue = GetFieldIDOrDie(env, clazz, "mOrgue", "J");    //gClassOffsets保存了Class.getName()方法    clazz = FindClassOrDie(env, "java/lang/Class");    gClassOffsets.mGetName = GetMethodIDOrDie(env, clazz, "getName", "()Ljava/lang/String;");    return RegisterMethodsOrDie(        env, kBinderProxyPathName,        gBinderProxyMethods, NELEM(gBinderProxyMethods));}

  依然是老套路,还是来个依葫芦画瓢算了,int_register_android_os_BinderProxy函数主要干了如下两件事情:

  • 将Java层BinderProxy类的相关信息保存到gBinderProxyOffsets结构体中,供后面Framework层Binder框架使用(关于结构体gBinderProxyOffsets详见2.5.1)
  • 调用RegisterMethodsOrDie函数,动态注册Java层BinderProxy类的对应的JNI方法(具体注册了那些JNI方法详见2.4.2)

2.5.1 binderproxy_offsets_t

//android_util_Binder.cppstatic struct binderproxy_offsets_t{        jclass mClass;    jmethodID mConstructor;    jmethodID mSendDeathNotice;        jfieldID mObject;    jfieldID mSelf;    jfieldID mOrgue;} gBinderProxyOffsets;

  这里的gBinderProxyOffsets是全局静态结构体变量,该结构体主要的功能是用来设计保存Java层BinderProxy类本身以及其成员方法ID和变量域ID等相关信息。

2.5.2 gBinderProxyMethods

  这里的gBinderProxyMethods也是一个全局的结构体数组,保存的是BinderProxy类本地方法以及对应的JNI方法对应关系表,如下所示:

//jni.htypedef struct {    const char* name;//Java本地方法名称    const char* signature;//Java本地方法签名    void*       fnPtr;//对应的JNI函数} JNINativeMethod;//android_util_Binder.cppstatic const JNINativeMethod gBinderProxyMethods[] = {     /* name, signature, funcPtr */    {"pingBinder",          "()Z", (void*)android_os_BinderProxy_pingBinder},    {"isBinderAlive",       "()Z", (void*)android_os_BinderProxy_isBinderAlive},    {"getInterfaceDescriptor", "()Ljava/lang/String;", (void*)android_os_BinderProxy_getInterfaceDescriptor},    {"transactNative",      "(ILandroid/os/Parcel;Landroid/os/Parcel;I)Z", (void*)android_os_BinderProxy_transact},//这个函数是重点    {"linkToDeath",         "(Landroid/os/IBinder$DeathRecipient;I)V", (void*)android_os_BinderProxy_linkToDeath},    {"unlinkToDeath",       "(Landroid/os/IBinder$DeathRecipient;I)Z", (void*)android_os_BinderProxy_unlinkToDeath},    {"destroy",             "()V", (void*)android_os_BinderProxy_destroy},};

  分析至此,Framework层的BinderProxy类和JNI之间的关系已经建立,进而为后续Android Framewrok层Binder和Native层Binder关系的建立夯实了坚定的基础。


2.6 Android Framework层Binder框架的初始化小结

  到这里Android Framework层Binder框架在Zygote进程中的初始化也告一段落了,通过这一些列的初始化工作Android Framework层的Binder和JNI建立起了相关的关联,进而为后续的和Native Binder关联建立起了通道。此时关于Android Framework层Binder相关类和JNI之间的联系,可以使用下述的图示来表示,如下:



三. Framework层服务注册

  在博客中Android system_server启动大揭秘我们讲述了在system_server启动的过程中会启动一系列的Android Framework层的BInder服务,这里我们以AMS(的注册为例来说明,Android Framework层的Binder服务是怎么注册到servicemanager中的(注意这里我们只重点关注AMS的注册,而对于AMS本身的功能不做过多分析和说明)。

  在AMS服务启动的setSystemProcess阶段会将自己注册到servicemanager进程中,源码如下:

//ActivityManagerService.javapublic final class ActivityManagerService extends ActivityManagerNative        implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback {       ...   public void setSystemProcess() {   ...   ServiceManager.addService(Context.ACTIVITY_SERVICE, this, true);//详细分析过程见3.1   ...   }   ...

3.1 ServiceManager.addService

//ServiceManager.java    public static void addService(String name, IBinder service, boolean allowIsolated) {        try {        /**        *看着精简的两句代码,蕴含着无限的能力        *这里会分为两个步骤来分解:        *一:getIServiceManager()获取ServiceManagerProxy代理端,详见章节3.2        *二:调用ServiceManagerProxy代理端addService方法注册服务,详见章节3.5        */            getIServiceManager().addService(name, service, allowIsolated);        } catch (RemoteException e) {            Log.e(TAG, "error in addService", e);        }    }

  ServiceManager.addService会调用getIServiceManager()方法获取SMN的代理端SMP,并且调用其方法addService将AMS注册到servicemanager中。


3.2 ServiceManager.getIServiceManager

//ServiceManager.javaprivate static IServiceManager sServiceManager;    private static IServiceManager getIServiceManager() {        if (sServiceManager != null) {            return sServiceManager;        }             //等价于new ServiceManagerProxy(new BinderProxy(0))        /**       *看着精简的两句代码,蕴含着无限的能力,其实我是想吐糟谷歌你为啥就不能好好的写代码呢       *这里会分为两个步骤来分解:       *一:BinderInternal.getContextObject(),创建new BinderProxy(0),详见章节3.2.1       *二:调用ServiceManagerNative.asInterface(),创建SMP服务代理端,详见章节3.3       */        sServiceManager = ServiceManagerNative.asInterface(BinderInternal.getContextObject());        return sServiceManager;    }

  该段代码短小精悍,但是却孕育着无限的力量!getIServiceManager采用了单例模式最终获取的是new ServiceManagerProxy(new BinderProxy(0)),不要问我是怎么来的,我只能说路途艰辛且行且珍惜!

3.2.1 BinderInternal.getContextObject

//BinderInternal.javapublic class BinderInternal {...public static final native IBinder getContextObject();...}

  一看就是一个本地方法,通过前面的章节我们知道在android_util_Binder.cpp中完成了对BinderInternal 本地方法的注册,其对应的JNI函数为android_os_BinderInternal_getContextObject,让我们前往一探究竟。

//android_util_Binder.cppstatic jobject android_os_BinderInternal_getContextObject(JNIEnv* env, jobject clazz){//此处返回的是new BpBinder(0)sp<IBinder> b = ProcessState::self()->getContextObject(NULL);//此处返回的是new BinderProxy(0)    return javaObjectForIBinder(env, b);//详见2.3.2}

  关于ProcessState::self()->getContextObject(NULL)这里就不展开进行具体相关的分析了,详见博客Android Binder框架实现之defaultServiceManager()的实现的2.3章节,总之最后如上代码会转换成new BpBinder(0)返回回来。

3.2.2 javaObjectForIBinder

//Binder.cppbool IBinder::checkSubclass(const void* /*subclassID*/) const{    return false;}//android_util_Binder.cpp//此处的入参val是BpBinderjobject javaObjectForIBinder(JNIEnv* env, const sp<IBinder>& val){    if (val == NULL) return NULL;    if (val->checkSubclass(&gBinderOffsets)) {//返回false,这个得val是BpBinder其父类是IBinder并且没有重写checkSubclass,所以直接返回false不会走该分支        // One of our own!        jobject object = static_cast<JavaBBinder*>(val.get())->object();        LOGDEATH("objectForBinder %p: it's our own %p!\n", val.get(), object);        return object;    }    // For the rest of the function we will hold this lock, to serialize    // looking/creation/destruction of Java proxies for native Binder proxies.    AutoMutex _l(mProxyLock);    // Someone else's...  do we know about it?    jobject object = (jobject)val->findObject(&gBinderProxyOffsets);    if (object != NULL) {//第一次object为null        jobject res = jniGetReferent(env, object);        if (res != NULL) {            ALOGV("objectForBinder %p: found existing %p!\n", val.get(), res);            return res;        }        LOGDEATH("Proxy object %p of IBinder %p no longer in working set!!!", object, val.get());        android_atomic_dec(&gNumProxyRefs);        val->detachObject(&gBinderProxyOffsets);        env->DeleteGlobalRef(object);    }//创建BinderProxy对象    object = env->NewObject(gBinderProxyOffsets.mClass, gBinderProxyOffsets.mConstructor);    if (object != NULL) {        LOGDEATH("objectForBinder %p: created new proxy %p !\n", val.get(), object);        // The proxy holds a reference to the native object.        //BinderProxy.mObject成员变量记录BpBinder对象        env->SetLongField(object, gBinderProxyOffsets.mObject, (jlong)val.get());        val->incStrong((void*)javaObjectForIBinder);        // The native object needs to hold a weak reference back to the        // proxy, so we can retrieve the same proxy if it is still active.        jobject refObject = env->NewGlobalRef(                env->GetObjectField(object, gBinderProxyOffsets.mSelf));//将BinderProxy对象信息附加到BpBinder的成员变量mObjects中        val->attachObject(&gBinderProxyOffsets, refObject,                jnienv_to_javavm(env), proxy_cleanup);        // Also remember the death recipients registered on this proxy        sp<DeathRecipientList> drl = new DeathRecipientList;        drl->incStrong((void*)javaObjectForIBinder);//BinderProxy.mOrgue成员变量记录死亡通知对象        env->SetLongField(object, gBinderProxyOffsets.mOrgue, reinterpret_cast<jlong>(drl.get()));        // Note that a new object reference has been created.        android_atomic_inc(&gNumProxyRefs);        incRefsCreated(env);    }    return object;}

  还记得大明湖畔的夏雨荷吗,走错片场了!还记得章节2.5 int_register_android_os_BinderProxy吗,里面有将BinderProxy类的相关信息保存到了gBinderProxyOffsets结构体中,而在这里正是借助这个gBinderProxyOffsets结构体保存的信息,以BpBinder(0)为参数在C++层通过JNI操作创建Java层的BinderProxy实例对象,并把BpBinder对象地址保存到BinderProxy.mObject成员变量中。至此BinderInternal.getContextObject()就已经分析完成了,将其扩展开来可以得到如下代码:

BinderInternal.getContextObject() = new BinderProxy(0);

3.3 ServiceManagerNative.asInterface

//ServiceManagerNative.javapublic abstract class ServiceManagerNative extends Binder implements IServiceManager{...//注意这里的入参是BinderProxy    static public IServiceManager asInterface(IBinder obj)    {        if (obj == null) {            return null;        }        //由于入参为BinderProxy,其方法queryLocalInterface返回为null        IServiceManager in =            (IServiceManager)obj.queryLocalInterface(descriptor);//详见3.3.1        if (in != null) {            return in;        }                return new ServiceManagerProxy(obj);//详见3.3.2    }    ...}

  我们通过前面的分析可知ServiceManagerNative.asInterface的入参为BinderProxy,然后调用其方法queryLocalInterface返回为null,所以ServiceManagerNative.asInterface(new BinderProxy(0))最后等价为:

ServiceManagerNative.asInterface(new BinderProxy(0)) = ServiceManagerProxy(new BinderProxy(0))

3.3.1 BinderProxy.queryLocalInterface

//BinderProxy.javafinal class BinderProxy implements IBinder {...public IInterface queryLocalInterface(String descriptor) {        return null;    }    ...}

  无需多言,只是为了验证前面的结论!

3.3.2 ServiceManagerProxy的构造初始化

//ServiceManagerNative.javaclass ServiceManagerProxy implements IServiceManager {//这里的入参为BinderProxy(0)    public ServiceManagerProxy(IBinder remote) {        mRemote = remote;    }   }

  ServiceManagerProxy的构造比较简单,就是将mRemote指向入参BinderProxy(0),该BinderProxy对象对应于BpBinder(0),其作为Binder代理端,指向Native层大管家service Manager。


3.4 getIServiceManager小结

//ServiceManager.javaprivate static IServiceManager sServiceManager;    private static IServiceManager getIServiceManager() {        if (sServiceManager != null) {            return sServiceManager;        }             //等价于new ServiceManagerProxy(new BinderProxy(0))        /**       *看着精简的两句代码,蕴含着无限的能力,其实我是想吐糟谷歌你为啥就不能好好的写代码呢       *这里会分为两个步骤来分解:       *一:BinderInternal.getContextObject(),创建new BinderProxy(0)       *二:调用ServiceManagerNative.asInterface(),创建SMP服务代理端       */        sServiceManager = ServiceManagerNative.asInterface(BinderInternal.getContextObject());        return sServiceManager;    }

  至此getIServiceManager就分析完成了,其基本功能就是获取SMP服务端代理对象SMP,其获取流程可以分为如下两步:

  • BinderInternal.getContextObject() 创建一个Java层的BinderProxy(0)对象,该对象与C++层的BpBinder(0)一一对应;

  • ServiceManagerNative.asInterface(obj) 创建一个Java层面的ServiceManagerProxy代理对象,作用与C++层的BpServiceManager相同。

对于上述的流程归纳总结起来即先创建一个BpBinder(0)用于和Binder驱动交互,接着以BpBinder(0)为参数创建Java层的BinderProxy(0)用于数据的传输,接着最后以BinderProxy(0)为参数创建ServiceManagerProxy用于数据的打包。上述对应的关系可以详见下列示意图:


3.5 ServiceManagerProxy.addService

//ServiceManagerProxy.java//这里的入参为三个分别是服务的名字(主要供第三方通过name想servicemanager大管家查询),服务的实体    public void addService(String name, IBinder service, boolean allowIsolated)            throws RemoteException {        Parcel data = Parcel.obtain();        Parcel reply = Parcel.obtain();        data.writeInterfaceToken(IServiceManager.descriptor);        data.writeString(name);//最终等价于writeStrongBinder(new JavaBBinder(env, obj))        data.writeStrongBinder(service);//详见3.6        data.writeInt(allowIsolated ? 1 : 0);//成员变量mRemote指向BinderProxy        mRemote.transact(ADD_SERVICE_TRANSACTION, data, reply, 0);//详见3.8        reply.recycle();        data.recycle();    }

  回到章节3.1,如果说前面的分析是开胃菜,那么到这里就要开始正餐了。我们调用getIServiceManager得到了SMP,接着继续调用其方法addService注册Framework层系统服务。其中主要涉及到了Parcel对数据的打包和传输,关于Parcel对基本数据的打包和写入可以详见博客Android Binder框架实现之Parcel详解一,这里我们会重点关注Binder类型对象的写入和Parcel类型数据的transact。


3.6 Parcel .writeStrongBinder

//Parcel.javaprivate static native void nativeWriteStrongBinder(long nativePtr, IBinder val); public final void writeStrongBinder(IBinder val) {     nativeWriteStrongBinder(mNativePtr, val); }

  依然是老套路,通过JNI调用C++层的Parcel实现Binder对象的写入。

3.6.1 android_os_Parcel_writeStrongBinder

//android_os_Parcel.cpp//注意此处的入参object是Framework层Binder服务实体 static void android_os_Parcel_writeStrongBinder(JNIEnv* env, jclass clazz, jlong nativePtr, jobject object){    Parcel* parcel = reinterpret_cast<Parcel*>(nativePtr);//强制将nativePtr转换成Parcel对象,这个见怪不怪了    if (parcel != NULL) {//等价于parcel->writeStrongBinder(new JavaBBinder(env, obj));/***我只能说此处代码短小精悍,力量无穷啊,此处的代码逻辑分为两步*第一:调用ibinderForJavaObject创建JavaBBinder,详见章节3.6.2*第二:调用C++层的Parcel对象函数writeStrongBinder写入JavaBBinder,这个内容有点只能详见3.7了*/        const status_t err = parcel->writeStrongBinder(ibinderForJavaObject(env, object));        if (err != NO_ERROR) {            signalExceptionForError(env, clazz, err);        }    }}

  谷歌的Binder是设计思想是如此的巧妙,可是为什么代码逻辑排版就不能好好的整理一下呢!android_os_Parcel_writeStrongBinder函数逻辑看着比较简单,也就干了两件事情(但是分析起来你懂的):

  • 调用ibinderForJavaObject创建JavaBBinder
  • 调用C++层的Parcel对象函数writeStrongBinder写入JavaBBinder

3.6.3 Binder.java的初始化

  在正式开始该函数分析前,让我们回过头捋一捋我们是要将什么Framework层Binder服务注册到servicemanager中的,对了是AMS,我们看看其继承和实现关系,如下:

//ActivityManagerService.java public final class ActivityManagerService extends ActivityManagerNative        implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback {        ...}//ActivityManagerNative.java//这里的父类Binder是重点public abstract class ActivityManagerNative extends Binder implements IActivityManager{...}

  平谈无奇,没有啥重点!那还能咋样呢,继续往下分析Binder.java的构造方法,逻辑如下:

//Binder.javaprivate native final void init();public class Binder implements IBinder {    public Binder() {        init();...    }}

  又是老套路,调用到了JNI层的函数,如下所示:

//android_util_Binder.cppstatic void android_os_Binder_init(JNIEnv* env, jobject obj){    JavaBBinderHolder* jbh = new JavaBBinderHolder();//构建一个JavaBBinderHolder对象    if (jbh == NULL) {        jniThrowException(env, "java/lang/OutOfMemoryError", NULL);        return;    }    ALOGV("Java Binder %p: acquiring first ref on holder %p", obj, jbh);//增加引用计数    jbh->incStrong((void*)android_os_Binder_init);/**将JavaBBinderHolder对象保存在gBinderOffsets.mObject中,此时的gBinderOffsets.mObject已经在Zygote启动中和Binder.java中的mObject绑定了*/    env->SetLongField(obj, gBinderOffsets.mObject, (jlong)jbh);}

  这个函数还干了一点事情,捯饬捯饬来说可以分为两步:

  • 构建一个JavaBBinderHolder对象
  • 将JavaBBinderHolder对象保存在gBinderOffsets.mObject中,此时的gBinderOffsets.mObject已经在Zygote启动中和Binder.java中的mObject绑定了

此时的你我想会有一个疑问就是gBinderOffsets这些的初始化不都是在Zygote进程中吗,我注册AMS服务是在system_server进程中进行的,这个gBinderOffsets能用不。我只能说孩子不要忘了你来自那里,当然不是天上了,那怕你贵为system_server进程你不也是Zygote进程孵化出来的,我们的Zygote进程也是无私的你继承了其所有也包括上面初数化的相关变量空间。

  最后我要说一句,真的,我不是闲的蛋疼才加上上面的描述的,更加不是为了凑字数的,再说也没有稿费可骗啊!真的你们要相信我,接着继续分析ibinderForJavaObject。

3.6.4 ibinderForJavaObject

//android_os_Parcel.cpp//此时的入参obj为Binder.java实例对象sp<IBinder> ibinderForJavaObject(JNIEnv* env, jobject obj){    if (obj == NULL) return NULL;//这里的IsInstanceOf和Java中的instanceof方法有点类似    if (env->IsInstanceOf(obj, gBinderOffsets.mClass)) {//检测是否是Java层Binder类或者子类        JavaBBinderHolder* jbh = (JavaBBinderHolder*)            env->GetLongField(obj, gBinderOffsets.mObject);//以Binder(Java层)为参数生成JavaBBinderHolder(C++层),详见3.6.5        return jbh != NULL ? jbh->get(env, obj) : NULL;//详见章节3.6.6    }    if (env->IsInstanceOf(obj, gBinderProxyOffsets.mClass)) {//检测是否是Java层BinderProxy类或者子类        return (IBinder*)            env->GetLongField(obj, gBinderProxyOffsets.mObject);    }    ALOGW("ibinderForJavaObject: %p is not a Binder object", obj);    return NULL;}

  是吗,真的没有骗你吗!通过前面我们知道AMS是Binder的子类,所以会走第一个分支即此时的入参obj和gBinderOffsets.mClass保存的Bindre信息是属于同一类型都是Binder类型,这里的JNI提供的函数IsInstanceOf和Java层的instanceof方法类似即判断两个对象是否属于同一个类或者及其子类。接着根据Binde(Java)生成JavaBBinderHolder(C++)对象,然后调用其get返回一个IBinder对象实例。

3.6.5 JavaBBinderHolder类初始化

//android_util_Binder.cpp,又是在这个文件里面,你干的活还真多class JavaBBinderHolder : public RefBase{public:...    sp<JavaBBinder> getExisting()    {        AutoMutex _l(mLock);        return mBinder.promote();    }private:    Mutex           mLock;    wp<JavaBBinder> mBinder;//注意这里的指针是wp类型的,即C++中的弱引用,可能会被回收};

  JavaBBinderHolder它是如此朴实无华,没有构造函数,既然你这么绝情也不要怪我无意,老子不分析你了。

3.6.6 JavaBBinderHolder::get

//android_util_Binder.cpp    sp<JavaBBinder> get(JNIEnv* env, jobject obj)    {        AutoMutex _l(mLock);        sp<JavaBBinder> b = mBinder.promote();//判断是会被回收或者已经创建了        if (b == NULL) {            b = new JavaBBinder(env, obj);//创建JavaBBinder,注意参数obj为Binder.java实例对象,详见3.6.7            mBinder = b;            ALOGV("Creating JavaBinder %p (refs %p) for Object %p, weakCount=%" PRId32 "\n",                 b.get(), b->getWeakRefs(), obj, b->getWeakRefs()->getWeakCount());        }        return b;    }

  通过前面的章节我们知道JavaBBinderHolder有一个名为mBinder的JavaBBinder对象的弱指针(C++层),mBinder是否被重新赋值的关键有两点第一其是否是第一次进入,第二由于mBinder是一个wp类型弱引用所以可能被系统垃圾回收机制回收,所以每次使用它的时候必须先行判断一下。

3.6.7 JavaBBinder

class JavaBBinder : public BBinder{public:    JavaBBinder(JNIEnv* env, jobject object)        : mVM(jnienv_to_javavm(env)), mObject(env->NewGlobalRef(object))    {        ALOGV("Creating JavaBBinder %p\n", this);        android_atomic_inc(&gNumLocalRefs);        incRefsCreated(env);    }}

  这里注意JavaBBinder的父类为BBinder,并且以传递过来的参数object构建一个全局的mObject引用(object为Java层的Binder.java)。

3.6.8 JavaBBinder JavaBBinderHolder Binder关系小结

  分析至此,各位小伙们是不是对于JavaBBinder (C++),JavaBBinderHolder(C++),Binder(Java)三者之间的关系有理不断剪还乱的感觉!真的不要怪Android的妈咪谷歌,它这么做也是为了世界和平!这里我们可以使用下述的示意图来表述三者之间的关系:


上述三者是怎么关勾搭上的呢(不,关联上的),这要回到3.6.4 ibinderForJavaObject说起了:

  • 在该函数中首先将Binder.java构造方法创建的JavaBBinderHolder(C++)捯饬出来,
  • 接续创建一个JavaBBinder对象,并将传递过来的Framework层的Binder服务实例对象作为参数创建Java层服务Binder对象的全局引用,并将其保存到JavaBBinder对象的mObject变量中
  • 返回创建的JavaBBinder对象实例

是不是还是有点一脸蒙蔽,但是既然现在三者之间的关系已经创建既成事实了,那么就着这三者之间的关系再来捋一捋:

  • Android Framework层的Binder服务必须继承于Binder类,而我们的Binder对象在构造服务时,会首先在C++层构造一个JavaBBinderHolder对象,并将该对象的指针保存到Java层的服务的mObject变量中,即建立起了Binder(Java)到JavaBBinderHolder(C++)之间的关系了

  • 而我们的C++层的JavaBBinderHolder对象通过成员变量mBinder指向一个C++层的JavaBBinder对象,JavaBBinder类继承于BBinder类,是服务在C++层的表现形式,此时建立起了JavaBBinderHolder(C++)和JavaBBinder(C++)之间的关系了

  • 而我们的C++层的JavaBBinder对象又通过成员变量mObject指向Java层的Binder服务对象,即建立起了Binder(Java)和JavaBBinder(C++)之间的关联了

对于这三者之间的关系,我只想说一句话造孽啊!


3.7 Parcel::writeStrongBinder

  在前面的博客Android Binder框架实现之Native层服务注册过程源码分析中此处的源码相关逻辑其实已经有分析了,但是为了整体框架的完整和从上到下的贯通我们还是简单过一下(其实我是舍不得各位小伙伴们)!

//Parcel.cppstatus_t Parcel::writeStrongBinder(const sp<IBinder>& val)//注意这里的入参val为JavaBBinder{    return flatten_binder(ProcessState::self(), val, this);//详见3.7.1}

  该函数没有多说的直接调用flatten_binder写将入参参数JavaBBinder写入C++层Parcel容器中。

3.7.1 flatten_binder扁平化处理

//Parcel.cppstatus_t flatten_binder(const sp<ProcessState>& /*proc*/,    const sp<IBinder>& binder, Parcel* out){    flat_binder_object obj;    obj.flags = 0x7f | FLAT_BINDER_FLAG_ACCEPTS_FDS;    if (binder != NULL) {        IBinder *local = binder->localBinder();        if (!local) {            BpBinder *proxy = binder->remoteBinder();            if (proxy == NULL) {                ALOGE("null proxy");            }            const int32_t handle = proxy ? proxy->handle() : 0;            obj.type = BINDER_TYPE_HANDLE;//远程Binder            obj.binder = 0; /* Don't pass uninitialized stack data to a remote process */            obj.handle = handle;//记录Binder代理的句柄            obj.cookie = 0;        } else {            obj.type = BINDER_TYPE_BINDER;//本地Binder进入该分支            obj.binder = reinterpret_cast<uintptr_t>(local->getWeakRefs());            obj.cookie = reinterpret_cast<uintptr_t>(local);//记录Binder实体的指针        }    } else {        obj.type = BINDER_TYPE_BINDER;        obj.binder = 0;        obj.cookie = 0;    }    return finish_flatten_binder(binder, obj, out);}

  我们这里的入参binder为JavaBBinder对象实例,我们回忆回忆其继承关系,其关系如下

IBinder(C++) ----> BBinder(C++) ---> JavaBBinder(C++)

而我们的BBinder的localBinder函数实现如下:

//Binder.cppBBinder* BBinder::localBinder(){    return this;}

所以精简过后的flatten_binder函数如下:

//Parcel.cppstatus_t flatten_binder(const sp<ProcessState>& /*proc*/,    const sp<IBinder>& binder, Parcel* out){    flat_binder_object obj;    obj.flags = 0x7f | FLAT_BINDER_FLAG_ACCEPTS_FDS;    if (binder != NULL) {        IBinder *local = binder->localBinder();        if (!local) {...        } else {            obj.type = BINDER_TYPE_BINDER;//本地Binder进入该分支            obj.binder = reinterpret_cast<uintptr_t>(local->getWeakRefs());            obj.cookie = reinterpret_cast<uintptr_t>(local);//记录Binder实体的指针        }    } else {...    }    return finish_flatten_binder(binder, obj, out);//详见章节3.7.2}

此时重要大哥flat_binder_object 要上场了,我们可以将其理解为它是用来专门描述BBinder对象的,将其带入实际代码,会得到如下的逻辑:

  • flat_binder_object结构体变量obj其成员type被赋值为BINDER_TYPE_BINDER,即表示此时的obj是一个BIndere实体对象
  • flat_binder_object结构体变量obj其成员binder记录Binder弱引用指针地址
  • flat_binder_object结构体变量obj其成员cookie 记录Binder实体指针地址

此时此刻关于flatten_binder扁平化的分析已经分析完毕了,此时flat_binder_object结构体中数据的映射关系如下:

3.7.2 finish_flatten_binder写入扁平化BInder数据

//Parcel.cppinline static status_t finish_flatten_binder(    const sp<IBinder>& /*binder*/, const flat_binder_object& flat, Parcel* out){    return out->writeObject(flat, false);}

  好了这里真的不能扩展了,我们只需要知道此时会将扁平化的Binder实体数据写入Parcel的存储结构中,具体的真的小伙伴们只能异步到Android Binder框架实现之Native层服务注册过程源码分析中了(因为有些小伙们,可能只是向了解Framewrok层的实现)。

更多相关文章

  1. Flutter插件混编的各种奇葩问题以及Flutter与Native数据交互,Meth
  2. android Frame动画概述+示例
  3. android截图事件监听
  4. Android(安卓)控件架构
  5. 说说 Android(安卓)中的 SQLite 数据库
  6. Android之最简单的ImageView加边框方法
  7. Android的Message机制(简单小结)
  8. 图解Android:Touch事件传递机制
  9. Android(安卓)gallery 3D效果

随机推荐

  1. 实战 | Python爬取B站柯南弹幕+Gephi梳理
  2. 对不起,我把APP也给爬了
  3. Python实现数据写入 Excel 的三种模块!
  4. 绝了!Python定时爬取微博热搜+pyecharts动
  5. 这52页pdf,顶10篇python自动化办公文章
  6. 学习C的第五天
  7. 刚学完python自动化系列文章,就接了一单任
  8. 学习C的第四天
  9. 开开心心爬APP,结果一坑连一坑
  10. nginx跳转 去掉工程名