【FLY】Android JNI流程

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.");
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值