目录
1 SimpleJNI
Android有个JNI Demo - SimpleJNI,简单介绍了JNI的用法及流程。
1.1 代码位置
development/samples/SimpleJNI
1.2 目录结构
├── Android.bp
├── AndroidManifest.xml
├── jni
│ ├── Android.bp
│ └── native.cpp
└── src
└── com
└── example
└── android
└── simplejni
└── SimpleJNI.java
1.3 Java
SimpleJNI.java中,Native.add为native方法,调用Native.add时,首先会通过System.loadLibrary加载C++库libsimplejni.so到虚拟机(static代码块先执行且只执行一次)。
package com.example.android.simplejni;
public class SimpleJNI extends Activity {
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
TextView tv = new TextView(this);
int sum = Native.add(2, 3);
tv.setText("2 + 3 = " + Integer.toString(sum));
setContentView(tv);
}
}
class Native {
static {
// The runtime will add "lib" on the front and ".o" on the end of
// the name supplied to loadLibrary.
System.loadLibrary("simplejni");
}
static native int add(int a, int b);
}
1.4 bp
jni/Android.bp中将native.cpp编译为libsimplejni.so。
cc_library_shared {
name: "libsimplejni",
// All of the source files that we will compile.
srcs: ["native.cpp"],
// All of the shared libraries we link against.
shared_libs: ["liblog"],
// No static libraries.
static_libs: [],
cflags: [
"-Wall",
"-Werror",
],
header_libs: ["jni_headers"],
stl: "none",
sdk_version: "current",
}
Android.bp中app依赖了libsimplejni.so。
android_app {
name: "SimpleJNI",
srcs: ["**/*.java"],
jni_libs: ["libsimplejni"],
optimize: {
enabled: false,
},
sdk_version: "current",
dex_preopt: {
enabled: false,
},
}
1.5 C++
虚拟机加载libsimplejni.so时,会调用标准接口JNI_OnLoad(对应的卸载时调用JNI_OnUnload),使用JavaVM的GetEnv获取JNIEnv,然后通过registerNatives注册C++方法,也就是把Java方法和C++方法关联起来。
/*
* This is called by the VM when the shared library is first loaded.
*/
typedef union {
JNIEnv* env;
void* venv;
} UnionJNIEnvToVoid;
jint JNI_OnLoad(JavaVM* vm, void* /*reserved*/)
{
UnionJNIEnvToVoid uenv;
uenv.venv = NULL;
jint result = -1;
JNIEnv* env = NULL;
ALOGI("JNI_OnLoad");
if (vm->GetEnv(&uenv.venv, JNI_VERSION_1_4) != JNI_OK) {
ALOGE("ERROR: GetEnv failed");
goto bail;
}
env = uenv.env;
if (registerNatives(env) != JNI_TRUE) {
ALOGE("ERROR: registerNatives failed");
goto bail;
}
result = JNI_VERSION_1_4;
bail:
return result;
}
registerNatives通过registerNativeMethods完成注册。
/*
* Register native methods for all classes we know about.
*
* returns JNI_TRUE on success.
*/
static int registerNatives(JNIEnv* env)
{
if (!registerNativeMethods(env, classPathName,
methods, sizeof(methods) / sizeof(methods[0]))) {
return JNI_FALSE;
}
return JNI_TRUE;
}
classPathName为native方法的package名和class名拼起来。
static const char *classPathName = "com/example/android/simplejni/Native";
methods数组中元素类型为JNINativeMethod,依次为Java方法名、Java方法签名、C++方法名。
static jint add(JNIEnv* /*env*/, jobject /*thiz*/, jint a, jint b) {
int result = a + b;
ALOGI("%d + %d = %d", a, b, result);
return result;
}
static JNINativeMethod methods[] = {
{"add", "(II)I", (void*)add },
};
最终,registerNativeMethods中使用JNIEnv的FindClass获取Java类,然后是JNIEnv的RegisterNatives完成注册(支持注册多个方法)。
/*
* Register several native methods for one class.
*/
static int registerNativeMethods(JNIEnv* env, const char* className,
JNINativeMethod* gMethods, int numMethods)
{
jclass clazz;
clazz = env->FindClass(className);
if (clazz == NULL) {
ALOGE("Native registration unable to find class '%s'", className);
return JNI_FALSE;
}
if (env->RegisterNatives(clazz, gMethods, numMethods) < 0) {
ALOGE("RegisterNatives failed for '%s'", className);
return JNI_FALSE;
}
return JNI_TRUE;
}
上面的例子是一种动态注册方法,是Android中常用的方法,另外还有一种传统的Java静态注册方法。Android中有很多使用jni的地方,流程大都是类似的。
2 jni headers
Android中使用了jni的地方,依赖了libnativehelper中的jni头文件。
2.1 代码位置
libnativehelper/include_jni/jni.h
2.2 引用类型
jni有三种引用类型,局部引用、全局引用和弱全局引用。
局部引用,也称本地引用,通常是在函数中创建并使用,函数返回时自动释放,当然可以使用DeleteLocalRef函数手动释放,存在某些场景下native函数在返回前占用了大量内存。
全局引用,可以跨方法、跨线程使用,直到被开发者显式释放。类似局部引用,一个全局引用在被释放前保证引用对象不被GC回收。和局部应用不同的是,只能通过NewGlobalRef和ReleaseGlobalRef两个函数来创建和释放全局引用。
弱全局引用,与全局引用类似,创建跟删除都需要由编程人员来进行,这种引用与全局引用一样可以在多个本地native有效,不一样的是,弱引用将不会阻止垃圾回收期回收这个引用所指向的对象,所以在使用时需要多加小心,它所引用的对象可能是不存在的或者已经被回收,因此在使用前需要使用JNIEnv的IsSameObject函数来判断。
typedef enum jobjectRefType {
JNIInvalidRefType = 0,
JNILocalRefType = 1,
JNIGlobalRefType = 2,
JNIWeakGlobalRefType = 3
} jobjectRefType;
2.3 环境变量
JavaVM,是Java虚拟机在JNI层的代表,JNI全局仅仅有一个JavaVM结构中封装了一些函数指针(或叫函数表结构),JavaVM中封装的这些函数指针主要是对JVM操作接口。
JNIEnv,是一个线程相关的结构体,该结构体代表了Java在本线程的执行环境。
区别:JavaVM全局只有一个,JNIEnv每个线程都有一个。
3 AndroidRuntime
android_runtime是Android的运行时库,是C++层与Java层之间的桥梁。
3.1 代码位置
frameworks/base/core/jni
3.2 启动模式
AndroidRuntime有四种启动模式,分别是Zygote、SystemServer、Application和Tool。
enum StartMode {
Zygote,
SystemServer,
Application,
Tool,
};
3.3 运行阶段
AndroidRuntime运行时有四个重要阶段,对应的有四个回调,分别是onVmCreated、onStarted、onZygoteInit、onExit。
/**
* This gets called after the VM has been created, but before we
* run any code. Override it to make any FindClass calls that need
* to use CLASSPATH.
*/
virtual void onVmCreated(JNIEnv* env);
/**
* This gets called after the JavaVM has initialized. Override it
* with the system's native entry point.
*/
virtual void onStarted() = 0;
/**
* This gets called after the JavaVM has initialized after a Zygote
* fork. Override it to initialize threads, etc. Upon return, the
* correct static main will be invoked.
*/
virtual void onZygoteInit() { }
/**
* Called when the Java application exits to perform additional cleanup actions
* before the process is terminated.
*/
virtual void onExit(int /*code*/) { }
3.4 运行链路
AndroidRuntime运行时的链路大致如下:
a AndroidRuntime::start开始 > startVm启动虚拟机 > JNI_CreateJavaVM创建虚拟机 > onVmCreated
b startReg注册native方法 > register_jni_procs注册所有的jni方法(注册方法与上面提到的流程类似)
c 调用Java main函数(Java程序标准入口)包括下面两种情况
c.1 com.android.internal.os.ZygoteInit.main > onZygoteInit
c.2 com.android.internal.os.RuntimeInit.main > onStarted
d AndroidRuntime::exit > onExit
4 app process
AndroidRuntime通过工具app_process触发,这个工具作为Java程序启动的入口。
4.1 代码路径
/frameworks/base/cmds/app_process
4.2 启动模式
app_process与AndroidRuntime对应,有下面四种启动模式。
// --zygote : Start in zygote mode
// --start-system-server : Start the system server.
// --application : Start in application (stand alone, non zygote) mode.
// --nice-name : The nice name for this process.
4.3 AndroidRuntime
下面是start AndroidRuntime的情况,与上面的链路相符。
if (zygote) {
runtime.start("com.android.internal.os.ZygoteInit", args, zygote);
} else if (className) {
runtime.start("com.android.internal.os.RuntimeInit", args, zygote);
} else {
fprintf(stderr, "Error: no class name or --zygote supplied.\n");
app_usage();
LOG_ALWAYS_FATAL("app_process: no class name or --zygote supplied.");
}