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代碼可以主動調(diào)用native代碼,然后native代碼在被調(diào)用時,可以回調(diào)java的代碼.
jni由兩部分構(gòu)成. Java端的聲明為 native 的java方法.和native端對應的Java方法的實現(xiàn)方法. 然后就是通過一些技術(shù)把兩端的聲明方法和實現(xiàn)方法建立映射關(guān)系.就可以調(diào)用了.
jni分為靜態(tài)注冊和動態(tài)注冊.
靜態(tài)注冊就是由java端聲明的native的java方法生成固定命名規(guī)則的native方法的頭文件.然后native端實現(xiàn)這些頭文件的方法. 在把native端代碼打成so包.java端在調(diào)用聲明native的java方法前加載so包.然后執(zhí)行native 的java方法.系統(tǒng)就會在so包找找固定命名規(guī)則的native方法進行調(diào)用.
靜態(tài)注冊的話,每個java類的native方法都會生成一個固定的.h文件.
動態(tài)注冊就是對java端聲明的native方法和native端的方法進行動態(tài)的綁定(當然這兩個方法的形參得匹配),然后保存在一個native端的JNINativeMethod的結(jié)構(gòu)體中,然后在JNI_OnLoad方法中注冊這個匹配關(guān)系,java中先加載這個native方法生成的so庫,然后在java代碼執(zhí)行native聲明的方法時,從so庫中查找JNINativeMethod機構(gòu)提的匹配規(guī)則,找到對應native中的方法,執(zhí)行.
動態(tài)注冊的話,可以為多個java類中聲明的所有native方法聲明一個native端實現(xiàn)文件,并且不需要頭文件.
靜態(tài)注冊
java端native方法如下
public static native String getAppKey(); 加入native關(guān)鍵字,不需要實現(xiàn),系統(tǒng)就知道這個方法由native實現(xiàn)
把java端的native聲明的方法生成對應頭文件.命令行中
javah -jni safedemo.jniTest.NativeHelper NativeHelper是有native方法的java類,用javah聲明
注意.這里有個javah 命令和NativeHelper.java文件的層級關(guān)系問題.我這個命令是在java目錄下執(zhí)行的.
生成navite的頭文件.并把它實現(xiàn).
/* 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
把實現(xiàn)這個接口的文件達成so包.在java中調(diào)用native前引入.就可以執(zhí)行native方法了.這里使用了cmake
public class NativeHelper {
static {
System.loadLibrary("native-lib");
}
public static native String getAppKey();
}
這樣就建立了so庫中的方法和java中的方法的動態(tài)綁定關(guān)系.
在java中,因為存在多態(tài)性,因此需要通過類名+方法名+方法的簽名(返回值+形參)來唯一確定一個方法.
jni寫的過程稍微有些復雜.但是原理其實是比較簡單的.就是native聲明的java方法和 c代碼進行映射,然后調(diào)用java代碼時執(zhí)行c代碼.
動態(tài)注冊
動態(tài)注冊的主要數(shù)據(jù)結(jié)構(gòu)是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結(jié)構(gòu)
{"jnitest_nativefunction", "()V", (void *)android_jnitest_nativefunction_01}
()V 是形參+返回值的組成,java方法沒有形參所以()括號里沒值, 返回值為空,表示為V
然后是要把這種映射結(jié)構(gòu)注冊到系統(tǒng)中
在native代碼被加載時,jint JNI_OnLoad(JavaVM* vm, void* reserved)方法會被調(diào)用.因此我們要實現(xiàn)這個方法,然后注冊java和native方法的映射關(guān)系.如下.這里是標準寫法了.就是把某個java類里的所有映射關(guān)系,通過AndroidRuntime::registerNativeMethods 進行注冊.
這里是java類+java方法 和native方法的指針,一起完成的.
//結(jié)構(gòu)體綁定jnitest_javaclass_a中的兩個接口和本地兩個實現(xiàn)
static JNINativeMethod gMethods_class_a[] = {
{"jnitest_nativefunction_01", "()V", (void*)android_jnitest_nativefunction_01},
{"jnitest_nativefunction_02", "()V", (void*)android_jnitest_nativefunction_02},
};
//此函數(shù)在Java中調(diào)用System.loadLibrary("");時會被自動觸發(fā)推正,在這個函數(shù)中將調(diào)用上面的兩個注冊函數(shù)句占,最終返回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方法和本地的綁定關(guān)系 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數(shù)據(jù)類型轉(zhuǎn)換
java中的數(shù)據(jù)類型和native中有基本的對應關(guān)系.特殊的是java的所有對象在native中都是jobjct
基本數(shù)據(jù)類型
引用數(shù)據(jù)類型
所有數(shù)組都編程了 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方法調(diào)用 其中的<>可為各種數(shù)據(jù)類型
返回值 方法名 執(zhí)行環(huán)境 操作對象 操作對象的屬性
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執(zhí)行類的方法, Call<>MethodA執(zhí)行對象的方法
對象方法
返回值 | 方法名 | 執(zhí)行環(huán)境 | 操作對象 |操作對象的方法 | 方法參數(shù)
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, ...);
類方法
返回值 | 方法名 執(zhí)行環(huán)境| 操作的類 |類的方法 | 方法參數(shù)
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或不寫對應執(zhí)行方法的參數(shù) ,A一個參數(shù),V 數(shù)組,不寫表示可變參數(shù)
JNIENV
JNI接口指針僅在當前線程中起作用。這意味著指針不能從一個線程進入另一個線程甚纲。然而库正,可以在不同的咸亨中調(diào)用本地方法
JNIEnv是當前Java線程的執(zhí)行環(huán)境曲楚,一個JVM對應一個JavaVM結(jié)構(gòu),而一個JVM中可能創(chuàng)建多個Java線程褥符,每個線程對應一個JNIEnv結(jié)構(gòu)龙誊,它們保存在線程本地存儲TLS中。因此喷楣,不同的線程的JNIEnv是不同趟大,也不能相互共享使用鹤树。JNIEnv結(jié)構(gòu)也是一個函數(shù)表,在本地代碼中通過JNIEnv的函數(shù)表來操作Java數(shù)據(jù)或者調(diào)用Java方法逊朽。也就是說罕伯,只要在本地代碼中拿到了JNIEnv結(jié)構(gòu),就可以在本地代碼中調(diào)用Java代碼叽讳。
這里都需要傳入一個 JNIEnv,這個是每個線程都有一個代表jni環(huán)境的結(jié)構(gòu)體,這是通過JNIEnv來調(diào)用上邊的各種操作類和對象和方法和屬性的各種方法.這個JNIEnv在每個jni方法的native實現(xiàn)里都是默認提供的.我們需要通過
這里引入一篇很好的文章
http://luori366.github.io/JNI_doc/jni_function_mannual.html
JNIEnv首先指向一個線程相關(guān)的結(jié)構(gòu)追他,該結(jié)構(gòu)又指向一個指針數(shù)組,在這個指針數(shù)組中的每個元素最終指向一個JNI函數(shù)岛蚤。所以可以通過JNIEnv去調(diào)用JNI函數(shù)
java方法簽名
由于java的重載,導致光靠java方法名無法確認一個方法,因此需要方法的形參和返回值(差一點,java中,返回值不同不能代表方法重載,但是java虛擬機是支持返回值不同的重載的)
java的方法簽名格式如
(參數(shù)1類型參數(shù)2類型...)返回值類型,如果類型是對象.要寫L對象全路徑名, 如 String類型寫成L/java/language/String. object對象寫成 L/java/lang/object,一維數(shù)組用[表示
對象引用
因為java有g(shù)c垃圾回收,如果native中持有某個java對象,卻被回收了.就會出問題.因此native端有對java對象的引用封裝,具有不同的回收政策
LocalRef 本地引用.大部分都是這種,當該native函數(shù)返回時,java端就可以回收對應的對象
GlobalRef 全局引用, 需要native端主動釋放該對象,否則java端無法回收,全局引用可以跨方法邑狸、跨線程使用,直到被開發(fā)者顯式釋放
WeakGlobalRef 弱全局引用,運行時可能被回收,所以native端在使用他之前需要判斷是否已經(jīng)被回收
這些也需要通過JNIEnv指針來操作.
/
* 創(chuàng)建 obj 參數(shù)所引用對象的新全局引用, 創(chuàng)建的引用要通過調(diào)用DeleteGlobalRef() 來顯式撤消
*
* @param obj 全局或局部引用
* @return 返回全局引用灭美,如果系統(tǒng)內(nèi)存不足則返回 NULL
*/
object NewGlobalRef (JNIEnv *env, jobject obj);
/
* 刪除 globalRef 所指向的全局引用
*
* @param globalRef 全局引用
*/
void DeleteGlobalRef (JNIEnv *env, jobject globalRef);
/
* 創(chuàng)建 obj 參數(shù)所引用對象的局部引用, 創(chuàng)建的引用要通過調(diào)用DeleteLocalRef()來顯式刪除
*
* @param obj 全局或局部引用
* @return 返回局部引用推溃,如果系統(tǒng)內(nèi)存不足則返回 NULL
*/
jobject NewLocalRef(JNIEnv *env, jobject ref);
/
* 刪除 localRef所指向的局部引用
*
* @param localRef局部引用
*/
void DeleteLocalRef (JNIEnv *env, jobject localRef);
/
* 用obj創(chuàng)建新的弱全局引用,
* @param obj 全局或局部因喲娜
* @return 返回弱全局引用届腐,弱obj指向null,或者內(nèi)存不足時返回NULL蜂奸,同時拋出異常
*/
jweak NewWeakGlobalRef(JNIEnv *env, jobject obj);
/
* 刪除弱全局引用
*/
void DeleteWeakGlobalRef(JNIEnv *env, jweak obj);
總結(jié)
jni就是安卓和native搭建的一套調(diào)用方式,通過java定義native方法. native端實現(xiàn)方法.兩者盡心綁定.從而實現(xiàn)了java調(diào)用native, 但是這種調(diào)用是在同一線程中實現(xiàn)的.同時也都是從java調(diào)用native方法.native調(diào)用java方法就有點類似反射,也有點像是java虛擬機的執(zhí)行方式.
android studio 中.提供了ndk簡化jni的開發(fā). 他的cmake文件指定把哪些native文件編程一個so包.so包是有針對不同平臺的不同文件.
下邊提供幾個好的博客
http://www.reibang.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 是數(shù)據(jù)的容器,用來通過Binder發(fā)送數(shù)據(jù).他的特點是用對象的內(nèi)存直接打包起來,然后發(fā)送到對方的進程.
因此他不適合用于數(shù)據(jù)持久化(如把對象寫到文件中),而只是用于跨進程傳遞.以為他是把對象的完整內(nèi)存都保存并傳遞的.可以理解為夸進程傳遞后.兩個進程里的對象是完全一摸一樣的 .
binder驅(qū)動與協(xié)議
android是基于linux內(nèi)核的.binder驅(qū)動也是一個標準的linux驅(qū)動,android里進程的通過binder的進程間通訊.都是要與binder驅(qū)動打交道的.binder驅(qū)動在內(nèi)核態(tài).binder驅(qū)動的位置是(/dev/binder).
每個響應使用binder通訊的進程.都要與binder驅(qū)動打交道.
binder驅(qū)動提供的接口如下.
binder_poll,(多路復用io機制,簡單說就是同時監(jiān)聽多個io線路的變化,哪個io線路有數(shù)據(jù)變化了.就通知監(jiān)聽者)
各種io資料https://segmentfault.com/a/1190000003063859
binder_open, 打開binder驅(qū)動.每個進程都要,通過(ProcessState打開)每個進程會分配最大4M的binder驅(qū)動內(nèi)存,這個內(nèi)存是逐漸分配的.初始為一頁,同時把這個內(nèi)存和程序的內(nèi)存建立映射關(guān)系.
binder_ioctl, 對驅(qū)動內(nèi)存的操作.主要是讀寫操作.通過這個.將數(shù)據(jù)在進程內(nèi)存和內(nèi)核中傳遞.從而實現(xiàn)ipc
binder_mmap, 內(nèi)存映射.進程和內(nèi)核binder驅(qū)動通過內(nèi)存映射到同一個內(nèi)存空間上,二者都有指向這塊內(nèi)存的指針.這樣內(nèi)核把數(shù)據(jù)拷貝到這個內(nèi)存空間上,對應的進程就拿到了數(shù)據(jù).也就是獲得了跨進程傳遞的數(shù)據(jù).
binder服務管理-ServiceManager
Servicemanager 功能
大部分的系統(tǒng)服務.因為需要使用binder機制,又為了簡化.所以都注冊到ServiceManager中(這一過程也是一次binder通信).而ServiceManager是android在init進程時啟動的.并且作為默認的binder管家.APP可以通過binder號為0而拿到這個ServiceManager的代理.從而獲得其他的系統(tǒng)服務.
ServiceManager在啟動時會把自己注冊為整個系統(tǒng)的binder管家.然后打開binder設(shè)備.進程mmap內(nèi)存映射.
然后ServiceManager就進入了循環(huán),通過ioctl來監(jiān)聽binder驅(qū)動發(fā)來的消息(也就是其他進程與ServiceManager的通信請求),
然后把請求轉(zhuǎn)換到具體的處理函數(shù)中進行處理
最后把處理的結(jié)果寫回給binder驅(qū)動(傳遞回發(fā)送請求的進程.
ServiceManager對外提供的服務如getService,addService,checkServic,listService.
serviceManager會把執(zhí)行服務的結(jié)果寫回binder驅(qū)動
獲取ServiceManager流程
我們?nèi)绾蜗騍erviceManager獲取服務呢?通常有如下幾部
打開binder設(shè)備->執(zhí)行mmap內(nèi)存映射->通過binder驅(qū)動向ServiceManager發(fā)送請求(sm的handle為0)->獲得結(jié)果
這里有些步驟是所有binder進行ipc都需要的.因此需要進行封裝,不用讓我們每次使用時手寫.
ProcessState 是進程唯一的.用來在進程開始時打開binder設(shè)備,執(zhí)行mmap內(nèi)存映射
IPCThreadState 是每個線程唯一的.每個線程都可以進行binder通信,并且biner通信的實際代碼也是在IPCThreadState中實現(xiàn)的.
Proxy. proxy和服務實現(xiàn)一個共同的服務接口I Service,具有相同的功能, proxy在客戶端.客戶端調(diào)用它,就相當于調(diào)用服務進程的特定服務.總層次如下
ServiceManagerProxy
在ServiceManager中.在client會獲取一個代理類. ServiceManagerProxy ,他和服務端的ServiceManager都實現(xiàn)了 IServiceManager 接口,也就是具有相同的功能.而代理類對功能的處理則是通過內(nèi)部的IBinder類的mRemote的 transact 方法.來進行ipc通信.此時這個線程會被掛起.等待服務端返回后才能繼續(xù)執(zhí)行.
BpBinder和BinderProxy
因此我們看到.具體具體業(yè)務的執(zhí)行.都會有proxy代理類轉(zhuǎn)換到IBinder類的對象來處理.那么這個對象那里來的的?
這個Binder就是來自client進程中native端的ProcessState.方法如下,這里創(chuàng)建的就是BpBinder
sp<IBinder> ProcessState::getContextObject(const sp<IBinder>& caller)
因為代碼設(shè)計到了java層和native層.所以有很多類在兩層都有相似的名稱.
如binder的客戶端在java層是BinderProxy,在native層是BpBinder.他倆是對應的.并且能夠找到對方.因為java層的binder是由native層的BpBinder對象創(chuàng)建而來的.
這樣. ServiceManagerProxy 的請求方法就轉(zhuǎn)到了client進程中java里的BinderProxy.transact來處理了.而BinderProxy.transact方法是native方法.這又轉(zhuǎn)到了native層.找到對應的native層的BpBinder來處理.
這里簡單說就是client端的 ServiceManagerProxy 的請求,最后倒流client端native里的BpBinder.transact方法里了.
而BpBinder.transact的方法.又是由 IPCThreadState . transact 來執(zhí)行的.
流程如下,下邊所有方法都是client進程的.
(Java)ServiceManagerProxy.getService ->(java)BinderProxy.transact ->(native)BpBinder.transact->(native)IPCThreadState.transact
ProcessState
一個進程只有一個ProcessState.他在創(chuàng)建時打開binder驅(qū)動,進行mmap內(nèi)存映射.也就是一個進程之后進行一次內(nèi)存映射,里邊的線程都使用者一塊內(nèi)存.并且ProcessState會保存該進程創(chuàng)建的所有的binder信息,這里的binder是BpBinder. 每個BpBinder都和一個int的handle綁定.用來指明這個代理的binder的主人.
主要.這時候.新建的BpBinder只有一個handle標識.并沒有和遠程服務有什么關(guān)系.ServiceManager作為系統(tǒng)服務的大管家,他的handler默認是0.
上文我們看到.BpBinder.transact最后轉(zhuǎn)化到了IPCThreadState.transact.的方法里.這里的IPCThreadState是線程單實例的.我們繼續(xù)分析IPCThreadState,他是真正通過binder驅(qū)動與遠方發(fā)送數(shù)據(jù)的地方.
IPCThreadState進行ipc通信
transact函數(shù)
Handle 表示要照的server的服務的標記, code是要執(zhí)行什么服務,data和reply是發(fā)送的數(shù)據(jù)和接受的響應.flags是一些模式.可以指定異步,異步就是直接返回.不等待執(zhí)行結(jié)果.
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) { 把要發(fā)出的數(shù)據(jù)整理好.寫到mOut里.
err = writeTransactionData(BC_TRANSACTION, flags, handle, code, data, NULL);
}
if ((flags & TF_ONE_WAY) == 0) {發(fā)送數(shù)據(jù),得到結(jié)果
if (reply) {
err = waitForResponse(reply);
} else {
Parcel fakeReply;
err = waitForResponse(&fakeReply);
}
}
return err;
}
waitForResponse函數(shù)
status_t IPCThreadState::waitForResponse(Parcel *reply, status_t *acquireResult)
{
int32_t cmd;
int32_t err;
while (1) {
//真正執(zhí)行與驅(qū)動交換數(shù)據(jù)
if ((err=talkWithDriver()) < NO_ERROR) break;
cmd = mIn.readInt32(); //返回結(jié)過的不同處理.一會在看
switch (cmd) {
case BR_TRANSACTION_COMPLETE:
case BR_FAILED_REPLY:
case BR_REPLY:
}
}
talkWithDriver
這個函數(shù)讀寫都是在這執(zhí)行.具體是讀還是寫則是有mIn和mOut的數(shù)據(jù)決定的.,把數(shù)據(jù)寫入bwr中.
status_t IPCThreadState::talkWithDriver(bool doReceive)
{
//這個是與驅(qū)動打交道的數(shù)據(jù)結(jié)構(gòu).他負責寫出數(shù)據(jù)和讀入數(shù)據(jù)
binder_write_read bwr;
//mOut就是負責要寫出的書,min是負責讀入的數(shù)據(jù),他們里邊都有個位置計算已經(jīng)讀了多少,還剩多少.
//需要用mIn和mOut來初始化bwr, 來確定需要讀寫的數(shù)據(jù)和數(shù)據(jù)大小
bwr.write_size = outAvail;
bwr.write_buffer = (long unsigned int)mOut.data();
// 這里是處理要讀的數(shù)據(jù)
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驅(qū)動進行數(shù)據(jù)交換. BINDER_WRITE_READ這個命令很重要.我們以后要驅(qū)動的里這部分.
if (ioctl(mProcess->mDriverFD, BINDER_WRITE_READ, &bwr) >= 0)
err = NO_ERROR;
else
err = -errno;
} while (err == -EINTR);
//根據(jù)數(shù)據(jù)交換的結(jié)果.來確定本次讀了多少.寫了多少.更新mIn和mOut的數(shù)據(jù)尺度.
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表示改對象的數(shù)據(jù)最大容量.
其實讀寫的過程就是要使mDataPos最后等于mDataSize.
上邊通過ioctl和驅(qū)動進行了數(shù)據(jù)交換,再把交換結(jié)果通過mIn和mOut更新.也就知道本次寫和讀的情況.
ioctl(binder驅(qū)動)
如果是同步執(zhí)行(就是等待返回結(jié)果).會把client進程掛起.等待服務端響應后在喚醒服務端.
copy_from_user從用戶空間負責數(shù)據(jù)到內(nèi)核中.
根據(jù)用戶進程中talkWithDriver中填寫的bwr數(shù)據(jù)分別處理.
有數(shù)據(jù)要寫出就執(zhí)行binder_Thread_write,有數(shù)據(jù)要讀入就執(zhí)行binder_thread_read.如果失敗了.還有把負責到內(nèi)核的數(shù)據(jù)在復制回用戶控件.
下邊看binder_Thread_write如何寫出數(shù)據(jù)(這里有個疑問.已經(jīng)到了驅(qū)動,但是沒看到那個標識指向要訪問的服務端啊?--更正,在IpcThreadState.writeTransactionData包裝數(shù)據(jù)時候,把handle寫在了mOut里了.)
通過客戶端指定的handle.找到目標對象的target_node.進入找到目前對象的進程target_proc和線程target_Thread.
拿到target_thread目標線程的todo和wait列表.然后生成一個binder_transaction變量,加入到目標線程的todo隊列中.(這樣目標線程被喚醒,就可以從list中拿到client端的請求).又生成一個binder_work變量,加入到client對應線程的todo隊列中.
然后填寫發(fā)給對方的binder_transaction數(shù)據(jù),包括調(diào)用者和目標對象的信息,和本次轉(zhuǎn)換的內(nèi)存.
最后喚醒對方服務進程.
這是分成兩路.client的執(zhí)行和server的執(zhí)行.
驅(qū)動返回client
先看client執(zhí)行binder_Thread_write后又判斷是否要讀,會執(zhí)行binder_Thread_read
從client的todo隊列中取出一個請求(上邊寫數(shù)據(jù)的時候放進來的binder_work),取出的這個命令又向上報.直接到IPCThreadState.waitForResponse中talkWithDriver后的命令處理,此時命令cmd為BR_TRANSACTION_COMPLETE,然后結(jié)束此次循環(huán),再次開始執(zhí)行while循環(huán),繼續(xù)talkWithDriver.
這就代表是本次發(fā)送已經(jīng)完成.然后繼續(xù)talkWithDriver與驅(qū)動交互.但這次已經(jīng)沒有數(shù)據(jù)要寫出了.因此只執(zhí)行數(shù)據(jù)讀.然后進行睡眠.等待服務端處理完在喚醒.
也就是說.在client端.talkWithDriver把數(shù)據(jù)寫出后,內(nèi)核返回信息使client再次執(zhí)行waitForResources里的wihle循環(huán),然后再等待讀取消息時進入睡眠.
大概流程倒是這樣的
serviceManager被喚醒
ServiceManager在啟動時,會先打開binder驅(qū)動,然后進行mmap.再把自己設(shè)置成系統(tǒng)的大管家.然后就進入了一個無線的for循環(huán),通過ioctl監(jiān)聽只讀請求,等待客戶端請求.因此會進入驅(qū)動中的binder_Thread_read.此時沒有數(shù)據(jù)到來.就會進入休眠
ServiceManager被喚醒后.會檢查todo隊列是否有要處理的請求.如果有.就交給ServiceManager來處理.此時會到ServiceManager的 binder_parse 中解析
binder_parse中,對不同命令進行分別處理.主要作用就是得到客戶端要請求的數(shù)據(jù),進行封裝.然后通過ioctl交給binder驅(qū)動,寫回給用戶端.
ServiceManager中,組裝的數(shù)據(jù)是各個注冊到ServiceManager中的服務對應的一個binder驅(qū)動中的id號.通過這個id.可以找到對應的服務.(驅(qū)動中,通過這個id號,可以找到對應進程中對binder驅(qū)動進行的內(nèi)存映射區(qū)域,然后就能給這個進程發(fā)送數(shù)據(jù)了)
ServiceManager中組裝好數(shù)據(jù)后,里邊包含要獲得的服務的指針,在通過ioctl寫回驅(qū)動.
驅(qū)動里從這數(shù)據(jù)中拿到要返回的client的信息,找到client的進程和線程,找到對應的todo和wait隊列,給client的todo隊列寫一個變量.給ServiceManager的todo隊列也寫一個變量.然后喚醒client進程.并把ServiceManager要發(fā)送給client的數(shù)據(jù)返回給他.
ServiceManager在接著處理我方todo隊列的消息.然后也隨之進入睡眠了.等待下一次喚醒
內(nèi)核在通過copy_to_user,把數(shù)據(jù)復制到client進程的空間.用戶控件就繼續(xù)執(zhí)行talkWithDriver之后的代碼.此時就拿到了server端的數(shù)據(jù)了.
client線程在把收到的數(shù)據(jù)轉(zhuǎn)換成一個binder.在提供出去,在封裝成各種 proxy.就可以使用了.
在驅(qū)動中,各個進程的binder是通過一個句柄值(handle)來區(qū)分的.服務端也是把這個句柄值和一些數(shù)據(jù)返回給用戶端,從而完成夸進程通信.
總體概括
binder 是安卓特有的進程間通訊的機制.因為不同進程的內(nèi)存是不同的.不能直接進行數(shù)據(jù)交換.linux本有一些跨進程通訊的方式.本質(zhì)上都是把數(shù)據(jù)在兩個進程間傳遞.而binder機制是為安卓設(shè)計的.他的特點是只涉及一次內(nèi)存復制.就比較高效.且binder機制進行了分層.把底層的數(shù)據(jù)拷貝和上層也業(yè)務進行了分類.使上層使用時,不必寫重復的bindner先關(guān)的代碼.進程都有一個binder標準.通過該標志,可以找到對應的進程.
考慮一下傳統(tǒng)的IPC方式中犁苏,數(shù)據(jù)是怎樣從發(fā)送端到達接收端的呢?通常的做法是扩所,發(fā)送方將準備好的數(shù)據(jù)存放在緩存區(qū)中围详,調(diào)用API通過系統(tǒng)調(diào)用進入內(nèi)核中。內(nèi)核服務程序在內(nèi)核空間分配內(nèi)存祖屏,將數(shù)據(jù)從發(fā)送方緩存區(qū)復制到內(nèi)核緩存區(qū)中助赞。接收方讀數(shù)據(jù)時也要提供一塊緩存區(qū),內(nèi)核將數(shù)據(jù)從內(nèi)核緩存區(qū)拷貝到接收方提供的緩存區(qū)中并喚醒接收線程袁勺,完成一次數(shù)據(jù)發(fā)送雹食。這種存儲-轉(zhuǎn)發(fā)機制有兩個缺陷:首先是效率低下,需要做兩次拷貝:用戶空間->內(nèi)核空間->用戶空間期丰。Linux使用copy_from_user()和copy_to_user()實現(xiàn)這兩個跨空間拷貝群叶,在此過程中如果使用了高端內(nèi)存(high memory),這種拷貝需要臨時建立/取消頁面映射钝荡,造成性能損失街立。其次是接收數(shù)據(jù)的緩存要由接收方提供,可接收方不知道到底要多大的緩存才夠用埠通,只能開辟盡量大的空間或先調(diào)用API接收消息頭獲得消息體大小赎离,再開辟適當?shù)目臻g接收消息體。兩種做法都有不足端辱,不是浪費空間就是浪費時間
binder機制最底層是binder驅(qū)動.他是如何實現(xiàn)一次復制就在兩個進程中傳送數(shù)據(jù)的?
原理如下圖
驅(qū)動層
這利用了內(nèi)存映射的原理,我們假設(shè)目前是A進行向B進程發(fā)送數(shù)據(jù).而在B進程啟動之初,就對binder驅(qū)動進行了內(nèi)存映射,(binder驅(qū)動工作在內(nèi)核態(tài),可以訪問任意的內(nèi)存地址),操作是先打開binder驅(qū)動.在操作獲得的描述符進行內(nèi)存映射,內(nèi)存映射的結(jié)果就是binder驅(qū)動和B進程都獲得了一塊物理內(nèi)存,并且binder驅(qū)動和B進程都有一個指針指向這個物理內(nèi)存(這個物理內(nèi)存在內(nèi)核上分配),這樣.b進程和Binder驅(qū)動任意一方對這個內(nèi)存的操作,都能讓對方知道變化.當A進程要向B發(fā)送數(shù)據(jù)時,會調(diào)用Binder驅(qū)動,binder驅(qū)動通過copy_from_user把數(shù)據(jù)從進程A中拷貝到Binder驅(qū)動中,而拷貝的內(nèi)存位置就是進程B和Binder驅(qū)動進行映射的這塊內(nèi)存.這樣從進程A來說,就是把數(shù)據(jù)從用戶進程A拷貝到了binder驅(qū)動的內(nèi)核中.而對于進程B.則看到驅(qū)動把數(shù)據(jù)從進程A直接拷貝到自己的內(nèi)存中.這就實現(xiàn)了一次拷貝在兩個進程中傳遞數(shù)據(jù).
這里幾個注意的點. 進程與binder驅(qū)動建立內(nèi)存映射的時候.使用的內(nèi)存是binder驅(qū)動的,是在內(nèi)核中.
進程B建立內(nèi)存映射后.通過監(jiān)聽這端內(nèi)存的io變化.就知道是否有數(shù)據(jù)到來了.
因此.每個進程在啟動的時候,都會與binder驅(qū)動建立內(nèi)存映射關(guān)系,(在開始時,只分配一個比較小的內(nèi)存用來提高進程創(chuàng)建速度,且節(jié)省內(nèi)存)
Binder層
在驅(qū)動層面已經(jīng)實現(xiàn)了基礎(chǔ)的數(shù)據(jù)傳輸.這過程是固定且麻煩的.因此系統(tǒng)進程封裝后.我們直接使用就可以.
在業(yè)務層.要處理的就是.把數(shù)據(jù)封裝成固定的格式.交給驅(qū)動去傳遞.然后到對方進程時能正確的解析出來.進行處理.
這里使用了parcel,他是一種數(shù)容器.直接把要傳輸?shù)臄?shù)據(jù)的整個內(nèi)存給傳輸過去了.保證兩邊對象的一致性.
為了進一步簡化,系統(tǒng)有封裝了binder這一結(jié)構(gòu). 他代表在兩邊傳遞的數(shù)據(jù).android里大多用binder的是各種服務.
因此先定義一種共同的接口interface,在兩個進程實現(xiàn).client中則是把請求轉(zhuǎn)換成binder處理.而server端就是從binder中拿出請求,完成具體的工作.這里就是更進一步的抽象封裝,業(yè)務層只需要設(shè)計具體的服務如何提供,然后再把數(shù)據(jù)一封裝就完成了
這里產(chǎn)生的binder層,在client端是bpbinder,在server端是bbinder.這是用來封裝數(shù)據(jù)并發(fā)送過去的一層.
代理層
在上邊是業(yè)務層.兩端都有實現(xiàn)同樣接口的類.處理具體的功能,如activitymanagerservice在服務端,對應客戶端是activitymanagerproxy(我自己起的名字,) 客戶端的調(diào)用 activitymanagerproxy的具體功能,activitymanagerproxy再把數(shù)據(jù)交個bpbinder進行封裝.bpbinder封裝完成后,把數(shù)據(jù)通過IpcThreadState來發(fā)送數(shù)據(jù),在通過binder驅(qū)動發(fā)送過去.在server端.先是binder驅(qū)動的監(jiān)聽知道數(shù)據(jù)到來.在轉(zhuǎn)成bbinder解析數(shù)據(jù),在交給activitymanagerservice執(zhí)行.
同時為了簡化.加入了ServiceManager,統(tǒng)一管理所有的系統(tǒng)服務.系統(tǒng)服務要先注冊到ServiceManager,才算對外暴露.APP像使用什么服務時,先想ServiceManager,得到一個對應服務的代理.這個代理把想執(zhí)行的請求,和遠端的binder是具有聯(lián)系的.通過他就可以進行binder通信
上文的binder.不一定是framework中的binder類.只是一個功能的描述.
所以.最底層是binder驅(qū)動.需要每個進程在啟動時打開驅(qū)動,進行內(nèi)存映射,通過ProcessState
上層是IPCThreadState.他是每個線程唯一,真正用數(shù)據(jù)與binder驅(qū)動交互的地方.發(fā)送數(shù)據(jù),得到請求
在上層是Binder,在發(fā)送數(shù)據(jù)時,通過binder把數(shù)據(jù)發(fā)給IPCThreadState,而在獲得了遠程的數(shù)據(jù)返回后.IPCThreadState又回把數(shù)組封裝成一個binder,向上傳遞.
可以這樣理解,每個線程可以進行很多次 跨進程通訊,但是每次可能遠程通信都不同. IPCThreadState提供了遠程通信的通道.但是通道的內(nèi)容是由binder決定里的.所以同一個線程里.通道有一個,但是內(nèi)容有很多.
在上一層.把binder封裝成proxy.提供給客戶端.就徹底隱藏了底層的復雜機制.上層只需要操作這個代理類就可以了.
最外邊則是通過ServiceManager的代理類來拿到其他服務的代理. ServiceManager作為系統(tǒng)服務關(guān)鍵.默認為handle號為0.可以直接獲取他的代理.這次,在看這個分層.就很清楚了.
同時還要知道一點.java層的binder只不過是native層的一個鏡像.二者是綁定的.java層主要功能都是通過native層來實現(xiàn)的
系統(tǒng)啟動
系統(tǒng)組成
系統(tǒng)啟動時,會加載一系列的其他服務,這是通過讀取配置文件實現(xiàn)的.
系統(tǒng)啟動的第一個進程是init進程,他會讀取init.rc配置文件來啟動過各種服務
init.rc
分為 actio(動作),commands(命令) services(服務)options(選項)
on<trigger> 觸發(fā)條件
<command1> 執(zhí)行命令1
<command2> 執(zhí)行命令2
也就是在發(fā)送了trigger動作時,執(zhí)行命令1和命令2
舉例
on boot //觸發(fā)boot事件時
mkdir /dev 創(chuàng)建dev目錄
class_start default 啟動所有default標
service 是定義的可執(zhí)行程序,他會列出要執(zhí)行的文件的路徑,和附帶的約束,通常運行于另外的進程中.
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進程創(chuàng)建的另一個進程.用于fork進程用,他啟動后,會通過socket 監(jiān)聽systemServer發(fā)來的請求.然后fork自己創(chuàng)建新進程.安卓里大多數(shù)應用進程和系統(tǒng)進程都是他fork出來的.也是由init.rc配置文件里面試并由init進程啟動的.他死亡時會殺掉他所有創(chuàng)建的子進程.
主要流程
調(diào)用AppRuntime.start.把任務都交個他處理
AppRuntime.start里.創(chuàng)建java虛擬機,動態(tài)注冊大量jni函數(shù),調(diào)用ZygoteInit.main方法.
ZygoteInit.main里 1注冊ZygoteInit用的socket.自己作為服務端,2反射加載類和framework.apk中資源.3通過forkSystemServer來啟動system_server進程4.開啟循環(huán).等待socket客戶端的連接.
zygote進程和systemServer進程是同生共死的.systemServer死了.zygote進程也會殺死自己.
systemServer(zygote啟動)
由zygote進程創(chuàng)建的另一個進程.由zygote啟動的虛擬機中的java類ZygoteInit來啟動.通過forkSystemServer.來生產(chǎn)一個新進程.用于加載各種服務.
啟動時執(zhí)行 handleSystemServerProcess 里邊調(diào)用了RuntimeInit. zygoteInit
這里邊創(chuàng)建了1.ProcessState(進程獨有的)與binder通信系統(tǒng)建立聯(lián)系.然后2.啟動SystemServer(java)的main函數(shù)
SystemServer里執(zhí)行了init1,開啟圖形和聲音的服務和 init2,啟動了serverThread,初始化各種服務.又執(zhí)行ProcessState和IpcThreadState的線程服務,開啟了兩個binder通信線程.
總之就是在native端和java端啟動了各種服務.init1的服務是native的.init2的服務是java端的,通過線程啟動.
啟動其他進程
當需要啟動一個進程時.通過向zygote進程發(fā)送socket信息來讓zygote進程fork出新進程.以activitymanager.創(chuàng)建socket及發(fā)送給zygote是有process.java類實現(xiàn)
zygoteInit.java中通過ZygoteConnection.runOnce來處理收到的請求.然后就是fork出新進程.然后讓子進程處理請求.
ActivityManagerService
AMS由SystemServer創(chuàng)建,并且SystemServer會等AMS啟動完在繼續(xù)執(zhí)行.
原理是SystemServer調(diào)用AMS的main方法. main方法中開啟一個線程.然后SystemServer會在這里wait,等到線程里創(chuàng)建AMS成功后,AMS的線程notify喚醒SystemServer繼續(xù)工作.
Ams的主要任務是負責系統(tǒng)內(nèi)四大組件的啟動,切換,調(diào)用及應用進程的管理和調(diào)度.
ActivityStack 則是用來管理Activity狀態(tài)的類.他里邊保存了幾個集合,用來存儲各種狀態(tài)的Activity
Activity在Ams里都抽象成了ActivityRecord 類
值得注意的是,AMS因為需要調(diào)用用戶進程,所以每個進程在啟動時候,都會與ams建立鏈接,同時ams也會獲得一個向用戶進程發(fā)消息的binder(ApplicationThread),通過他,AMS就可以主動向用戶進程發(fā)消息,執(zhí)行一些操作.此時AMS變成了binder的client端,用戶進程成了binder的server端. 這種方式是匿名binder.
AMS啟動
Ams在SystemServer的線程中啟動,大概步驟如下,也就是先啟動Ams.然后又等待Ams的啟動回調(diào).
//創(chuàng)建線程,啟動
context = ActivityManagerService. main (factoryTest);
//把systemServer 加入到Ams中管理
ActivityManagerService. setSystemProcess ();
//初始化系統(tǒng)provider
ActivityManagerService. installSystemProviders ();
//Ams和Wms(WindowManagerService)建立聯(lián)系
((ActivityManagerService)ServiceManager. getService ("activity"))
? . setWindowManager (wm);
//ams啟動完成的回調(diào),ams的 systemReady 完成后,其他服務的 systemReady 才能被調(diào)用.
((ActivityManagerService)ActivityManagerNative. getDefault ())
? . systemReady (new Runnable () {})
ActivityManagerService.main
啟動一個線程AThread,創(chuàng)建AMS后進入looper循環(huán)
Ams的創(chuàng)建中初始化幾個系統(tǒng)運行狀態(tài)服務,如ProcessStats,是一個統(tǒng)計信息的服務.并創(chuàng)建一個線程用來更新統(tǒng)計信息.
創(chuàng)建幾個系統(tǒng)服務service
通過ActivityThread.main 創(chuàng)建ActivityThread(此時在SystemServer線程).
ActivityThread會為"android"(也就是faramwork.apk)這個app創(chuàng)建application和context并進行關(guān)聯(lián).這是系統(tǒng)資源APP.所以AMS為他初始化了和應用APP一樣的環(huán)境.
總結(jié)就是.啟動一個loop線程,創(chuàng)建AMS,為系統(tǒng)APP創(chuàng)建ActivityThread.Application,Context.
此時的ActivityThrad運行在SystemServer進程中.是系統(tǒng)APP(framework.apk)的主線程
ActivityManagerService.setSystemProcess
把AMS注冊到ServiceManager中.
獲取系統(tǒng)APP(framework.apk)的ApplicationInfo,ApplicationInfo是PackageManagerServic中對應用信息的抽象.對應AndroidManifest里的application節(jié)點
調(diào)用ActivityThread. installSystemApplicationInfo 這里真正將系統(tǒng)APP的ApplicationInfo和Context綁定起來.此時 AMS為系統(tǒng)APP在System_server中制造的APP的運行時環(huán)境才算完整.
為系統(tǒng)APP創(chuàng)造ProcessRecord,代表進程的記錄.并保存在AMS中.ProcessRecord是AMS里對APP進程的一個抽象.用來保存進程的信息. ProcessRecord中還保存了AMS和用戶進程通信的IApplicationThread,在客戶端對應的則是ApplicationThread. AMS通過IApplicationThread來調(diào)用客戶端,這也是一個binder通信(通常都是binder通信.但是在系統(tǒng)服務這里.是同一進程的.則不是binder通信)
總結(jié),主要是把AMS注冊到ServiceManager,然后通過PKMS返回的ApplicationInfo,創(chuàng)建system_server進程的ProcessRecord.加入AMS的管理.(因為framework.apk.運行在system_server進程)
ActivityManagerService.systemReady
發(fā)送ACTION_PRE_BOOT_COMPLETED廣播
殺死先于AMS啟動的應用進程.
完成其他服務的 systemReady 方法,再啟動系統(tǒng)桌面home
home啟動成功后,會回到Ams的finishBooting方法,然后Ams發(fā)送ACTION_BOOT_COMPLETED廣播.
總結(jié)
AMS的main函數(shù),創(chuàng)建AMS對象,開啟一個looper線程,為framework.apk 初始化環(huán)境(創(chuàng)建ActivityThread.Application,Context.)
AMS的setSystemProcess . 注冊AMS和一些運行信息服務到ServiceManager.并為system_server創(chuàng)建processRecord.表示system_server進程也作為AMS管理的用戶進程.(因為system_server中有framework.apk等)
AMS的installSystemProviders. 為AMS加載 SettingsProvider
AMS的systemReady.掃尾工作.啟動系統(tǒng)桌面. Home Activity.
startActivity 分析
從Am.java 分析Activity的啟動. am是 提供的腳本.用來和AMS交互. 如
am start -W -n com.test/MainActivity 啟動com.test包下的 MainActivity. am 命令要 通過adb shell 登錄手機后執(zhí)行
ActivityStack簡介
mMainStack是ActivityStack類. 他負責組織 Activity的task棧. task棧是用來完成同一任務的結(jié)果.不同APP的Activity可以在一個棧里. 安卓還支持多個task 棧.但是只能有一個在前臺, 當一個棧在前臺時,點擊返回按鈕,則會把這個task棧的所有Activity都清空,然后在回調(diào)別的task棧上.
ActivityStack 及相關(guān)成員如下
可以看到.在AMS中.所有的Activity都抽象成ActivityRecord. 而task則由TaskRecord表示. ActivityRecord知道自己屬于哪個task.而ActivityStack則通過mHistory管理ActivityRecord.
ActivityStack里有很多不同狀態(tài)的ActivRecord ,因此ActivityStack肯定要讓ActivityRecord在不同狀態(tài)切換
ArrayList<ActivityRecord> mStoppingActivities
ArrayList<ActivityRecord> mGoingToSleepActivities
ArrayList<ActivityRecord> mWaitingVisibleActivities
ArrayList<ActivityRecord> mHistory
ActivityStack 主要功能如下
topRunningActivityLocked 找到正在運行的ActivityRecord
findActivityLocked 根據(jù)intent和ActivityInfo.來匹配ActivityRecord
所有大概來看 ActivityStack 就是通過保管TaskRecord和ActivityRecord. 來管理Activity和task 的關(guān)系.(四種啟動模式)
ActivityStack.startActivityMayWait
AMS的啟動從 startActivityAndWait 開始
mMainStack.startActivityMayWait(caller, -1, intent, resolvedType,
resultTo, resultWho, requestCode, startFlags, profileFile, profileFd,
res, null, options, userId);
startActivityMayWait 的大概流程是.
創(chuàng)建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.執(zhí)行函數(shù)Activity啟動
int res = startActivityLocked(caller, intent, resolvedType,
grantedUriPermissions, grantedMode, aInfo,
resultTo, resultWho, requestCode, callingPid, callingUid,
onlyIfNeeded, componentSpecified, null);
//3.等待用戶進程啟動成功后回調(diào)AMS,保存該Activityinfo 啟動結(jié)果.
if (res == IActivityManager.START_SUCCESS) {
mWaitingActivityLaunched.add(outResult)
do {
try {
mService.wait();
} catch (InterruptedException e) {
}
} while (!outResult.timeout && outResult.who == null);
}
}
那么.第二步就是啟動Activity的地方.這里有兩個同名函數(shù),此時是走參數(shù)多的那個.
ActivityStack.startActivityLocked(參數(shù)多的)
主要是進行權(quán)限的檢查,和activity的來龍去脈的設(shè)置
- 先是拿到調(diào)用者進程的pid和uid.看是否正確,
- 在設(shè)定 要啟動Activity的 sourceRecord和resultRecord這倆都是(ActivityRecord),表示誰啟動的新Activity.和新Activity完成后 返回到那里.
- 處理intent的flag
- 檢查調(diào)用者權(quán)限.用1步驟的pid和uid看調(diào)用者使用有權(quán)限啟動Activity.
- 創(chuàng)建要啟動Activity的ActivityRecord對象.
- 啟動Activity.
startActivityLocked{
//1.先是拿到調(diào)用者進程的pid和uid.看是否正確
if (callerApp != null) {
callingPid = callerApp.pid;
callingUid = callerApp.info.uid;
}
2.//然后在設(shè)定 要啟動Activity的 sourceRecord和resultRecord這倆都是(ActivityRecord),表示誰啟動的新Activity.和新Activity完成后 返回到那里
ActivityRecord sourceRecord = null;
ActivityRecord resultRecord = null;
3.處理flag
int launchFlags = intent.getFlags();
4. 檢查調(diào)用者權(quán)限
final int perm = mService.checkComponentPermission(aInfo.permission, callingPid,
callingUid, aInfo.applicationInfo.uid, aInfo.exported);
5. 創(chuàng)建一個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
- 根據(jù)intent.flag來為新的Activity選擇合適的task.
- 找到一個ActivityRecord
- 為新的ActivityRecord 設(shè)置 taskRecord,此時新Activity的 ActivityRecord 和TaskRecord都準備好了
//1.根據(jù)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 設(shè)置 taskRecord
if (reuseTask == null) {
r.setTask(new TaskRecord(mService.mCurTask, r.info, intent), null, true);
} else {
r.setTask(reuseTask, reuseTask, true);
}
//一個同名函數(shù),這次是參數(shù)少的,里邊主要是執(zhí)行 resumeTopActivityLocked.
startActivityLocked(r, newTask, doResume, keepCurTransition, options);
ActivityStack.resumeTopActivityLocked
- 找到要啟動的Activity
- 暫停舊的Activity
- 新的Activity和wms搭配,繼續(xù)執(zhí)行新Activity的啟動.
//找到隊列中棧頂中運行的Activity.這是我們要啟動的那個
ActivityRecord next = topRunningActivityLocked(null);
//暫停目前正在顯示的Activity.(暫停原來的Activity.好讓新的Activity顯示出來)
//這里.ams會等待舊Activity暫停.暫停后客戶端會回調(diào)ams的resumeTopActivitLocked.然后繼續(xù)啟動新的activity
if (mResumedActivity != null) {
(userLeaving, false);
return true;
}
// 再次執(zhí)行時,舊Activity已經(jīng)暫停了.此時要啟動新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);
}
}
//繼續(xù)啟動新Activity.
startSpecificActivityLocked(next, true, true);
此時這里產(chǎn)生了分叉.舊的Activity需要暫停.然后新的Activity在進行啟動.我們先看新的Activity的啟動.舊Activity的暫停以后再看
ActivityStack.startSpecificActivityLocked
- 先拿到新Activity對應的進程
- 如果進程已經(jīng)啟動.就繼續(xù)啟動Activity
- 進程沒啟動,先啟動Activity所在進程
processRecord 是進程在AMs的抽象對象. 這里看新Activity的進程是否存在
ProcessRecord app = mService.getProcessRecordLocked(r.processName,
r.info.applicationInfo.uid);
//如果集成已經(jīng)啟動.就直接啟動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
- 通過啟動ActivityThread 來啟動用戶進程,同時保存用戶進程的ProcessRecord在ams中
ActivityThread 就是用戶線程.當然這個本質(zhì)是通過zygote進程fork出的新進程.然后執(zhí)行ActivityThread的main函數(shù).
- 發(fā)送延時消息(10s),如果10s內(nèi)用戶進程沒啟動完畢并向AMS注冊.則認為用戶進程啟動失敗.
來啟動用戶進程,執(zhí)行ActivityThread ,同時保存用戶進程的ProcessRecord在ams中
Process.ProcessStartResult startResult = Process.start("android.app.ActivityThread",
app.processName, uid, uid, gids, debugFlags,
app.info.targetSdkVersion, null);
AMS發(fā)送一個超時消息.等待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);
}
到這里.我們知道.一個Activity 的啟動.在AMS端.需要有一個ActivitRecord,TaskRecord.ProcessRecord.來對應.
同時,還要處理他原有Activity的暫停,舊的Activity的暫停.早于新Activity 的啟動.
ActivityThread.main
- 開啟消息循環(huán)loop
- 創(chuàng)建ActivityThread對象.執(zhí)行attach方法
Looper.prepareMainLooper();
if (sMainThreadHandler == null) {
sMainThreadHandler = new Handler();
}
ActivityThread thread = new ActivityThread();
thread.attach(false);
Looper.loop();
ActivityThread.attach
- 主要就是與AMS建立binder通信. 這里的mAppThread 是ApplicationThread類,這里ActivityThread的內(nèi)部類,傳遞給AMS,AMS就可以通過 他來主動調(diào)用ActivityThread的方法.也就是AMS主動通過Binder與ActivityThread交流.此時AMS是binder的client端.
IActivityManager mgr = ActivityManagerNative.getDefault();
try {
mgr.attachApplication(mAppThread);
} catch (RemoteException ex) {
}
ApplicationThread 在收到AMS的消息后.會通過handler.把消息發(fā)給ActivityThread來執(zhí)行.
public final void scheduleResumeActivity(IBinder token, boolean isForward) {
queueOrSendMessage(H.RESUME_ACTIVITY, token, isForward ? 1 : 0);
}
ActivityThread 里的attac方法.接收方是AMS里的 attachApplicationLocked
AMS. attachApplicationLocked
- 把用戶進程發(fā)來的binder和AMS對應的ProcessRecord綁定起來.
- 移除AMS之前發(fā)出的等待用戶進程啟動的消息
- 通知用戶進程初始化android的運行環(huán)境,并創(chuàng)建application對象.執(zhí)行oncreate.
- 找到之前正在等待啟動的activity,然后繼續(xù)啟動他
private final boolean attachApplicationLocked(IApplicationThread thread,int pid) {
//把用戶進程傳來的ApplicationThread 和一個processRecord 綁定起來.
ProcessRecord app;
app = mPidsSelfLocked.get(pid);
app.thread = thread;
//移除AMS之前發(fā)出的等待10s內(nèi)用戶進程啟動的消息
mHandler.removeMessages(PROC_START_TIMEOUT_MSG, app);
//通知用戶進程初始化android的運行環(huán)境,在用戶端會初始化很多東西.并創(chuàng)建application對象.執(zhí)行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,然后繼續(xù)啟動他
ActivityRecord hr = mMainStack.topRunningActivityLocked(null);
mMainStack.realStartActivityLocked(hr, app, true, true)
//后面還有啟動Service.不看了
}
總結(jié),用戶進程的啟動由AMS決定.AMS通過zygote來fork出用戶進程,用戶進程啟動后主動向AMS注冊.AMS再把這個用戶進程和一個ProcessRecord 綁定起來.同時回調(diào)用戶進程進行初始化.用戶進程在初始化資源,創(chuàng)建Application.并執(zhí)行onCreate.
ACtivityStack.realStartActivityLocked
此時,用戶進程已經(jīng)啟動完畢. ActivityRecord, TaskRecord,ProcessRecord 都已經(jīng)準備好了.用戶進程中Application已經(jīng)創(chuàng)建并執(zhí)行onCreate.繼續(xù)執(zhí)行Activity
- 通知用戶進程啟動Activity.
- 等待用戶進程Activity啟動,執(zhí)行完onResume后通知AMS,這里AMS也是超時等待,10s內(nèi)APP沒通知,就認為app啟動失敗.
final boolean realStartActivityLocked(ActivityRecord r,
ProcessRecord app, boolean andResume, boolean checkConfig){
//processRecord 把ActivityRecord 保存起來.
app.activities.add(r);
//通知用戶進程啟動activity.通過ApplicationThread進行binder通信來實現(xiàn).
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啟動,執(zhí)行完onResume后通知AMS,這里AMS也是超時等待,10s內(nèi)APP沒通知,就認為app啟動失敗.
completeResumeLocked(r);
}
此時AMS又通知Activity進行啟動.我們要再次來到用戶進程.ApplicationThread 通過發(fā)消息.傳遞給ActivityThread.handleLaunchActivity方法
ActivityThread.handleLaunchActivity
- 執(zhí)行activity的onCreat方法 和onRestart方法
- 執(zhí)行activity的onResume方法.同時用handle發(fā)送了一個Idler消息.Idler消息會在向AMS發(fā)送通知.說明用戶進程里Activity 已經(jīng)成功顯示.AMS就去掉上一步的超時等待的消息
private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent) {
//執(zhí)行activity的onCreat方法 和onRestart方法
Activity a = performLaunchActivity(r, customIntent);
//執(zhí)行activity的onResume方法.同時用handle發(fā)送了一個Idler消息.Idler消息會在向AMS發(fā)送通知.說明用戶進程里Activity 已經(jīng)成功顯示.AMS就去掉上一步的超時等待的消息.
handleResumeActivity(r.token, false, r.isForward);
}
AMS收到這個消息后.就知道APP已經(jīng)成功啟動了.會做一些收尾的工作.然后結(jié)束那些處于stop和finish狀態(tài)的activity.
ActivityStack.activityIdleInternal
- 處理所有的stop和finish狀態(tài)的activity
stopActivityLocked(r);
destroyActivityLocked(r, true, false, "finish-idle");
到此. 我們經(jīng)歷了一個activity的啟動. 首先是權(quán)限的檢查,然后為這個新的activity創(chuàng)建ActivityRecord.ProcessRecord,TaskRecord, 然后在啟動用戶線程,創(chuàng)建Application,用戶線程在綁定到AMS,AMS在把ProcessRecord和ApplicationThread進行綁定然后通知用戶進程初始化安卓環(huán)境.AMS在通知ActivityThread 進行l(wèi)aunchActivity. 用戶進程就創(chuàng)建這個Activity.執(zhí)行onCreate,onStart,onResume. 然后在onResume后通知Ams啟動完畢.Ams在做一些收尾工作.
ActivityStack.
之前我們在ActivityThread..resumeTopActivityLocked的時候,看到,會先中止舊的activity的顯示,在創(chuàng)建新的activity.當時略過去.現(xiàn)在繼續(xù)看舊activity的暫停過程
- 拿到要暫停的activityRecord
- 通知該進程,暫停這個activity
- 發(fā)送超時提醒,等待用戶端暫停后通知AMS
//拿到正在顯示的activity,也就是要暫停的那個
ActivityRecord prev = mResumedActivity;
//同理.通過用戶進程的ApplicationThread來通知用戶進程,這個activity要暫停
prev.app.thread.schedulePauseActivity(prev.appToken, prev.finishing,
userLeaving, prev.configChangeFlags);
//同樣設(shè)置延時等待,等用戶進程暫停activity后的通知,這個實際是500毫秒.
Message msg = mHandler.obtainMessage(LAUNCH_TIMEOUT_MSG);
mHandler.sendMessageDelayed(msg, LAUNCH_TIMEOUT);
這就又進入了用戶進程的執(zhí)行情況
ActivityThread.handlePauseActivity
- 調(diào)用戶的onPause
- 通知AMS. 用戶端onPause完成
//調(diào)用戶的onPause
r.activity.mConfigChangeFlags |= configChanges;
performPauseActivity(token, finished, r.isPreHoneycomb());
try { //通知AMS. 用戶端onPause完成
ActivityManagerNative.getDefault().activityPaused(token);
} catch (RemoteException ex) {
}
ActivityStack.completePauseLocked
- 跳轉(zhuǎn)這暫停的activityinfo 在activityStack中各個狀態(tài)里邊中的情況,把他加入mStoppingActivities里.這會在新ActivityRecord 對應的activity完全啟動完成后.執(zhí)行舊activity的onstop.
- 因此我們看到.舊activity的onPaus是早于新activity的onCreate.但是舊activity的onStop要等新activity的onResume 執(zhí)行完成后才執(zhí)行.
ActivityRecord prev = mPausingActivity;
mStoppingActivities.add(prev);
mWaitingVisibleActivities.remove(prev);
最后在附一個自己整理的流程圖