Android开发Android技术知识Android开发经验谈

Android JNI概述

2018-12-01  本文已影响5人  lbtrace

本文基于Android 9.0源码分析

Android JNI简介

JNI是Java Native Interface, 它提供了一种从字节码(Java/Kotlin)到Native代码(c/c++/assembly)的交互方式

JavaVM与JNIEnv

JNI定义了两个关键的数据结构:JavaVM和JNIEnv

Android Native方法注册

使用RegisterNatives显示注册

示例

public class MainActivity extends AppCompatActivity {

    // Used to load the 'native-lib' library on application startup.
    static {
        System.loadLibrary("native-lib");
    }
    ......
    /**
     * A native method that is implemented by the 'native-lib' native library,
     * which is packaged with this application.
     */
    public native String stringFromJNI();
}
// 1) using RegisterNatives register native method
static jstring StringFromJNI(JNIEnv *env, jobject obj) {
  std::string hello = "Hello from C++";
  return env->NewStringUTF(hello.c_str());
}

static JNINativeMethod jni_methods[] = {
    {"stringFromJNI", "()Ljava/lang/String;", (void *)StringFromJNI}
};

// 这里JNI_OnLoad不需要添加extern "C"
JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) {
  JNIEnv* env;

  if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
    return -1;
  }

  if (env->RegisterNatives(env->FindClass("lbtrace/jniregister/MainActivity"),
      jni_methods, sizeof(jni_methods) / sizeof(JNINativeMethod)) != JNI_OK)
    return -1;

  return JNI_VERSION_1_6;
}

原理

运行时动态查找

示例

// 2) Android Runtime dynamic find native method
// 必须添加extern "C"
extern "C" JNIEXPORT jstring JNICALL
Java_lbtrace_jniregister_MainActivity_stringFromJNI(
        JNIEnv* env,
        jobject /* this */) {
    std::string hello = "Hello from C++";
    return env->NewStringUTF(hello.c_str());
}

原理

思考:Native方法中jobject类型的参数是什么?

在先前的Android版本中是Local Reference

Android JNI本地引用和全局引用

对于基本类型,进行JNI调用时,可以直接拷贝到Native方法,但是对于Java对象采用引用传递。虚拟机必须能够追踪到所有传递到Native方法的Java对象,避免被GC回收。当Native方法不再需要Java对象时,必须有一种方法通知虚拟机。

对于本地引用及全局引用的实现细节,将在后续文章中讨论。

Local Reference

本地引用在Native方法调用期间可用,在Native方法返回后自动释放。

Global Reference

全局引用不会自动释放,直到显式的删除

Weak Global Reference

弱全局引用是一种特殊的全局引用,与普通的全局引用不同的是,GC可以回收弱全局引用关联的Java对象。当GC运行时,会回收一个仅仅被弱全局引用关联Java对象。所以在使用之前,应该首先检查弱全局引用的Java对象是否被回收。

思考:Android中Java引用到底是什么?

public class MainActivity extends AppCompatActivity {

    // Used to load the 'native-lib' library on application startup.
    static {
        System.loadLibrary("native-lib");
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        ......
        TestObject testObject = new TestObject();
        Log.i("JNI", "Before : " + testObject.month + "月" +
                testObject.day + "日");
        stringFromJNI(testObject);
        Log.i("JNI", "After : " + testObject.month + "月" +
                testObject.day + "日");
        ......
    }

    public native String stringFromJNI(Object obj);

    static class TestObject {
        int month = 11;
        int day = 29;
    }
}

extern "C" JNIEXPORT jstring JNICALL
Java_lbtrace_jniregister_MainActivity_stringFromJNI(
        JNIEnv* env,
        jobject obj, jobject test_obj) {
    std::string hello = "Hello from C++";

    // Java TestObject Mirror address
    int32_t *test_obj_ptr = reinterpret_cast<int32_t *>(
        *(reinterpret_cast<int32_t *>(test_obj)));

    // According TestObject memory layout
    // Swap month field and day field in TestObject
    int32_t tmp = *(test_obj_ptr + 2);
    *(test_obj_ptr + 2) = *(test_obj_ptr + 3);
    *(test_obj_ptr + 3) = tmp;

    return env->NewStringUTF(hello.c_str());
}

参考

上一篇 下一篇

猜你喜欢

热点阅读