這是一個連載的博文系列,我將持續爲你們提供儘量透徹的Android源碼分析 github連載地址html
前文講到虛擬機建立後反射調用了ZygoteInit的main方法,說到虛擬機,咱們就不得不說下JNI,它是溝通Java和C++的橋樑。 JNI全稱是Java Native Interface,能夠把它理解爲一種接口編程方式,就像咱們日常開發的C/S模式同樣, Client和Server要通訊,那就得用接口。JNI主要包括兩個方面的內容:java
本文涉及到的文件linux
platform/libnativehelper/include/nativehelper/jni.h
platform/art/runtime/java_vm_ext.cc
platform/art/runtime/jni_internal.cc
platform/frameworks/base/core/java/com/android/internal/os/ZygoteInit.java
platform/libcore/dalvik/src/main/java/dalvik/system/ZygoteHooks
platform/art/runtime/native/dalvik_system_ZygoteHooks.cc
platform/art/runtime/runtime.h
platform/libnativehelper/JNIHelp.cpp
platform/libcore/luni/src/main/java/android/system/Os.java
platform/libcore/luni/src/main/java/libcore/io/Libcore.java
platform/libcore/luni/src/main/java/libcore/io/BlockGuardOs.java
platform/libcore/luni/src/main/java/libcore/io/ForwardingOs.java
platform/libcore/luni/src/main/java/libcore/io/Linux.java
platform/libcore/luni/src/main/native/libcore_io_Linux.cpp
複製代碼
爲何我先講C++調用Java呢?由於前文建立了虛擬機後,首先是從C++調用了Java,因此我接着前文的例子來說, 咱們回顧一下以前C++調用ZygoteInit的main函數的過程,我將分段一步步爲你們解釋。android
void AndroidRuntime::start(const char* className, const Vector<String8>& options, bool zygote)
{
/* * We want to call main() with a String array with arguments in it. * At present we have two arguments, the class name and an option string. * Create an array to hold them. */
jclass stringClass;
jobjectArray strArray;
jstring classNameStr;
stringClass = env->FindClass("java/lang/String");
assert(stringClass != NULL);
strArray = env->NewObjectArray(options.size() + 1, stringClass, NULL);
assert(strArray != NULL);
classNameStr = env->NewStringUTF(className);
assert(classNameStr != NULL);
env->SetObjectArrayElement(strArray, 0, classNameStr);
for (size_t i = 0; i < options.size(); ++i) {
jstring optionsStr = env->NewStringUTF(options.itemAt(i).string());
assert(optionsStr != NULL);
env->SetObjectArrayElement(strArray, i + 1, optionsStr);
}
/* * Start VM. This thread becomes the main thread of the VM, and will * not return until the VM exits. */
char* slashClassName = toSlashClassName(className);//將字符中的.轉換爲/
jclass startClass = env->FindClass(slashClassName);//找到class
if (startClass == NULL) {
ALOGE("JavaVM unable to locate class '%s'\n", slashClassName);
/* keep going */
} else {
jmethodID startMeth = env->GetStaticMethodID(startClass, "main",
"([Ljava/lang/String;)V");
if (startMeth == NULL) {
ALOGE("JavaVM unable to find main() in '%s'\n", className);
/* keep going */
} else {
env->CallStaticVoidMethod(startClass, startMeth, strArray);//調用main函數
#if 0
if (env->ExceptionCheck())
threadExitUncaughtException(env);
#endif
}
}
free(slashClassName);
...
}
複製代碼
好比說咱們Java中有常見的Class,String,int,short等,這些在C++中並非叫原來的名字,而是另外取了個名字, 基本就是在原來的名字前加了個j,表示java. 下面是他們的對應關係git
基本數據類型和voidgithub
Java類型 | C++類型 |
---|---|
boolean | jboolean |
byte | jbyte |
char | jchar |
short | jshort |
int | jint |
long | jlong |
float | jfloat |
double | jdouble |
void | void |
引用數據類型編程
Java類型 | C++類型 |
---|---|
All objects | jobject |
java.lang.Class實例 | jclass |
java.lang.String實例 | jstring |
java.lang.Throwable實例 | jthrowable |
Object[](包含Class,String,Throwable) | jobjectArray |
boolean[] | jbooleanArray |
byte[](其餘基本數據類型相似) | jbyteArray |
那其實下面的代碼就好理解了,就至關於定義了三個局部變量,類型爲Class,String[],String數組
jclass stringClass;
jobjectArray strArray;
jstring classNameStr;
複製代碼
咱們再接着往下看,env->FindClass, env是虛擬機的環境,能夠類比爲Android中無處不在的Context, 可是這個env是指特定線程的環境,也就是說一個線程對應一個env.
bash
env有許多的函數,FindClass只是其中一個,做用就是根據ClassName找到對應的class, 用法是否是跟Java中反射獲取Class有點像,其實Java反射也是native方法,也得走到C++層,在實現上也是跟env->FindClass同樣.oracle
咱們來具體看看env->FindClass的實現,env的類型是JNIEnv,定義在platform/libnativehelper/include/nativehelper/jni.h中, 這個JNIEnv 在C環境和C++環境類型不同,在C環境中定義的是JNINativeInterface* , 而C++中定義的是_JNIEnv,_JNIEnv其實內部也是調用JNINativeInterface的對應函數,只是作了層代理, JNINativeInterface是個結構體,裏面就有咱們要找的函數FindClass
#if defined(__cplusplus) //若是是C++
typedef _JNIEnv JNIEnv;
typedef _JavaVM JavaVM;
#else //若是是C
typedef const struct JNINativeInterface* JNIEnv;
typedef const struct JNIInvokeInterface* JavaVM;
#endif
struct _JNIEnv {
const struct JNINativeInterface* functions;
...
jclass FindClass(const char* name) { return functions->FindClass(this, name); }
...
}
struct JNINativeInterface {
...
jclass (*FindClass)(JNIEnv*, const char*);
...
}
複製代碼
那這個結構體JNINativeInterface中FindClass的函數指針何時賦值的呢?還記得上文中有個建立虛擬機的函數JNI_CreateJavaVM, 裏面有個參數就是JNIEnv,其實也就是在建立虛擬機的時候把函數指針賦值的,咱們知道JNI_CreateJavaVM是加載libart.so時獲取的, 那咱們就得找libart.so的源碼,這個對應的源碼在platform/art/runtime/java_vm_ext.cc,它會調用Runtime::Create函數去新建線程, 在線程新建的過程當中會對JNIEnv進行賦值,JNI_CreateJavaVM函數最後會去調用線程的GetJniEnv獲得JNIEnv的實例,將實例賦值給p_env.
(線程在新建過程當中如何對JNIEnv進行賦值的,就不細講了,我提供幾個關鍵的函數,runtime.cc的Create和Init、thread.cc的Attach和Init、 jni_env_ext.cc的Create、jni_internal.cc的GetJniNativeInterface,涉及到的文件我都放在AOSP項目中,有興趣的能夠去看看. )
extern "C" jint JNI_CreateJavaVM(JavaVM** p_vm, JNIEnv** p_env, void* vm_args) {
...
if (!Runtime::Create(options, ignore_unrecognized)) {
return JNI_ERR;
}
*p_env = Thread::Current()->GetJniEnv();
}
複製代碼
GetJniEnv返回的是一個JNINativeInterface的實例,定義在/platform/art/runtime/jni_internal.cc,其中就有咱們要找的FindClass
const JNINativeInterface gJniNativeInterface = {
nullptr, // reserved0.
nullptr, // reserved1.
nullptr, // reserved2.
nullptr, // reserved3.
JNI::GetVersion,
JNI::DefineClass,
JNI::FindClass,
}
複製代碼
咱們看到實例中FindClass對應的函數是JNI::FindClass,定義在當前文件中,FindClass的工做是交給ClassLinker, ClassLinker內部的實現是經過ClassLoader獲取一個ClassTable對象,再經過ClassTable中的一個HashSet獲得對應的Class, ClassLoader其實咱們也比較熟悉,Java層中就有,咱們apk中的dex文件就是須要ClassLoader去加載,最終會將Class裝進一個HashSet中, 所以,咱們FindClass也去這個HashSet中去找.
(ClassLinker內部的實現我就不細講了,我提供幾個關鍵的函數,class_linker.cc的FindClass和LookupClass、class_table.cc的Lookup ,涉及到的文件我都放在AOSP項目中,有興趣同窗能夠去具體看看.)
static jclass FindClass(JNIEnv* env, const char* name) {
CHECK_NON_NULL_ARGUMENT(name);
Runtime* runtime = Runtime::Current();
ClassLinker* class_linker = runtime->GetClassLinker(); //獲取ClassLinker
std::string descriptor(NormalizeJniClassDescriptor(name));
ScopedObjectAccess soa(env);
mirror::Class* c = nullptr;
if (runtime->IsStarted()) {
StackHandleScope<1> hs(soa.Self());
Handle<mirror::ClassLoader> class_loader(hs.NewHandle(GetClassLoader(soa)));
c = class_linker->FindClass(soa.Self(), descriptor.c_str(), class_loader); //查找類
} else {
c = class_linker->FindSystemClass(soa.Self(), descriptor.c_str()); //查找系統類
}
return soa.AddLocalReference<jclass>(c);
}
複製代碼
說完env->FindClass,其實其餘env->方式調用的函數也就大致知道源碼在哪兒了,在接下來的分析中我就只說明下對應函數的做用,具體實現能夠根據 本身的須要深刻去看.
env函數特別多,我這裏只列舉一些咱們經常使用的
新建實例,至關於Java中的new
函數名 | 做用 | 類比Java |
---|---|---|
NewObject | 新建Object | new Object |
NewStringUTF | 新建String字符 | new String() |
NewObjectArray | 新建Object數組 | new Object[] |
New(Type)Array | 新建Type數組,如NewByteArray | new byte[] |
獲取和設置成員變量和類變量,至關於Java中的獲取和設置變量,下面以A a=new A()爲例子
函數名 | 做用 | 類比Java |
---|---|---|
GetFieldID | 獲取成員變量id,全部獲取成員變量的方法都要傳入這個值 | -- |
GetObjectField | 獲取Object類型的成員變量 | a.object |
Get(Type)Field | 獲取Type類型的成員變量,如GetBooleanField | bool b=a.bool |
Set(Type)Field | 設置Type類型的成員變量,如SetBooleanField | a.bool=b |
GetStaticFieldID | 獲取類變量id,全部獲取類變量的方法都要傳入這個值 | -- |
GetStaticObjectField | 獲取Object類型的類變量 | A.object |
GetStatic(Type)Field | 獲取Type類型的類變量,如GetStaticBooleanField | bool b=A.bool |
SetStatic(Type)Field | 設置Type類型的類變量,如SetStaticBooleanField | A.bool=b |
調用成員方法和類方法,至關於Java中的調用方法,下面以A a=new A()爲例子
函數名 | 做用 | 類比Java |
---|---|---|
GetMethodID | 獲取成員方法id,全部獲取成員方法的方法都要傳入這個值 | -- |
CallObjectMethod | 調用返回值爲Object類型的成員方法 | Object o=a.a() |
Call(Type)Method | 調用返回值爲Type類型的成員方法,如CallBooleanMethod | bool b=a.b() |
GetStaticMethodID | 獲取類方法id,全部獲取類方法的方法都要傳入這個值 | -- |
CallStaticObjectMethod | 調用返回值爲Object類型的類方法 | Object o=A.a() |
CallStatic(Type)Method | 調用返回值爲Type類型的類方法,如CallStaticBooleanMethod | bool b=A.b() |
數組相關操做,以bool[] bs=new bool[] 爲例
函數名 | 做用 | 類比Java |
---|---|---|
Get(Type)ArrayElements | 獲取Type類型的數組的某個元素 | bool b=bs[0] |
Set(Type)ArrayElements | 設置Type類型的數組的某個元素 | bs[0]=b |
內存釋放相關,這個是C++獨有的,沒有Java相應的調用
函數名 | 做用 | 類比Java |
---|---|---|
ReleaseStringUTFChars | 釋放String | -- |
Release(Typge)ArrayElements | 釋放Type類型的數組 | -- |
我這裏只是籠統地列舉了一些env函數的做用,對於參數及返回值並無細講,主要是這些屬於API範疇的東西,要用的時候再查也不遲
start函數最後會調用main函數,在獲取main函數時須要傳遞三個參數,第一個是函數所在的類,第二個是函數名稱,第三個就是函數簽名
jmethodID startMeth = env->GetStaticMethodID(startClass, "main", "([Ljava/lang/String;)V");
複製代碼
函數簽名其實就是對一個函數的參數及返回值的一種符號表示,表示形式是 (params)return 下面我列舉一下符號與Java類型的一一對應關係:
基本數據類型和void,咱們能夠看到除了boolean和long表示得不同外,其餘都是以首字母進行表示,我想主要緣由多是B與byte衝突了,L與object衝突
符號 | Java類型 |
---|---|
B | byte |
C | char |
S | short |
I | int |
F | float |
D | double |
Z | boolean |
J | long |
V | void |
引用數據類型和數組,引用數據類型以L開頭,後面接完整路徑,最後有個分號,這個分號必定不要忘記!必定不要忘記!必定不要忘記!數組用 [ 表示
符號 | Java類型 |
---|---|
L/java/lang/String; | String |
[I | int[] |
[L/java/lang/object; | object[] |
咱們回到剛纔的例子 ([Ljava/lang/String;)V ,這個就表示main函數的參數是String[],返回值是void.
咱們在Java中常常用try catch來處理異常很是方便,咱們在C++中調用Java函數時,也能夠去捕獲異常,咱們能夠有兩種方式:
我先講講 ExceptionCheck ,這個函數是會返回一個bool值,true表示有異常,false表示沒有異常
env->CallStaticVoidMethod(cls,mid);
if (env->ExceptionCheck()) { // 檢查JNI調用是否有引起異常
env->ExceptionDescribe(); //打印錯誤日誌堆棧信息
env->ExceptionClear(); // 清除引起的異常
env->ThrowNew(env->FindClass(env,"java/lang/Exception"),"JNI拋出的異常!"); //拋出異常
}
複製代碼
再看看ExceptionOccurred,這個用法其實跟ExceptionCheck差很少,只是它返回的不是bool值,而是當前異常的引用
jthrowable exc = NULL;
exc = env->ExceptionOccurred(); // 返回一個指向當前異常對象的引用
if (exc) {
env->ExceptionDescribe(); //打印錯誤日誌堆棧信息
env->ExceptionClear(); // 清除引起的異常
env->ThrowNew(env->FindClass(env,"java/lang/Exception"),"JNI拋出的異常!"); //拋出異常
}
複製代碼
start函數最後就用到了ExceptionCheck,由於調用Java的方法是可能引起異常的
講完了C++調用Java,咱們再看看Java如何調用C++,咱們接着前面的講,以前經過 env->CallStaticVoidMethod(startClass, startMeth, strArray) 調用了ZygoteInit的 main 函數,咱們就以main函數爲例講解Java調用C++的過程。
main函數開頭有兩個方法調用 startZygoteNoThreadCreation和setpgid,這兩個其實都是native方法,接下來我就以這兩個爲例子。
public static void main(String argv[]) {
...
ZygoteHooks.startZygoteNoThreadCreation(); //設置標記,不容許新建線程
try {
Os.setpgid(0, 0); //設置zygote進程組id爲zygote的pid
} catch (ErrnoException ex) {
throw new RuntimeException("Failed to setpgid(0,0)", ex);
}
...
}
複製代碼
startZygoteNoThreadCreation 定義在platform/libcore/dalvik/src/main/java/dalvik/system/ZygoteHooks中
/* * Called by the zygote when starting up. It marks the point when any thread * start should be an error, as only internal daemon threads are allowed there. */
public static native void startZygoteNoThreadCreation();
複製代碼
startZygoteNoThreadCreation 是一個native方法,咱們知道native方法有兩種註冊方式,一種是靜態註冊,一種動態註冊。
所謂靜態註冊就是根據函數名稱和一些關鍵字就能夠註冊, 好比 startZygoteNoThreadCreation 要靜態註冊的話,它對應的實現函數應該是
JNIEXPORT void JNICALL Java_dalvik_system_ZygoteHooks_startZygoteNoThreadCreation(JNIEnv *, jobject){
}
複製代碼
也就是說首先得有JNIEXPORT,JNICALL這些關鍵字,其次函數名稱必須以Java開頭,後面接的是native函數所在類的完整路徑加native函數名, 最後參數及返回值要相同,參數會多出兩個:
只要你按照這個規則寫,Java的native函數就會自動調用這個C++層的函數。這種靜態的註冊方式有個很差的地方就是函數名太長,書寫不方便,並且在首次調用時會有一個註冊過程, 影響效率,那有沒有其餘方式呢?答案就是動態註冊
其實大多數frameworks層的native函數都是用動態方式註冊的,startZygoteNoThreadCreation函數也是
咱們怎麼尋找startZygoteNoThreadCreation的實現呢?這裏有個規律,Google工程師喜歡以native所在類的完整路徑爲C++的實現類名,好比 startZygoteNoThreadCreation所在類的完整路徑是dalvik.system.ZygoteHooks,咱們嘗試搜索dalvik_system_ZygoteHooks, 就會出現dalvik_system_ZygoteHooks.h和dalvik_system_ZygoteHooks.cc,咱們看下dalvik_system_ZygoteHooks.cc
static JNINativeMethod gMethods[] = {
NATIVE_METHOD(ZygoteHooks, nativePreFork, "()J"),
NATIVE_METHOD(ZygoteHooks, nativePostForkChild, "(JIZLjava/lang/String;)V"),
NATIVE_METHOD(ZygoteHooks, startZygoteNoThreadCreation, "()V"),
NATIVE_METHOD(ZygoteHooks, stopZygoteNoThreadCreation, "()V"),
};
void register_dalvik_system_ZygoteHooks(JNIEnv* env) {
REGISTER_NATIVE_METHODS("dalvik/system/ZygoteHooks");
}
複製代碼
原本動態註冊是個很簡單的過程,直接調用 env->RegisterNatives ,將綁定信息做爲參數便可,可是這個源碼裏寫得比較複雜,我一步步講吧
首先Java的native方法要調用到C++函數,確定得有個鍵值對做爲綁定信息,也就是告訴虛擬機哪一個native該執行哪一個C++函數,gMethods就是這樣一個角色
gMethods數組的類型是JNINativeMethod,咱們回顧下 JNINativeMethod ,它是一個結構體,name表示native函數名,signature表示用字符串描述native函數的參數和返回值, fnPtr表示native指向的C++函數指針,這其實就是動態註冊的映射關係了,將native函數對應一個C++函數
typedef struct {
const char* name;
const char* signature;
void* fnPtr;
} JNINativeMethod;
複製代碼
可是gMethods數組中倒是NATIVE_METHOD,咱們看看這個NATIVE_METHOD是什麼
#define NATIVE_METHOD(className, functionName, signature) \ { #functionName, signature, (void*)(className ## _ ## functionName) }
複製代碼
如何理解這個定義呢?#define是宏定義,也就是說編譯期間要作宏替換,這裏就是把NATIVE_METHOD替換成 {"","",(void*)()},具體怎麼替換呢?咱們看到{}裏有些#、##,#表示字符串化,至關於Java中的toString,##表示字符串化拼接,至關於Java中的 String.format,以NATIVE_METHOD(ZygoteHooks, startZygoteNoThreadCreation, "()V")爲例,替換後就是 {"startZygoteNoThreadCreation","()V",(void*)(ZygoteHooks_startZygoteNoThreadCreation) }
JNINativeMethod只是個結構體,真正註冊的函數是在 REGISTER_NATIVE_METHODS("dalvik/system/ZygoteHooks"),咱們先看看 REGISTER_NATIVE_METHODS
#define REGISTER_NATIVE_METHODS(jni_class_name) \ RegisterNativeMethods(env, jni_class_name, gMethods, arraysize(gMethods))
複製代碼
它也是一個宏定義,指向的是RegisterNativeMethods,這個函數定義在platform/frameworks/base/core/jni/AndroidRuntime.cpp
/* * Register native methods using JNI. */
/*static*/ int AndroidRuntime::registerNativeMethods(JNIEnv* env,
const char* className, const JNINativeMethod* gMethods, int numMethods)
{
return jniRegisterNativeMethods(env, className, gMethods, numMethods);
}
複製代碼
其實它調用的是jniRegisterNativeMethods,這個定義在platform/libnativehelper/JNIHelp.cpp, jniRegisterNativeMethods函數首先是將傳過來的類名字符串找到對應的class,而後就是調用(*env)->RegisterNatives動態註冊JNI, 其實調用這麼多層,動態註冊最關鍵的就是構建一個結構體JNINativeMethod,而後調用(*env)->RegisterNatives,RegisterNatives屬於 虛擬機內的函數了,從此講虛擬機時我再具體去分析,這裏咱們知道它的做用就好了.
extern "C" int jniRegisterNativeMethods(C_JNIEnv* env, const char* className, const JNINativeMethod* gMethods, int numMethods) {
JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
ALOGV("Registering %s's %d native methods...", className, numMethods);
scoped_local_ref<jclass> c(env, findClass(env, className)); //根據類名找到class
if (c.get() == NULL) {
char* tmp;
const char* msg;
if (asprintf(&tmp,
"Native registration unable to find class '%s'; aborting...",
className) == -1) {
// Allocation failed, print default warning.
msg = "Native registration unable to find class; aborting...";
} else {
msg = tmp;
}
e->FatalError(msg);
}
if ((*env)->RegisterNatives(e, c.get(), gMethods, numMethods) < 0) { //動態註冊jni
char* tmp;
const char* msg;
if (asprintf(&tmp, "RegisterNatives failed for '%s'; aborting...", className) == -1) {
// Allocation failed, print default warning.
msg = "RegisterNatives failed; aborting...";
} else {
msg = tmp;
}
e->FatalError(msg);
}
return 0;
}
複製代碼
咱們接着上面的startZygoteNoThreadCreation函數講,由上可知這個native函數實際會調用ZygoteHooks_startZygoteNoThreadCreation, 它定義在platform/art/runtime/native/dalvik_system_ZygoteHooks.cc
static void ZygoteHooks_startZygoteNoThreadCreation(JNIEnv* env ATTRIBUTE_UNUSED, jclass klass ATTRIBUTE_UNUSED) {
Runtime::Current()->SetZygoteNoThreadSection(true);
}
複製代碼
其實它又是調用Runtime的SetZygoteNoThreadSection函數,這個定義在platform/art/runtime/runtime.h,這個函數的實現很是簡單, 就是將zygote_no_threads_這個bool值設置爲想要的值
static Runtime* instance_;
// Whether zygote code is in a section that should not start threads.
bool zygote_no_threads_;
static Runtime* Current() {
return instance_;
}
void SetZygoteNoThreadSection(bool val) {
zygote_no_threads_ = val;
}
複製代碼
由此咱們能夠看到startZygoteNoThreadCreation這個native函數通過層層調用,最終就是將一個bool變量設置爲true. 講得是有點多了, 這裏主要是告訴你們如何去追蹤native函數的實現,由於這是閱讀frameworks層代碼必備的技能. 這裏我仍是再次推薦你們用Source Insight 來看代碼,不論是函數跳轉仍是全局搜索都是很是方便的,詳情請看我以前寫的如何閱讀Android源碼
定義在platform/libcore/luni/src/main/java/android/system/Os.java
這個Os.java類是比較特殊的一個類,這個類至關於一個代理類,全部的方法都是去調用Libcore.os類中相關的方法,
/** * See <a href="http://man7.org/linux/man-pages/man2/setpgid.2.html">setpgid(2)</a>. */
/** @hide */ public static void setpgid(int pid, int pgid) throws ErrnoException { Libcore.os.setpgid(pid, pgid); }
複製代碼
而Libcore.os的實現類是BlockGuardOs,BlockGuardOs的父類是ForwardingOs,ForwardingOs也是個代理類,裏面全部方法都是調用 Linux.java中的對應函數,也就是說Os.java中的函數最終調用的是Linux.java中的函數. 另外在BlockGuardOs類中有重載一些方法,作了一些 Policy權限的檢查.
public final class Libcore {
private Libcore() { }
/** * Direct access to syscalls. Code should strongly prefer using {@link #os} * unless it has a strong reason to bypass the helpful checks/guards that it * provides. */
public static Os rawOs = new Linux();
/** * Access to syscalls with helpful checks/guards. */
public static Os os = new BlockGuardOs(rawOs);
}
複製代碼
咱們再來看看Linux.java的實現是怎樣的
public final class Linux implements Os {
Linux() { }
...
public native void setpgid(int pid, int pgid) throws ErrnoException;
...
}
複製代碼
沒錯,這裏面全是native函數,這些native的實現又在哪兒呢?老方法,找libcore_io_Linux,果真又找到了libcore_io_Linux.cpp
static JNINativeMethod gMethods[] = {
...
NATIVE_METHOD(Linux, setpgid, "(II)V"),
...
}
void register_libcore_io_Linux(JNIEnv* env) {
jniRegisterNativeMethods(env, "libcore/io/Linux", gMethods, NELEM(gMethods));
}
static void Linux_setpgid(JNIEnv* env, jobject, jint pid, int pgid) {
throwIfMinusOne(env, "setpgid", TEMP_FAILURE_RETRY(setpgid(pid, pgid)));
}
複製代碼
註冊方式也是跟以前同樣,用jniRegisterNativeMethods,由此咱們知道setpgid就是調用Linux的系統調用setgpid. 這個系統調的做用是設置進程組id,第一個參數pid是指設置哪一個進程所屬的進程組,若是是0,就是當前進程所屬的進程組,第二個參數是設置的id值, 若是是0,那麼就把當前進程的pid做爲進程組的id. 因此setgpid(0,0)的意思就是將zygote進程所在進程組id設置爲zygote的pid
小結
做爲進入Java世界的鋪墊,本篇講解了C++與Java之間的橋樑JNI,有了它,C++和Java就能夠相互調用,本文只是講了一些皮毛的東西,要深刻理解和使用JNI,請參考英文官方,中文手冊