Android NDK 开发

Android NDK 开发

JNI 基础

  1. Java 与 C/C++ 基本类型对应
Java jni 占用大小
byte jbyte 1
short jshort 2
int jint 4
float jfloat 4
long jlong 8
double jdouble 8
char jchar 2
boolean jboolean 1
void void

定义在

JDK_HOME/include/jni.h

JDK_HOME/include/platform/jni_md.h

  1. 引用类型对应
Java引用类型 Java jni
Class jclass
Object jobject
String jstring
Array jarray,jbooleanArray,jbyteArray,jcharArray….
throwable jthrowable
  1. 域和方法IDs
    jfiledID,jmethodID

    struct _jfieldID;              /* opaque structure */ 
    typedef struct _jfieldID *jfieldID;   /* field IDs */ 
    
    struct _jmethodID;              /* opaque structure */ 
    typedef struct _jmethodID *jmethodID; /* method IDs */ 
  2. jvalue Type

    typedef union jvalue { 
    jboolean z; 
    jbyte    b; 
    jchar    c; 
    jshort   s; 
    jint     i; 
    jlong    j; 
    jfloat   f; 
    jdouble  d; 
    jobject  l; 
    } jvalue; 
  3. 类型签名(Type Signatures)
Type Signature Java Type
Z boolean
B byte
C char
S short
I int
J long
F float
D Double
L fully-qualified-class; full-qualified-class
[ type type[]
(arg-types)ret-type method type

对于Java方法:
long f (int n,String s,int[] arr);

有如下签名:
(ILjava/lang/String;[I)J
需要注意的是引用类型String,必须像这样Ljava/lang/String; 分号不能少。
参考

JNI 几个重要函数及变量

  1. System.loadLibrary()
    导入so库函数。比如你的库是libhelljni.so 那么导入是这样的System.loadLibrary(“helljni”)
    该函数调用一般是在静态代码块中

    static{
       System.loadLibrary("helljni");
    }
  2. JNIEnv
    JNIEnv JNI接口指针 用于调用方法等

  3. JNI_OnLoad
    在加载so之后需要做的工作,主动注入native函数需要实现

JNI 主动注册和被动注册 native 函数

  1. 调用 RegisterNatives 主动注册JNI函数

    Android 系统模块一般都用这个方法。具体可查看源码
    frameworks\base\core\jni
    frameworks\base\services\jni
    fremeworks\base\media\jni

    我们来拿源码Media部分代码看看这个这过程这么做的。
    首先,需要在Java代码中声明native函数
    framework/base/media/java/android/media/MediaScanner

    package android.media;
    .......
    public class MediaScanner
    {
       static {
           System.loadLibrary("media_jni");
           native_init();
       }
       .........
           private static native final void native_init();
           private native final void native_setup();
           private native final void native_finalize();
    }

    C++实现/framework/base/media/jni/android_media_MediaScanner.cpp

    // This function gets a field ID, which in turn causes class initialization.
    // It is called from a static block in MediaScanner, which won't run until the
    // first time an instance of this class is used.
    static void android_media_MediaScanner_native_init(JNIEnv *env)
    {
      ALOGV("native_init");
      jclass clazz = env->FindClass(kClassMediaScanner);
      if (clazz == NULL) {
          return;
      }
    
      fields.context = env->GetFieldID(clazz, "mNativeContext", "I");
      if (fields.context == NULL) {
          return;
      }
    }
    .......
    static JNINativeMethod gMethods[] = {
    ......
    
    {
        "native_init",
        "()V",
        (void *)android_media_MediaScanner_native_init
    },
    
    {
        "native_setup",
        "()V",
        (void *)android_media_MediaScanner_native_setup
    },
    
    {
        "native_finalize",
        "()V",
        (void *)android_media_MediaScanner_native_finalize
    },
    };
    ........
    // This function only registers the native methods, and is called from
    // JNI_OnLoad in android_media_MediaPlayer.cpp
    int register_android_media_MediaScanner(JNIEnv *env)
    {
      return AndroidRuntime::registerNativeMethods(env,
                kClassMediaScanner, gMethods, NELEM(gMethods));
    }

    真正导入so然后调用JNI_OnLoad 然后注册registerNativeMethods否则找不到native函数
    我们看看framework/base/media/jni/android_media_MediaPlayer.cpp

    ......
    extern int register_android_media_MediaScanner(JNIEnv *env);
    ......
    jint JNI_OnLoad(JavaVM* vm, void* reserved)
    {
    JNIEnv* env = NULL;
    jint result = -1;
    
    if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
        ALOGE("ERROR: GetEnv failed\n");
        goto bail;
    }
    assert(env != NULL);
    
    ........
    
    if (register_android_media_MediaScanner(env) < 0) {
        ALOGE("ERROR: MediaScanner native registration failed\n");
        goto bail;
    }
    .......
    
    /* success -- return valid version number */
    result = JNI_VERSION_1_4;
    
    bail:
      return result;
    }
    
  2. 被动注册Native函数

    这个很多jni编写教程都是这种方法,大致流程是这样的

    • 编写Java native 代码,javac 生产class(Android Studio 可以自动构建)
    • 使用javah命令,生产包含JNI本地函数原型的头文件
      一般生产的函数原型头文件是这样的
       /* DO NOT EDIT THIS FILE - it is machine generated */
    
        #include 
       /* Header for class com_XXX_XXXX_utils_NativeSpeedTest    */
    
      #ifndef _Included_com_XXX_XXXX_utils_NativeSpeedTest
      #define _Included_com_XXX_XXXX_utils_NativeSpeedTest
      #ifdef __cplusplus
      extern "C" {
      #endif
        /*
         * Class:     com_XXX_XXXX_utils_NativeSpeedTest
         * Method:    startDownloadWireless
         * Signature: ([Ljava/lang/String;II)I
         */
        JNIEXPORT jint JNICALL Java_com_XXX_XXXX_utils_NativeSpeedTest_startDownloadWireless
          (JNIEnv *env, jclass cclass, jobjectArray jurls, jint jconns , jint jduration);
    
        /*
         * Class:     com_XXX_XXXX_utils_NativeSpeedTest
         * Method:    signatureVerify
         * Signature: (Ljava/lang/String;)I
         */
        JNIEXPORT jint JNICALL Java_com_XXX_XXXX_utils_NativeSpeedTest_signatureVerify
          (JNIEnv *env, jclass cclass, jstring jstr);
    
        /*
         * Class:     com_XXX_XXXX_utils_NativeSpeedTest
         * Method:    init
         * Signature: (Ljava/lang/String;)I
         */
        JNIEXPORT jint JNICALL Java_com_XXX_XXXX_utils_NativeSpeedTest_init
          (JNIEnv *env, jclass cclass, jstring jstr);
    
        /*
         * Class:     com_XXX_XXXX_utils_NativeSpeedTest
         * Method:    uninit
         * Signature: ()V
         */
        JNIEXPORT void JNICALL Java_com_XXX_XXXX_utils_NativeSpeedTest_uninit
          (JNIEnv *env, jclass cclass);
    
        /*
         * Class:     com_XXX_XXXX_utils_NativeSpeedTest
         * Method:    startDownload
         * Signature: ([Ljava/lang/String;II)I
         */
        JNIEXPORT jint JNICALL Java_com_XXX_XXXX_utils_NativeSpeedTest_startDownload
          (JNIEnv *env, jclass cclass, jobjectArray jurls, jint jconns, jint jduration);
    
        /*
         * Class:     com_XXX_XXXX_utils_NativeSpeedTest
         * Method:    statUpload
         * Signature: ([Ljava/lang/String;II)I
         */
        JNIEXPORT jint JNICALL Java_com_XXX_XXXX_utils_NativeSpeedTest_statUpload
          (JNIEnv *env, jclass cclass, jobjectArray jurls, jint jconns, jint jduration);
    
        /*
         * Class:     com_XXX_XXXX_utils_NativeSpeedTest
         * Method:    callBack
         * Signature: (Ljava/lang/String;Ljava/lang/String;)I
         */
        JNIEXPORT jint JNICALL Java_com_XXX_XXXX_utils_NativeSpeedTest_callBack
          (JNIEnv *env, jclass thiz, jstring jclassname, jstring jmethodname);
    
        #ifdef __cplusplus
        }
        #endif
    
    #endif
    

    自动生产的话是没有定义变量像这样


    /*
    * Class: com_XXX_XXXX_utils_NativeSpeedTest
    * Method: callBack
    * Signature: (Ljava/lang/String;Ljava/lang/String;)I
    */
    JNIEXPORT jint JNICALL Java_com_XXX_XXXX_utils_NativeSpeedTest_callBack
    (JNIEnv , jclass, jstring, jstring);

    自己加上变量名

    • 实现JNI本地函数
      然后新建.cpp文件实现上面函数
    • 生产C 共享库
      ndk-build 生产so文件,Android studio 需要做ndk配置。
      build.gradle

      android{
       defaultConfig{
              ndk{
                  moduleName "XXX"
               ldLibs "log"
               abiFilters "armeabi", "armeabi-v7a", "x86"
              }
       }
      }

      在main/下建立 jni和jniLibs

      gradle.properties加入

      android.useDeprecatedNdk=true

    • 通过JNI,调用JNI本地函数

这两种方式建议用主动注册方式(或者动态注册),编写起来函数名简便,且不需要VM去查找函数省时间。
参考

你可能感兴趣的:(android,ndk,android-ndk开发)