深入理解android-jni,binder,zygote,ams


title: 深入理解android-jni,binder,zygote,ams
date: 2020-02-25 15:48:23
tags: [android framework jni binder]

typora-copy-images-to: upload
typora-root-url: ./深入理解android


jni

Jni就是java代码与Native(c,c++)代码交互的媒介. 通过jni,java代码可以主动调用native代码,然后native代码在被调用时,可以回调java的代码.

jni由两部分构成. Java端的声明为 native 的java方法.和native端对应的Java方法的实现方法. 然后就是通过一些技术把两端的声明方法和实现方法建立映射关系.就可以调用了.

jni分为静态注册和动态注册.

静态注册就是由java端声明的native的java方法生成固定命名规则的native方法的头文件.然后native端实现这些头文件的方法. 在把native端代码打成so包.java端在调用声明native的java方法前加载so包.然后执行native 的java方法.系统就会在so包找找固定命名规则的native方法进行调用.

静态注册的话,每个java类的native方法都会生成一个固定的.h文件.

动态注册就是对java端声明的native方法和native端的方法进行动态的绑定(当然这两个方法的形参得匹配),然后保存在一个native端的JNINativeMethod的结构体中,然后在JNI_OnLoad方法中注册这个匹配关系,java中先加载这个native方法生成的so库,然后在java代码执行native声明的方法时,从so库中查找JNINativeMethod机构提的匹配规则,找到对应native中的方法,执行.

动态注册的话,可以为多个java类中声明的所有native方法声明一个native端实现文件,并且不需要头文件.

静态注册

java端native方法如下

public static native String getAppKey();  加入native关键字,不需要实现,系统就知道这个方法由native实现

把java端的native声明的方法生成对应头文件.命令行中

 javah  -jni safedemo.jniTest.NativeHelper    NativeHelper是有native方法的java类,用javah声明

注意.这里有个javah 命令和NativeHelper.java文件的层级关系问题.我这个命令是在java目录下执行的.

生成navite的头文件.并把它实现.

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>  //这个头文件是必须的
#ifndef _Included_niTest_NativeHelper
#define _Included_safedemo_jiveHelper
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     safedemo_jniTest_NativeHelper
 * Method:    getAppKey
 * Signature: ()Ljava/lang/String;
 * 这里是对这个jni方法的解释 classs是java方法所属的类,然后是方法名,然后是方法的签名,方法签名可已唯一确定一个类中的唯一方法.
 */
JNIEXPORT jstring JNICALL Java_safedemo_jniTest_NativeHelper_getAppKey
  (JNIEnv *env, jclass);

#ifdef __cplusplus
}
#endif
#endif

把实现这个接口的文件达成so包.在java中调用native前引入.就可以执行native方法了.这里使用了cmake

public class NativeHelper {
    static {
        System.loadLibrary("native-lib");
    }
        public static native String getAppKey();
}

这样就建立了so库中的方法和java中的方法的动态绑定关系.

在java中,因为存在多态性,因此需要通过类名+方法名+方法的签名(返回值+形参)来唯一确定一个方法.

jni写的过程稍微有些复杂.但是原理其实是比较简单的.就是native声明的java方法和 c代码进行映射,然后调用java代码时执行c代码.

动态注册

动态注册的主要数据结构是JNINativeMethod,他定义在 jni.h里如下

typedef struct {
    const char* name;   //java的方法名
    const char* signature;  //java方法签名
    void*       fnPtr;  //对应c方法的指针
} JNINativeMethod;

举个例子

java方法
 public native void jnitest_nativefunction();
native 方法
    JNIEXPORT void JNICALL android_jnitest_1nativefunction(JNIEnv *, jobject);

JNINativeMethod结构
 {"jnitest_nativefunction",       "()V",    (void *)android_jnitest_nativefunction_01}
 ()V 是形参+返回值的组成,java方法没有形参所以()括号里没值, 返回值为空,表示为V

然后是要把这种映射结构注册到系统中

在native代码被加载时,jint JNI_OnLoad(JavaVM* vm, void* reserved)方法会被调用.因此我们要实现这个方法,然后注册java和native方法的映射关系.如下.这里是标准写法了.就是把某个java类里的所有映射关系,通过AndroidRuntime::registerNativeMethods 进行注册.

这里是java类+java方法 和native方法的指针,一起完成的.

//结构体绑定jnitest_javaclass_a中的两个接口和本地两个实现
static JNINativeMethod gMethods_class_a[] = {
        {"jnitest_nativefunction_01",       "()V",    (void*)android_jnitest_nativefunction_01},
        {"jnitest_nativefunction_02",       "()V",    (void*)android_jnitest_nativefunction_02},
};

//此函数在Java中调用System.loadLibrary("");时会被自动触发,在这个函数中将调用上面的两个注册函数,最终返回JNI版本号
jint JNI_OnLoad(JavaVM* vm, void* reserved) {
    JNIEnv *env = NULL;
    jint result = -1;
        //拿到env
    if (vm->GetEnv((void  ) &env, JNI_VERSION_1_4) != JNI_OK) {
        LOGE("ERROR: GetEnv failed\n");
        goto bail;
    }
        //注册两个java方法和本地的绑定关系 com/pioneer/jnitest/jnitest_javaclass是有native方法的类名
    AndroidRuntime::registerNativeMethods(env,
                                          "com/pioneer/jnitest/jnitest_javaclass",
                                          gMethods_class_a, NELEM(gMethods_class_a));
    result = JNI_VERSION_1_4;

    bail:
    return result;
}

jni数据类型转换

java中的数据类型和native中有基本的对应关系.特殊的是java的所有对象在native中都是jobjct

基本数据类型

image-20200225213307572

引用数据类型

所有数组都编程了 array, 所有对象都是 jobject

jobject                     (all Java objects)
|
|-- jclass                  (java.lang.Class objects)
|-- jstring                 (java.lang.String objects)
|-- jarray                  (array)
|     |--jobjectArray       (object arrays)
|     |--jbooleanArray      (boolean arrays)
|     |--jbyteArray         (byte arrays)
|     |--jcharArray         (char arrays)
|     |--jshortArray        (short arrays)
|     |--jintArray          (int arrays)
|     |--jlongArray         (long arrays)
|     |--jfloatArray        (float arrays)
|     |--jdoubleArray       (double arrays)
|
|--jthrowable

类属性

属性变量在 native用 jfieldID 表示 通过Get<>Field ,Set<>Field方法调用 其中的<>可为各种数据类型

        返回值                 方法名                 执行环境    操作对象    操作对象的属性
        jlong       (*GetLongField)(JNIEnv*, jobject, jfieldID);  
    jfloat      (*GetFloatField)(JNIEnv*, jobject, jfieldID);
    jdouble     (*GetDoubleField)(JNIEnv*, jobject, jfieldID);

    void        (*SetObjectField)(JNIEnv*, jobject, jfieldID, jobject);
    void        (*SetBooleanField)(JNIEnv*, jobject, jfieldID, jboolean);
    void        (*SetByteField)(JNIEnv*, jobject, jfieldID, jbyte);
    void        (*SetCharField)(JNIEnv*, jobject, jfieldID, jchar);

方法 zai native 用 _jmethodID表示 通过 CallStatic<>Method执行类的方法, Call<>MethodA执行对象的方法


