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.cppint 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去查找函数省时间。
参考

更多相关文章

  1. 自定义全屏Dialog
  2. android ndk调用Log.getStackTraceString(new Throwable())输出
  3. Android(安卓)调用系统浏览器 出现activitynotfoundexception 解
  4. 几个activity跳转特效的实现
  5. Android(安卓)SDL移植版学习笔记
  6. FregServer进程,返回BR_REPLY
  7. Android(安卓)如何调用MediaPlayer播放视频[状态机]【图】
  8. android framework添加自定义服务,实现java层api调用
  9. Android实现自己的回调函数

随机推荐

  1. Android 系统HAL 简介
  2. [Android--Tool]Android如何将他人的代码
  3. android 笔记 ---- 使用Hessian与Java服
  4. Android样式的开发:shape篇
  5. Android(安卓)multidex 使用 与 实现原理
  6. [置顶] 【电子书下载】《Android应用程序
  7. Android 中的Parcelable序列化对象
  8. Android SDK 2.3与Eclipse最新版开发环境
  9. Android权限安全(5)组件的android:export
  10. 深入浅出Android消息系统之一