对象方法
        返回值     | 方法名       |   执行环境    |   操作对象    |操作对象的方法 |  方法参数
        jint         (*CallIntMethod)(JNIEnv*, jobject, jmethodID, ...);
    jint        (*CallIntMethodV)(JNIEnv*, jobject, jmethodID, va_list);
    jint        (*CallIntMethodA)(JNIEnv*, jobject, jmethodID, const jvalue*);
    jlong       (*CallLongMethod)(JNIEnv*, jobject, jmethodID, ...);
    jlong       (*CallLongMethodV)(JNIEnv*, jobject, jmethodID, va_list);
    jlong       (*CallLongMethodA)(JNIEnv*, jobject, jmethodID, const jvalue*);
    jfloat      (*CallFloatMethod)(JNIEnv*, jobject, jmethodID, ...);
类方法
        返回值     |   方法名         执行环境|       操作的类    |类的方法   | 方法参数
    jdouble     (*CallStaticDoubleMethod)(JNIEnv*, jclass, jmethodID, ...);
    jdouble     (*CallStaticDoubleMethodV)(JNIEnv*, jclass, jmethodID, va_list);
    jdouble     (*CallStaticDoubleMethodA)(JNIEnv*, jclass, jmethodID, const jvalue*);
    void        (*CallStaticVoidMethod)(JNIEnv*, jclass, jmethodID, ...);
    void        (*CallStaticVoidMethodV)(JNIEnv*, jclass, jmethodID, va_list);
    void        (*CallStaticVoidMethodA)(JNIEnv*, jclass, jmethodID, const jvalue*);
最后,方法名最后的A,V或不写对应执行方法的参数 ,A一个参数,V 数组,不写表示可变参数

JNIENV

JNI接口指针仅在当前线程中起作用。这意味着指针不能从一个线程进入另一个线程。然而,可以在不同的咸亨中调用本地方法

JNIEnv是当前Java线程的执行环境,一个JVM对应一个JavaVM结构,而一个JVM中可能创建多个Java线程,每个线程对应一个JNIEnv结构,它们保存在线程本地存储TLS中。因此,不同的线程的JNIEnv是不同,也不能相互共享使用。JNIEnv结构也是一个函数表,在本地代码中通过JNIEnv的函数表来操作Java数据或者调用Java方法。也就是说,只要在本地代码中拿到了JNIEnv结构,就可以在本地代码中调用Java代码。

这里都需要传入一个 JNIEnv,这个是每个线程都有一个代表jni环境的结构体,这是通过JNIEnv来调用上边的各种操作类和对象和方法和属性的各种方法.这个JNIEnv在每个jni方法的native实现里都是默认提供的.我们需要通过

这里引入一篇很好的文章

http://luori366.github.io/JNI_doc/jni_function_mannual.html

JNIEnv首先指向一个线程相关的结构,该结构又指向一个指针数组,在这个指针数组中的每个元素最终指向一个JNI函数。所以可以通过JNIEnv去调用JNI函数

java方法签名

由于java的重载,导致光靠java方法名无法确认一个方法,因此需要方法的形参和返回值(差一点,java中,返回值不同不能代表方法重载,但是java虚拟机是支持返回值不同的重载的)

java的方法签名格式如

(参数1类型参数2类型...)返回值类型,如果类型是对象.要写L对象全路径名, 如 String类型写成L/java/language/String. object对象写成 L/java/lang/object,一维数组用[表示

image-20200225220646742

对象引用

因为java有gc垃圾回收,如果native中持有某个java对象,却被回收了.就会出问题.因此native端有对java对象的引用封装,具有不同的回收政策

LocalRef 本地引用.大部分都是这种,当该native函数返回时,java端就可以回收对应的对象

GlobalRef 全局引用, 需要native端主动释放该对象,否则java端无法回收,全局引用可以跨方法、跨线程使用,直到被开发者显式释放

WeakGlobalRef 弱全局引用,运行时可能被回收,所以native端在使用他之前需要判断是否已经被回收

这些也需要通过JNIEnv指针来操作.

 / 
     * 创建 obj 参数所引用对象的新全局引用, 创建的引用要通过调用DeleteGlobalRef() 来显式撤消
     *
     * @param obj 全局或局部引用
     * @return 返回全局引用,如果系统内存不足则返回 NULL
     */
    object NewGlobalRef (JNIEnv *env, jobject obj); 
    
    / 
     * 删除 globalRef 所指向的全局引用
     *
     * @param globalRef 全局引用
     */    
    void DeleteGlobalRef (JNIEnv *env, jobject globalRef); 
    
    / 
     * 创建 obj 参数所引用对象的局部引用, 创建的引用要通过调用DeleteLocalRef()来显式删除
     *
     * @param obj 全局或局部引用
     * @return 返回局部引用,如果系统内存不足则返回 NULL
     */    
    jobject NewLocalRef(JNIEnv *env, jobject ref);
    
    / 
     * 删除 localRef所指向的局部引用
     *
     * @param localRef局部引用
     */    
    void  DeleteLocalRef (JNIEnv *env, jobject localRef); 
    
    / 
     * 用obj创建新的弱全局引用,
     * @param obj 全局或局部因哟娜
     * @return 返回弱全局引用,弱obj指向null,或者内存不足时返回NULL,同时抛出异常
     */    
    jweak NewWeakGlobalRef(JNIEnv *env, jobject obj);
    
    / 
     * 删除弱全局引用
     */    
    void DeleteWeakGlobalRef(JNIEnv *env, jweak obj);

总结

jni就是安卓和native搭建的一套调用方式,通过java定义native方法. native端实现方法.两者尽心绑定.从而实现了java调用native, 但是这种调用是在同一线程中实现的.同时也都是从java调用native方法.native调用java方法就有点类似反射,也有点像是java虚拟机的执行方式.

android studio 中.提供了ndk简化jni的开发. 他的cmake文件指定把哪些native文件编程一个so包.so包是有针对不同平台的不同文件.

下边提供几个好的博客

https://www.jianshu.com/p/87ce6f565d37

http://luori366.github.io/JNI_doc/jni_function_mannual.html

https://www.zybuluo.com/cxm-2016/note/563686

binder 机制

binder载体 parcel

Parcel 是数据的容器,用来通过Binder发送数据.他的特点是用对象的内存直接打包起来,然后发送到对方的进程.

因此他不适合用于数据持久化(如把对象写到文件中),而只是用于跨进程传递.以为他是把对象的完整内存都保存并传递的.可以理解为夸进程传递后.两个进程里的对象是完全一摸一样的 .

binder驱动与协议

android是基于linux内核的.binder驱动也是一个标准的linux驱动,android里进程的通过binder的进程间通讯.都是要与binder驱动打交道的.binder驱动在内核态.binder驱动的位置是(/dev/binder).

每个响应使用binder通讯的进程.都要与binder驱动打交道.

binder驱动提供的接口如下.

binder_poll,(多路复用io机制,简单说就是同时监听多个io线路的变化,哪个io线路有数据变化了.就通知监听者)

各种io资料https://segmentfault.com/a/1190000003063859

binder_open, 打开binder驱动.每个进程都要,通过(ProcessState打开)每个进程会分配最大4M的binder驱动内存,这个内存是逐渐分配的.初始为一页,同时把这个内存和程序的内存建立映射关系.

binder_ioctl, 对驱动内存的操作.主要是读写操作.通过这个.将数据在进程内存和内核中传递.从而实现ipc

binder_mmap, 内存映射.进程和内核binder驱动通过内存映射到同一个内存空间上,二者都有指向这块内存的指针.这样内核把数据拷贝到这个内存空间上,对应的进程就拿到了数据.也就是获得了跨进程传递的数据.

binder服务管理-ServiceManager

Servicemanager 功能

大部分的系统服务.因为需要使用binder机制,又为了简化.所以都注册到ServiceManager中(这一过程也是一次binder通信).而ServiceManager是android在init进程时启动的.并且作为默认的binder管家.APP可以通过binder号为0而拿到这个ServiceManager的代理.从而获得其他的系统服务.

ServiceManager在启动时会把自己注册为整个系统的binder管家.然后打开binder设备.进程mmap内存映射.

然后ServiceManager就进入了循环,通过ioctl来监听binder驱动发来的消息(也就是其他进程与ServiceManager的通信请求),

然后把请求转换到具体的处理函数中进行处理

最后把处理的结果写回给binder驱动(传递回发送请求的进程.

ServiceManager对外提供的服务如getService,addService,checkServic,listService.

serviceManager会把执行服务的结果写回binder驱动

获取ServiceManager流程

我们如何向ServiceManager获取服务呢?通常有如下几部

打开binder设备->执行mmap内存映射->通过binder驱动向ServiceManager发送请求(sm的handle为0)->获得结果

这里有些步骤是所有binder进行ipc都需要的.因此需要进行封装,不用让我们每次使用时手写.

ProcessState 是进程唯一的.用来在进程开始时打开binder设备,执行mmap内存映射

IPCThreadState 是每个线程唯一的.每个线程都可以进行binder通信,并且biner通信的实际代码也是在IPCThreadState中实现的.

Proxy. proxy和服务实现一个共同的服务接口I Service,具有相同的功能, proxy在客户端.客户端调用它,就相当于调用服务进程的特定服务.总层次如下

image-20200301220817905

ServiceManagerProxy

在ServiceManager中.在client会获取一个代理类. ServiceManagerProxy ,他和服务端的ServiceManager都实现了 IServiceManager 接口,也就是具有相同的功能.而代理类对功能的处理则是通过内部的IBinder类的mRemote的 transact 方法.来进行ipc通信.此时这个线程会被挂起.等待服务端返回后才能继续执行.

BpBinder和BinderProxy

因此我们看到.具体具体业务的执行.都会有proxy代理类转换到IBinder类的对象来处理.那么这个对象那里来的的?

这个Binder就是来自client进程中native端的ProcessState.方法如下,这里创建的就是BpBinder

sp<IBinder> ProcessState::getContextObject(const sp<IBinder>& caller)

因为代码设计到了java层和native层.所以有很多类在两层都有相似的名称.

如binder的客户端在java层是BinderProxy,在native层是BpBinder.他俩是对应的.并且能够找到对方.因为java层的binder是由native层的BpBinder对象创建而来的.

这样. ServiceManagerProxy 的请求方法就转到了client进程中java里的BinderProxy.transact来处理了.而BinderProxy.transact方法是native方法.这又转到了native层.找到对应的native层的BpBinder来处理.

这里简单说就是client端的 ServiceManagerProxy 的请求,最后倒流client端native里的BpBinder.transact方法里了.

而BpBinder.transact的方法.又是由 IPCThreadState . transact 来执行的.

流程如下,下边所有方法都是client进程的.

(Java)ServiceManagerProxy.getService ->(java)BinderProxy.transact ->(native)BpBinder.transact->(native)IPCThreadState.transact

ProcessState

一个进程只有一个ProcessState.他在创建时打开binder驱动,进行mmap内存映射.也就是一个进程之后进行一次内存映射,里边的线程都使用者一块内存.并且ProcessState会保存该进程创建的所有的binder信息,这里的binder是BpBinder. 每个BpBinder都和一个int的handle绑定.用来指明这个代理的binder的主人.

主要.这时候.新建的BpBinder只有一个handle标识.并没有和远程服务有什么关系.ServiceManager作为系统服务的大管家,他的handler默认是0.

上文我们看到.BpBinder.transact最后转化到了IPCThreadState.transact.的方法里.这里的IPCThreadState是线程单实例的.我们继续分析IPCThreadState,他是真正通过binder驱动与远方发送数据的地方.

IPCThreadState进行ipc通信

transact函数

Handle 表示要照的server的服务的标记, code是要执行什么服务,data和reply是发送的数据和接受的响应.flags是一些模式.可以指定异步,异步就是直接返回.不等待执行结果.
status_t IPCThreadState::transact(int32_t handle,uint32_t code, const Parcel& data,
  Parcel* reply, uint32_t flags)
{
 flags |= TF_ACCEPT_FDS;
    if (err == NO_ERROR) { 把要发出的数据整理好.写到mOut里.
        err = writeTransactionData(BC_TRANSACTION, flags, handle, code, data, NULL);
    }
    
    if ((flags & TF_ONE_WAY) == 0) {发送数据,得到结果
        if (reply) {
            err = waitForResponse(reply);
        } else {
            Parcel fakeReply;
            err = waitForResponse(&fakeReply);
        }
        
    }
    return err;
}

waitForResponse函数

status_t IPCThreadState::waitForResponse(Parcel *reply, status_t *acquireResult)
{
    int32_t cmd;
    int32_t err;

    while (1) {
            //真正执行与驱动交换数据
        if ((err=talkWithDriver()) < NO_ERROR) break;
        cmd = mIn.readInt32(); //返回结过的不同处理.一会在看
        switch (cmd) {
            case BR_TRANSACTION_COMPLETE:
          case BR_FAILED_REPLY:
            case BR_REPLY:
        }
    }

talkWithDriver

这个函数读写都是在这执行.具体是读还是写则是有mIn和mOut的数据决定的.,把数据写入bwr中.

status_t IPCThreadState::talkWithDriver(bool doReceive)
{
    //这个是与驱动打交道的数据结构.他负责写出数据和读入数据
    binder_write_read bwr;
    
    //mOut就是负责要写出的书,min是负责读入的数据,他们里边都有个位置计算已经读了多少,还剩多少.
    //需要用mIn和mOut来初始化bwr, 来确定需要读写的数据和数据大小
    bwr.write_size = outAvail;
    bwr.write_buffer = (long unsigned int)mOut.data();

    // 这里是处理要读的数据
    if (doReceive && needRead) {
        bwr.read_size = mIn.dataCapacity();
        bwr.read_buffer = (long unsigned int)mIn.data();
    } else {
        bwr.read_size = 0;
    }
    status_t err;
    do {
         //与binder驱动进行数据交换. BINDER_WRITE_READ这个命令很重要.我们以后要驱动的里这部分.
        if (ioctl(mProcess->mDriverFD, BINDER_WRITE_READ, &bwr) >= 0)
            err = NO_ERROR;
        else
            err = -errno;
    } while (err == -EINTR);
    //根据数据交换的结果.来确定本次读了多少.写了多少.更新mIn和mOut的数据尺度.
    if (err >= NO_ERROR) {
        if (bwr.write_consumed > 0) {
            if (bwr.write_consumed < (ssize_t)mOut.dataSize())
                mOut.remove(0, bwr.write_consumed);
            else    
                mOut.setDataSize(0);
        }
        if (bwr.read_consumed > 0) {
            mIn.setDataSize(bwr.read_consumed);
            mIn.setDataPosition(0);
        }
         
        return NO_ERROR;
    }
    return err;
}

mIn和mOut解释如下,他俩都是Parcel类的对象.MDataCapacity表示改对象的数据最大容量.

其实读写的过程就是要使mDataPos最后等于mDataSize.

image-20200301234611274

上边通过ioctl和驱动进行了数据交换,再把交换结果通过mIn和mOut更新.也就知道本次写和读的情况.

ioctl(binder驱动)

如果是同步执行(就是等待返回结果).会把client进程挂起.等待服务端响应后在唤醒服务端.

copy_from_user从用户空间负责数据到内核中.

根据用户进程中talkWithDriver中填写的bwr数据分别处理.

有数据要写出就执行binder_Thread_write,有数据要读入就执行binder_thread_read.如果失败了.还有把负责到内核的数据在复制回用户控件.

image-20200302001403716
image-20200302001425557

下边看binder_Thread_write如何写出数据(这里有个疑问.已经到了驱动,但是没看到那个标识指向要访问的服务端啊?--更正,在IpcThreadState.writeTransactionData包装数据时候,把handle写在了mOut里了.)

通过客户端指定的handle.找到目标对象的target_node.进入找到目前对象的进程target_proc和线程target_Thread.

拿到target_thread目标线程的todo和wait列表.然后生成一个binder_transaction变量,加入到目标线程的todo队列中.(这样目标线程被唤醒,就可以从list中拿到client端的请求).又生成一个binder_work变量,加入到client对应线程的todo队列中.

然后填写发给对方的binder_transaction数据,包括调用者和目标对象的信息,和本次转换的内存.

最后唤醒对方服务进程.

这是分成两路.client的执行和server的执行.

驱动返回client

先看client执行binder_Thread_write后又判断是否要读,会执行binder_Thread_read

从client的todo队列中取出一个请求(上边写数据的时候放进来的binder_work),取出的这个命令又向上报.直接到IPCThreadState.waitForResponse中talkWithDriver后的命令处理,此时命令cmd为BR_TRANSACTION_COMPLETE,然后结束此次循环,再次开始执行while循环,继续talkWithDriver.

这就代表是本次发送已经完成.然后继续talkWithDriver与驱动交互.但这次已经没有数据要写出了.因此只执行数据读.然后进行睡眠.等待服务端处理完在唤醒.

也就是说.在client端.talkWithDriver把数据写出后,内核返回信息使client再次执行waitForResources里的wihle循环,然后再等待读取消息时进入睡眠.

大概流程倒是这样的

binder-clent12415

serviceManager被唤醒

ServiceManager在启动时,会先打开binder驱动,然后进行mmap.再把自己设置成系统的大管家.然后就进入了一个无线的for循环,通过ioctl监听只读请求,等待客户端请求.因此会进入驱动中的binder_Thread_read.此时没有数据到来.就会进入休眠

ServiceManager被唤醒后.会检查todo队列是否有要处理的请求.如果有.就交给ServiceManager来处理.此时会到ServiceManager的 binder_parse 中解析

binder_parse中,对不同命令进行分别处理.主要作用就是得到客户端要请求的数据,进行封装.然后通过ioctl交给binder驱动,写回给用户端.

ServiceManager中,组装的数据是各个注册到ServiceManager中的服务对应的一个binder驱动中的id号.通过这个id.可以找到对应的服务.(驱动中,通过这个id号,可以找到对应进程中对binder驱动进行的内存映射区域,然后就能给这个进程发送数据了)

ServiceManager中组装好数据后,里边包含要获得的服务的指针,在通过ioctl写回驱动.

驱动里从这数据中拿到要返回的client的信息,找到client的进程和线程,找到对应的todo和wait队列,给client的todo队列写一个变量.给ServiceManager的todo队列也写一个变量.然后唤醒client进程.并把ServiceManager要发送给client的数据返回给他.

ServiceManager在接着处理我方todo队列的消息.然后也随之进入睡眠了.等待下一次唤醒

内核在通过copy_to_user,把数据复制到client进程的空间.用户控件就继续执行talkWithDriver之后的代码.此时就拿到了server端的数据了.

client线程在把收到的数据转换成一个binder.在提供出去,在封装成各种 proxy.就可以使用了.

binder-server12415

在驱动中,各个进程的binder是通过一个句柄值(handle)来区分的.服务端也是把这个句柄值和一些数据返回给用户端,从而完成夸进程通信.

总体概括

binder 是安卓特有的进程间通讯的机制.因为不同进程的内存是不同的.不能直接进行数据交换.linux本有一些跨进程通讯的方式.本质上都是把数据在两个进程间传递.而binder机制是为安卓设计的.他的特点是只涉及一次内存复制.就比较高效.且binder机制进行了分层.把底层的数据拷贝和上层也业务进行了分类.使上层使用时,不必写重复的bindner先关的代码.进程都有一个binder标准.通过该标志,可以找到对应的进程.

考虑一下传统的IPC方式中,数据是怎样从发送端到达接收端的呢?通常的做法是,发送方将准备好的数据存放在缓存区中,调用API通过系统调用进入内核中。内核服务程序在内核空间分配内存,将数据从发送方缓存区复制到内核缓存区中。接收方读数据时也要提供一块缓存区,内核将数据从内核缓存区拷贝到接收方提供的缓存区中并唤醒接收线程,完成一次数据发送。这种存储-转发机制有两个缺陷:首先是效率低下,需要做两次拷贝:用户空间->内核空间->用户空间。Linux使用copy_from_user()和copy_to_user()实现这两个跨空间拷贝,在此过程中如果使用了高端内存(high memory),这种拷贝需要临时建立/取消页面映射,造成性能损失。其次是接收数据的缓存要由接收方提供,可接收方不知道到底要多大的缓存才够用,只能开辟尽量大的空间或先调用API接收消息头获得消息体大小,再开辟适当的空间接收消息体。两种做法都有不足,不是浪费空间就是浪费时间

image-20200301203239363

binder机制最底层是binder驱动.他是如何实现一次复制就在两个进程中传送数据的?

原理如下图

image-20200301203437457

驱动层

这利用了内存映射的原理,我们假设目前是A进行向B进程发送数据.而在B进程启动之初,就对binder驱动进行了内存映射,(binder驱动工作在内核态,可以访问任意的内存地址),操作是先打开binder驱动.在操作获得的描述符进行内存映射,内存映射的结果就是binder驱动和B进程都获得了一块物理内存,并且binder驱动和B进程都有一个指针指向这个物理内存(这个物理内存在内核上分配),这样.b进程和Binder驱动任意一方对这个内存的操作,都能让对方知道变化.当A进程要向B发送数据时,会调用Binder驱动,binder驱动通过copy_from_user把数据从进程A中拷贝到Binder驱动中,而拷贝的内存位置就是进程B和Binder驱动进行映射的这块内存.这样从进程A来说,就是把数据从用户进程A拷贝到了binder驱动的内核中.而对于进程B.则看到驱动把数据从进程A直接拷贝到自己的内存中.这就实现了一次拷贝在两个进程中传递数据.

这里几个注意的点. 进程与binder驱动建立内存映射的时候.使用的内存是binder驱动的,是在内核中.

进程B建立内存映射后.通过监听这端内存的io变化.就知道是否有数据到来了.

因此.每个进程在启动的时候,都会与binder驱动建立内存映射关系,(在开始时,只分配一个比较小的内存用来提高进程创建速度,且节省内存)

Binder层

在驱动层面已经实现了基础的数据传输.这过程是固定且麻烦的.因此系统进程封装后.我们直接使用就可以.

在业务层.要处理的就是.把数据封装成固定的格式.交给驱动去传递.然后到对方进程时能正确的解析出来.进行处理.

这里使用了parcel,他是一种数容器.直接把要传输的数据的整个内存给传输过去了.保证两边对象的一致性.

为了进一步简化,系统有封装了binder这一结构. 他代表在两边传递的数据.android里大多用binder的是各种服务.

因此先定义一种共同的接口interface,在两个进程实现.client中则是把请求转换成binder处理.而server端就是从binder中拿出请求,完成具体的工作.这里就是更进一步的抽象封装,业务层只需要设计具体的服务如何提供,然后再把数据一封装就完成了

这里产生的binder层,在client端是bpbinder,在server端是bbinder.这是用来封装数据并发送过去的一层.

代理层

在上边是业务层.两端都有实现同样接口的类.处理具体的功能,如activitymanagerservice在服务端,对应客户端是activitymanagerproxy(我自己起的名字,) 客户端的调用 activitymanagerproxy的具体功能,activitymanagerproxy再把数据交个bpbinder进行封装.bpbinder封装完成后,把数据通过IpcThreadState来发送数据,在通过binder驱动发送过去.在server端.先是binder驱动的监听知道数据到来.在转成bbinder解析数据,在交给activitymanagerservice执行.

同时为了简化.加入了ServiceManager,统一管理所有的系统服务.系统服务要先注册到ServiceManager,才算对外暴露.APP像使用什么服务时,先想ServiceManager,得到一个对应服务的代理.这个代理把想执行的请求,和远端的binder是具有联系的.通过他就可以进行binder通信

上文的binder.不一定是framework中的binder类.只是一个功能的描述.

所以.最底层是binder驱动.需要每个进程在启动时打开驱动,进行内存映射,通过ProcessState

上层是IPCThreadState.他是每个线程唯一,真正用数据与binder驱动交互的地方.发送数据,得到请求

在上层是Binder,在发送数据时,通过binder把数据发给IPCThreadState,而在获得了远程的数据返回后.IPCThreadState又回把数组封装成一个binder,向上传递.

可以这样理解,每个线程可以进行很多次 跨进程通讯,但是每次可能远程通信都不同. IPCThreadState提供了远程通信的通道.但是通道的内容是由binder决定里的.所以同一个线程里.通道有一个,但是内容有很多.

在上一层.把binder封装成proxy.提供给客户端.就彻底隐藏了底层的复杂机制.上层只需要操作这个代理类就可以了.

最外边则是通过ServiceManager的代理类来拿到其他服务的代理. ServiceManager作为系统服务关键.默认为handle号为0.可以直接获取他的代理.这次,在看这个分层.就很清楚了.

同时还要知道一点.java层的binder只不过是native层的一个镜像.二者是绑定的.java层主要功能都是通过native层来实现的

image-20200301220817905

系统启动

系统组成

系统启动时,会加载一系列的其他服务,这是通过读取配置文件实现的.

系统启动的第一个进程是init进程,他会读取init.rc配置文件来启动过各种服务

init.rc

分为 actio(动作),commands(命令) services(服务)options(选项)

on<trigger> 触发条件                                                                            
    <command1> 执行命令1                                                                        
    <command2>  执行命令2                                                                       
    也就是在发送了trigger动作时,执行命令1和命令2 
举例
on boot //触发boot事件时
mkdir  /dev  创建dev目录
class_start default 启动所有default标
    
service 是定义的可执行程序,他会列出要执行的文件的路径,和附带的约束,通常运行于另外的进程中.
services <name><pathname>[ arguement]
    <option>
    <option>
举例
service zygote /system/bin.app_process  定制zygote服务
    socket zygote 666                                           启动socket,

ServiceManager(init启动)

ServiceManager也是由init.rc配置文件里面并由 init进程 启动的一个额外的进程.他同zyote进程绑定.同生共死.他负责所有binder通信的注册和管理.

zygote(init启动)

也是由init.rc进程创建的另一个进程.用于fork进程用,他启动后,会通过socket 监听systemServer发来的请求.然后fork自己创建新进程.安卓里大多数应用进程和系统进程都是他fork出来的.也是由init.rc配置文件里面试并由init进程启动的.他死亡时会杀掉他所有创建的子进程.

主要流程

调用AppRuntime.start.把任务都交个他处理

AppRuntime.start里.创建java虚拟机,动态注册大量jni函数,调用ZygoteInit.main方法.

ZygoteInit.main里 1注册ZygoteInit用的socket.自己作为服务端,2反射加载类和framework.apk中资源.3通过forkSystemServer来启动system_server进程4.开启循环.等待socket客户端的连接.

zygote进程和systemServer进程是同生共死的.systemServer死了.zygote进程也会杀死自己.

systemServer(zygote启动)

由zygote进程创建的另一个进程.由zygote启动的虚拟机中的java类ZygoteInit来启动.通过forkSystemServer.来生产一个新进程.用于加载各种服务.

启动时执行 handleSystemServerProcess 里边调用了RuntimeInit. zygoteInit

这里边创建了1.ProcessState(进程独有的)与binder通信系统建立联系.然后2.启动SystemServer(java)的main函数

SystemServer里执行了init1,开启图形和声音的服务和 init2,启动了serverThread,初始化各种服务.又执行ProcessState和IpcThreadState的线程服务,开启了两个binder通信线程.

总之就是在native端和java端启动了各种服务.init1的服务是native的.init2的服务是java端的,通过线程启动.

启动其他进程

当需要启动一个进程时.通过向zygote进程发送socket信息来让zygote进程fork出新进程.以activitymanager.创建socket及发送给zygote是有process.java类实现

zygoteInit.java中通过ZygoteConnection.runOnce来处理收到的请求.然后就是fork出新进程.然后让子进程处理请求.

image-20200304001205885

ActivityManagerService

AMS由SystemServer创建,并且SystemServer会等AMS启动完在继续执行.

原理是SystemServer调用AMS的main方法. main方法中开启一个线程.然后SystemServer会在这里wait,等到线程里创建AMS成功后,AMS的线程notify唤醒SystemServer继续工作.

Ams的主要任务是负责系统内四大组件的启动,切换,调用及应用进程的管理和调度.

ActivityStack 则是用来管理Activity状态的类.他里边保存了几个集合,用来存储各种状态的Activity

image-20200304221404144

Activity在Ams里都抽象成了ActivityRecord 类

值得注意的是,AMS因为需要调用用户进程,所以每个进程在启动时候,都会与ams建立链接,同时ams也会获得一个向用户进程发消息的binder(ApplicationThread),通过他,AMS就可以主动向用户进程发消息,执行一些操作.此时AMS变成了binder的client端,用户进程成了binder的server端. 这种方式是匿名binder.

AMS启动

Ams在SystemServer的线程中启动,大概步骤如下,也就是先启动Ams.然后又等待Ams的启动回调.

//创建线程,启动

context = ActivityManagerService. main (factoryTest);

//把systemServer 加入到Ams中管理

ActivityManagerService. setSystemProcess ();

//初始化系统provider

ActivityManagerService. installSystemProviders ();

//Ams和Wms(WindowManagerService)建立联系

((ActivityManagerService)ServiceManager. getService ("activity"))

​ . setWindowManager (wm);

//ams启动完成的回调,ams的 systemReady 完成后,其他服务的 systemReady 才能被调用.

((ActivityManagerService)ActivityManagerNative. getDefault ())

​ . systemReady (new Runnable () {})

ActivityManagerService.main

启动一个线程AThread,创建AMS后进入looper循环

Ams的创建中初始化几个系统运行状态服务,如ProcessStats,是一个统计信息的服务.并创建一个线程用来更新统计信息.

创建几个系统服务service

通过ActivityThread.main 创建ActivityThread(此时在SystemServer线程).

ActivityThread会为"android"(也就是faramwork.apk)这个app创建application和context并进行关联.这是系统资源APP.所以AMS为他初始化了和应用APP一样的环境.

总结就是.启动一个loop线程,创建AMS,为系统APP创建ActivityThread.Application,Context.

此时的ActivityThrad运行在SystemServer进程中.是系统APP(framework.apk)的主线程

ActivityManagerService.setSystemProcess

把AMS注册到ServiceManager中.

获取系统APP(framework.apk)的ApplicationInfo,ApplicationInfo是PackageManagerServic中对应用信息的抽象.对应AndroidManifest里的application节点

调用ActivityThread. installSystemApplicationInfo 这里真正将系统APP的ApplicationInfo和Context绑定起来.此时 AMS为系统APP在System_server中制造的APP的运行时环境才算完整.

为系统APP创造ProcessRecord,代表进程的记录.并保存在AMS中.ProcessRecord是AMS里对APP进程的一个抽象.用来保存进程的信息. ProcessRecord中还保存了AMS和用户进程通信的IApplicationThread,在客户端对应的则是ApplicationThread. AMS通过IApplicationThread来调用客户端,这也是一个binder通信(通常都是binder通信.但是在系统服务这里.是同一进程的.则不是binder通信)

总结,主要是把AMS注册到ServiceManager,然后通过PKMS返回的ApplicationInfo,创建system_server进程的ProcessRecord.加入AMS的管理.(因为framework.apk.运行在system_server进程)

ActivityManagerService.systemReady

发送ACTION_PRE_BOOT_COMPLETED广播

杀死先于AMS启动的应用进程.

完成其他服务的 systemReady 方法,再启动系统桌面home

home启动成功后,会回到Ams的finishBooting方法,然后Ams发送ACTION_BOOT_COMPLETED广播.

总结

AMS的main函数,创建AMS对象,开启一个looper线程,为framework.apk 初始化环境(创建ActivityThread.Application,Context.)

AMS的setSystemProcess . 注册AMS和一些运行信息服务到ServiceManager.并为system_server创建processRecord.表示system_server进程也作为AMS管理的用户进程.(因为system_server中有framework.apk等)

AMS的installSystemProviders. 为AMS加载 SettingsProvider

AMS的systemReady.扫尾工作.启动系统桌面. Home Activity.

startActivity 分析

从Am.java 分析Activity的启动. am是 提供的脚本.用来和AMS交互. 如

am start -W -n com.test/MainActivity 启动com.test包下的 MainActivity. am 命令要 通过adb shell 登录手机后执行

ActivityStack简介

mMainStack是ActivityStack类. 他负责组织 Activity的task栈. task栈是用来完成同一任务的结果.不同APP的Activity可以在一个栈里. 安卓还支持多个task 栈.但是只能有一个在前台, 当一个栈在前台时,点击返回按钮,则会把这个task栈的所有Activity都清空,然后在回调别的task栈上.

ActivityStack 及相关成员如下

image-20200307130000093

可以看到.在AMS中.所有的Activity都抽象成ActivityRecord. 而task则由TaskRecord表示. ActivityRecord知道自己属于哪个task.而ActivityStack则通过mHistory管理ActivityRecord.

image-20200307130710483

ActivityStack里有很多不同状态的ActivRecord ,因此ActivityStack肯定要让ActivityRecord在不同状态切换

ArrayList<ActivityRecord> mStoppingActivities

ArrayList<ActivityRecord> mGoingToSleepActivities

ArrayList<ActivityRecord> mWaitingVisibleActivities

ArrayList<ActivityRecord> mHistory

ActivityStack 主要功能如下

topRunningActivityLocked 找到正在运行的ActivityRecord

findActivityLocked 根据intent和ActivityInfo.来匹配ActivityRecord

所有大概来看 ActivityStack 就是通过保管TaskRecord和ActivityRecord. 来管理Activity和task 的关系.(四种启动模式)

ActivityStack.startActivityMayWait

AMS的启动从 startActivityAndWait 开始

mMainStack.startActivityMayWait(caller, -1, intent, resolvedType,
                resultTo, resultWho, requestCode, startFlags, profileFile, profileFd,
                res, null, options, userId);

startActivityMayWait 的大概流程是.

创建ActivityRecord.得到TaskRecord(如果有FLAG_ACTIVITY_NEW_TASK标识),还要启动一个新进程来加载Activity实例.如果当前有展示的Activity.还要停止当前Activity.精简代码如下

final int startActivityMayWait(){
//1.通过intent 从 PackageManagerService中得到ActivityInfo
ActivityInfo aInfo = resolveActivity(intent, resolvedType, debug,
                profileFile, profileFd, autoStopProfiler);
      //2.执行函数Activity启动          
     int res = startActivityLocked(caller, intent, resolvedType,
                    grantedUriPermissions, grantedMode, aInfo,
                    resultTo, resultWho, requestCode, callingPid, callingUid,
                    onlyIfNeeded, componentSpecified, null); 
 //3.等待用户进程启动成功后回调AMS,保存该Activityinfo 启动结果.

 if (res == IActivityManager.START_SUCCESS) {
         mWaitingActivityLaunched.add(outResult)
         do {
                 try {
                     mService.wait();
                   } catch (InterruptedException e) {
                  }
             } while (!outResult.timeout && outResult.who == null);
       }
                    
}

那么.第二步就是启动Activity的地方.这里有两个同名函数,此时是走参数多的那个.

ActivityStack.startActivityLocked(参数多的)

主要是进行权限的检查,和activity的来龙去脉的设置

  1. 先是拿到调用者进程的pid和uid.看是否正确,
  2. 在设定 要启动Activity的 sourceRecord和resultRecord这俩都是(ActivityRecord),表示谁启动的新Activity.和新Activity完成后 返回到那里.
  3. 处理intent的flag
  4. 检查调用者权限.用1步骤的pid和uid看调用者使用有权限启动Activity.
  5. 创建要启动Activity的ActivityRecord对象.
  6. 启动Activity.
startActivityLocked{
//1.先是拿到调用者进程的pid和uid.看是否正确
  if (callerApp != null) {
      callingPid = callerApp.pid;
      callingUid = callerApp.info.uid;
  }

  2.//然后在设定 要启动Activity的 sourceRecord和resultRecord这俩都是(ActivityRecord),表示谁启动的新Activity.和新Activity完成后 返回到那里
   ActivityRecord sourceRecord = null;
  ActivityRecord resultRecord = null;

  3.处理flag
    int launchFlags = intent.getFlags();
  4. 检查调用者权限
      final int perm = mService.checkComponentPermission(aInfo.permission, callingPid,
                callingUid, aInfo.applicationInfo.uid, aInfo.exported);
  5. 创建一个ActivityRecord 对象.就是我们要启动的Activity的在AMS的代表
      ActivityRecord r = new ActivityRecord(mService, this, callerApp, callingUid,
                intent, resolvedType, aInfo, mService.mConfiguration,
                resultRecord, resultWho, requestCode, componentSpecified);
    6.启动Activity
    err = startActivityUncheckedLocked(r, sourceRecord,
                grantedUriPermissions, grantedMode, onlyIfNeeded, true);
}


ActivityStack.startActivityUncheckedLocked

  1. 根据intent.flag来为新的Activity选择合适的task.
  2. 找到一个ActivityRecord
  3. 为新的ActivityRecord 设置 taskRecord,此时新Activity的 ActivityRecord 和TaskRecord都准备好了
//1.根据intent.flag来为新的Activity选择合适的task.
launchFlags |= Intent.FLAG_ACTIVITY_NEW_TASK;
//2. 找到合适的 ActivityRecord
  ActivityRecord taskTop = r.launchMode != ActivityInfo.LAUNCH_SINGLE_INSTANCE
                        ? findTaskLocked(intent, r.info)
                        : findActivityLocked(intent, r.info);
//3.为 新的ActivityRecord 设置 taskRecord
if (reuseTask == null) {
      r.setTask(new TaskRecord(mService.mCurTask, r.info, intent), null, true);
  } else {
      r.setTask(reuseTask, reuseTask, true);
  }
  //一个同名函数,这次是参数少的,里边主要是执行 resumeTopActivityLocked.
  startActivityLocked(r, newTask, doResume, keepCurTransition, options);

ActivityStack.resumeTopActivityLocked

  1. 找到要启动的Activity
  2. 暂停旧的Activity
  3. 新的Activity和wms搭配,继续执行新Activity的启动.
//找到队列中栈顶中运行的Activity.这是我们要启动的那个
ActivityRecord next = topRunningActivityLocked(null);
//暂停目前正在显示的Activity.(暂停原来的Activity.好让新的Activity显示出来)
//这里.ams会等待旧Activity暂停.暂停后客户端会回调ams的resumeTopActivitLocked.然后继续启动新的activity
if (mResumedActivity != null) {
      (userLeaving, false);
      return true;
  }
//  再次执行时,旧Activity已经暂停了.此时要启动新Activity.
  //新Activity和wms绑定
   if (SHOW_APP_STARTING_PREVIEW && mMainStack) {
          mService.mWindowManager.setAppStartingWindow(
                  next.appToken, next.packageName, next.theme,
                  mService.compatibilityInfoForPackageLocked(
                          next.info.applicationInfo),
                  next.nonLocalizedLabel,
                  next.labelRes, next.icon, next.windowFlags,
                  null, true);
      }
  }
  //继续启动新Activity.
  startSpecificActivityLocked(next, true, true);

此时这里产生了分叉.旧的Activity需要暂停.然后新的Activity在进行启动.我们先看新的Activity的启动.旧Activity的暂停以后再看

ActivityStack.startSpecificActivityLocked

  1. 先拿到新Activity对应的进程
  2. 如果进程已经启动.就继续启动Activity
  3. 进程没启动,先启动Activity所在进程
processRecord 是进程在AMs的抽象对象. 这里看新Activity的进程是否存在
ProcessRecord app = mService.getProcessRecordLocked(r.processName,
                r.info.applicationInfo.uid);
  //如果集成已经启动.就直接启动Activity.
  if (app != null && app.thread != null) {
                app.addPackage(r.info.packageName);
                realStartActivityLocked(r, app, andResume, checkConfig);
                return;
  }
  //进程没启动,先启动进程
  mService.startProcessLocked(r.processName, r.info.applicationInfo, true, 0,
                "activity", r.intent.getComponent(), false, false);

这里又分叉了. 我们按复杂的走.先启动进程

AMS.startProcessLocked

  1. 通过启动ActivityThread 来启动用户进程,同时保存用户进程的ProcessRecord在ams中

ActivityThread 就是用户线程.当然这个本质是通过zygote进程fork出的新进程.然后执行ActivityThread的main函数.

  1. 发送延时消息(10s),如果10s内用户进程没启动完毕并向AMS注册.则认为用户进程启动失败.
来启动用户进程,执行ActivityThread ,同时保存用户进程的ProcessRecord在ams中
 Process.ProcessStartResult startResult = Process.start("android.app.ActivityThread",
                    app.processName, uid, uid, gids, debugFlags,
                    app.info.targetSdkVersion, null);
  AMS发送一个超时消息.等待Activity的进程启动完与ams沟通.也就是如果这个超时消息响应时,新Activity的进程还没启动完并与ams沟通.则认为 用户进程启动失败.
  synchronized (mPidsSelfLocked) {
      this.mPidsSelfLocked.put(startResult.pid, app);
      Message msg = mHandler.obtainMessage(PROC_START_TIMEOUT_MSG);
      msg.obj = app;
      mHandler.sendMessageDelayed(msg, startResult.usingWrapper
              ? PROC_START_TIMEOUT_WITH_WRAPPER : PROC_START_TIMEOUT);
  }
image-20200307164943836

到这里.我们知道.一个Activity 的启动.在AMS端.需要有一个ActivitRecord,TaskRecord.ProcessRecord.来对应.

同时,还要处理他原有Activity的暂停,旧的Activity的暂停.早于新Activity 的启动.

ActivityThread.main

  1. 开启消息循环loop
  2. 创建ActivityThread对象.执行attach方法

        Looper.prepareMainLooper();
        if (sMainThreadHandler == null) {
            sMainThreadHandler = new Handler();
        }
    
        ActivityThread thread = new ActivityThread();
        thread.attach(false);
    
        Looper.loop();

ActivityThread.attach

  1. 主要就是与AMS建立binder通信. 这里的mAppThread 是ApplicationThread类,这里ActivityThread的内部类,传递给AMS,AMS就可以通过 他来主动调用ActivityThread的方法.也就是AMS主动通过Binder与ActivityThread交流.此时AMS是binder的client端.
 IActivityManager mgr = ActivityManagerNative.getDefault();
        try {
            mgr.attachApplication(mAppThread);
        } catch (RemoteException ex) {
        }

ApplicationThread 在收到AMS的消息后.会通过handler.把消息发给ActivityThread来执行.

   public final void scheduleResumeActivity(IBinder token, boolean isForward) {
            queueOrSendMessage(H.RESUME_ACTIVITY, token, isForward ? 1 : 0);
        }

ActivityThread 里的attac方法.接收方是AMS里的 attachApplicationLocked

AMS. attachApplicationLocked

  1. 把用户进程发来的binder和AMS对应的ProcessRecord绑定起来.
  2. 移除AMS之前发出的等待用户进程启动的消息
  3. 通知用户进程初始化android的运行环境,并创建application对象.执行oncreate.
  4. 找到之前正在等待启动的activity,然后继续启动他
 private final boolean attachApplicationLocked(IApplicationThread thread,int pid) {
    //把用户进程传来的ApplicationThread 和一个processRecord 绑定起来.
        ProcessRecord app;
        app = mPidsSelfLocked.get(pid);
        app.thread = thread;
        //移除AMS之前发出的等待10s内用户进程启动的消息        
    mHandler.removeMessages(PROC_START_TIMEOUT_MSG, app);
    //通知用户进程初始化android的运行环境,在用户端会初始化很多东西.并创建application对象.执行oncreate.
     thread.bindApplication(processName, appInfo, providers,
                    app.instrumentationClass, profileFile, profileFd, profileAutoStop,
                    app.instrumentationArguments, app.instrumentationWatcher, testMode,
                    enableOpenGlTrace, isRestrictedBackupMode || !normalMode, app.persistent,
                    new Configuration(mConfiguration), app.compat, getCommonServicesLocked(),
                    mCoreSettingsObserver.getCoreSettingsLocked());
      
      //找到之前正在等待启动的activity,然后继续启动他
     ActivityRecord hr = mMainStack.topRunningActivityLocked(null);
     mMainStack.realStartActivityLocked(hr, app, true, true)
     
     //后面还有启动Service.不看了
  }

总结,用户进程的启动由AMS决定.AMS通过zygote来fork出用户进程,用户进程启动后主动向AMS注册.AMS再把这个用户进程和一个ProcessRecord 绑定起来.同时回调用户进程进行初始化.用户进程在初始化资源,创建Application.并执行onCreate.

ACtivityStack.realStartActivityLocked

此时,用户进程已经启动完毕. ActivityRecord, TaskRecord,ProcessRecord 都已经准备好了.用户进程中Application已经创建并执行onCreate.继续执行Activity

  1. 通知用户进程启动Activity.
  2. 等待用户进程Activity启动,执行完onResume后通知AMS,这里AMS也是超时等待,10s内APP没通知,就认为app启动失败.
 final boolean realStartActivityLocked(ActivityRecord r,
            ProcessRecord app, boolean andResume, boolean checkConfig){
   //processRecord 把ActivityRecord 保存起来.         
   app.activities.add(r);
    //通知用户进程启动activity.通过ApplicationThread进行binder通信来实现.
     app.thread.scheduleLaunchActivity(new Intent(r.intent), r.appToken,
                    System.identityHashCode(r), r.info,
                    new Configuration(mService.mConfiguration),
                    r.compat, r.icicle, results, newIntents, !andResume,
                    mService.isNextTransitionForward(), profileFile, profileFd,
                    profileAutoStop);
    //等待用户进程Activity启动,执行完onResume后通知AMS,这里AMS也是超时等待,10s内APP没通知,就认为app启动失败.                
     completeResumeLocked(r);
}

此时AMS又通知Activity进行启动.我们要再次来到用户进程.ApplicationThread 通过发消息.传递给ActivityThread.handleLaunchActivity方法

ActivityThread.handleLaunchActivity

  1. 执行activity的onCreat方法 和onRestart方法
  2. 执行activity的onResume方法.同时用handle发送了一个Idler消息.Idler消息会在向AMS发送通知.说明用户进程里Activity 已经成功显示.AMS就去掉上一步的超时等待的消息
private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent) {
        //执行activity的onCreat方法 和onRestart方法
     Activity a = performLaunchActivity(r, customIntent);
     //执行activity的onResume方法.同时用handle发送了一个Idler消息.Idler消息会在向AMS发送通知.说明用户进程里Activity 已经成功显示.AMS就去掉上一步的超时等待的消息.
      handleResumeActivity(r.token, false, r.isForward);
}

AMS收到这个消息后.就知道APP已经成功启动了.会做一些收尾的工作.然后结束那些处于stop和finish状态的activity.

ActivityStack.activityIdleInternal

  1. 处理所有的stop和finish状态的activity
 stopActivityLocked(r);
 destroyActivityLocked(r, true, false, "finish-idle");

到此. 我们经历了一个activity的启动. 首先是权限的检查,然后为这个新的activity创建ActivityRecord.ProcessRecord,TaskRecord, 然后在启动用户线程,创建Application,用户线程在绑定到AMS,AMS在把ProcessRecord和ApplicationThread进行绑定然后通知用户进程初始化安卓环境.AMS在通知ActivityThread 进行launchActivity. 用户进程就创建这个Activity.执行onCreate,onStart,onResume. 然后在onResume后通知Ams启动完毕.Ams在做一些收尾工作.

image-20200307180845839

ActivityStack.

之前我们在ActivityThread..resumeTopActivityLocked的时候,看到,会先中止旧的activity的显示,在创建新的activity.当时略过去.现在继续看旧activity的暂停过程

  1. 拿到要暂停的activityRecord
  2. 通知该进程,暂停这个activity
  3. 发送超时提醒,等待用户端暂停后通知AMS
        //拿到正在显示的activity,也就是要暂停的那个
    ActivityRecord prev = mResumedActivity;
    //同理.通过用户进程的ApplicationThread来通知用户进程,这个activity要暂停
       prev.app.thread.schedulePauseActivity(prev.appToken, prev.finishing,
                        userLeaving, prev.configChangeFlags);
      //同样设置延时等待,等用户进程暂停activity后的通知,这个实际是500毫秒.
      Message msg = mHandler.obtainMessage(LAUNCH_TIMEOUT_MSG);
                mHandler.sendMessageDelayed(msg, LAUNCH_TIMEOUT);

这就又进入了用户进程的执行情况

ActivityThread.handlePauseActivity

  1. 调用户的onPause
  2. 通知AMS. 用户端onPause完成
    //调用户的onPause
  r.activity.mConfigChangeFlags |= configChanges;
  performPauseActivity(token, finished, r.isPreHoneycomb());
    
  try {     //通知AMS. 用户端onPause完成
      ActivityManagerNative.getDefault().activityPaused(token);
  } catch (RemoteException ex) {
  }

ActivityStack.completePauseLocked

  1. 跳转这暂停的activityinfo 在activityStack中各个状态里边中的情况,把他加入mStoppingActivities里.这会在新ActivityRecord 对应的activity完全启动完成后.执行旧activity的onstop.
  2. 因此我们看到.旧activity的onPaus是早于新activity的onCreate.但是旧activity的onStop要等新activity的onResume 执行完成后才执行.
ActivityRecord prev = mPausingActivity;
mStoppingActivities.add(prev);
mWaitingVisibleActivities.remove(prev);
image-20200307183446289

最后在附一个自己整理的流程图

ams124
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,132评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,802评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,566评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,858评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,867评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,695评论 1 282
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,064评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,705评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,915评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,677评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,796评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,432评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,041评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,992评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,223评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,185评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,535评论 2 343

推荐阅读更多精彩内容