Android Studio开发之 JNI 篇

作者: 某昆 | 来源:发表于2017-10-21 11:57 被阅读2856次

    前言

    Android上层应用使用java开发,不过java并不适合密集型运算,比如图片处理等,遇到密集型运算,一般使用c/c++完成。

    Java虚拟机支持调用c/c++代码,即JNI(Java Native Interface),它提供了若干的API实现了Java和其他语言的通信。为方便android平台上使用JNI技术,提供了NDK开发包,可以将NDK理解为对JNI的进一步封装,方便开发使用罢了。

    JNI开发方式有多种,可以在Android 源码中开发,也可以利用其它工具,但都比较烦琐或者要下载很多东西,Android Studio也支持JNI开发,使用起来也比较方便,本文主要讲述下如何使用Android Studio进行JNI开发。

    NDK设置

    NDK需要下载,一共有两种方式,建议从Android Studio中下载。

    • 从Android Studio中打开SDK Manager,进入如下界面并且勾选NDK选项。
    • 点击应用,安装完后重启Android Studio即可。

    也可以从官网下载,然后在Android Studio中设置,这种方式不再讲述。

    JNI开发

    本章中以高斯模糊图像处理为示例,学习如何进行JNI开发。

    1、新建一个Android工程,注意Android Studio对包名的处理,它的默认处理非常地别扭,如果不喜欢这种包名命名方式,可以点击 Edit 进行更改。

    2、将工程以Project视图显示,方便查找具体文件。

    3、在项目gradle.properties文件中加上以下代码,表示我们要使用NDK进行开发。

      android.useDeprecatedNdk=true
    

    4、查看项目local.properties中是否有加入ndk和sdk的路径,如果没有需要补充。

      ndk.dir=D\:\\android-sdk\\ndk-bundle
      sdk.dir=D\:\\android-sdk
    

    5、在app文件夹下的build.gradle的defaultConfig里加入如下代码

      ndk {
            moduleName "ImageBlur"       //生成的so文件名字,调用C程序的代码中会用到该名字
            abiFilters "armeabi", "armeabi-v7a", "x86" //输出指定三种平台下的so库
            ldLibs "log", "jnigraphics", "android"    //jni中需要用到的其它库
        }
    

    6、定义native方法

    7、生成h文件,打开Android Studio提供的命令行工具Terminal,输入以下指令。

      cd app/src/main/java
      javah -jni 包名+类名
    

    本例中报错,“无法确定Bitmap的签名”,根据网上搜索结果,需要指出 android.jar 文件的位置才行,于是按如下方法生成 h 文件。

      javah -classpath C:\PROGRA~2\Android\android-sdk\platforms\android-8\android.jar;. com.test.JniTest
    

    8、建立 JNI 文件夹,复制生成的 h 文件到 JNI 文件夹中来。 选择File->New->Folder->JNI Folder

    注意:在弹出创建 JNI 文件夹的对话框中勾选 Change Folder Location,并在下面输入文件夹名,如下图所示。

    一般来说JNI相关文件放在 src/main/jni 之中。

    9、新建c文件,实现对应接口,在java代码中完成 JNI 接口调用。

    方法访问

    通常使用JNI,都是在java代码中访问c或c++代码,但是JNI还提供这样的能力,可以在本地代码中访问java代码。

    JNI方法中都会存在JNIEnv变量,它是本地函数的第一个参数,其本质指向了一个函数列表的指针。

    调用Java方法一共有两个步骤,先找到这个java类的class对象,再得到java类具体方法的method,类似于反射,最后调用:

      char* classname = "wjy/geridge/com/testndk/jni/JniUtils";  
      jclass dpclazz = (*env)->FindClass(env, classname);  
      //参数介绍 : 第二个参数是Class对象, 第三个参数是方法名,第四个参数是方法的签名, 获取到调用的method  
      jmethodID methodID = (*env)->GetMethodID(env, dpclazz,"add", "(II)I");  
    

    JNI识别Java方法 : JNI依靠函数名 和 方法签名 识别方法, 函数名是不能唯一识别一个方法的, 因为方法可以重载, 类型签名代表了 参数 和 返回值;

    Java类型 与 类型签名对照表 : 注意 boolean 与 long 不是大写首字母, 分别是 Z 与 J, 类是L全限定类名, 数组是[元素类型签名;

    -- 类的签名规则 :L + 全限定名 + ;三部分, 全限定类名以 / 分割;

    Java类型 类型签名

    boolean Z
    byte B
    char C
    short S
    int I
    long J
    float F
    double D
    L全限定类名
    数组 [元素类型签名

    -- 签名规则 : (参数1类型签名参数2类型签名参数3类型签名参数N类型签名...)返回值类型签名, 注意参数列表中没有任何间隔;

    如. long function(int n, String str, int[] arr);
    该方法的签名 :(ILjava/lang/String;[I)J
    上面的例子中两个参数都是int类型返回值也是int所以是:(II)I
    例子中调用了GetMethodID方法去获取add方法的唯一标识,如果add是个静态方法呢?

      jmethodID methodID = (*env)->GetStaticMethodID(env, dpclazz,"add", "(II)I");
    

    可以看到获取静态方法需要调用GetStaticMethodID方法,参数与GetMethodID相同

    已经获取了methodId了,接下来就要调用具体方法了。

    普通方法 : CallTypeMethod , 其中的Type随着返回值类型的不同而改变;
    参数介绍 : ① JNIEnv指针 ②调用该native方法的对象 ③方法的methodID ④⑤... 后面是可变参数

    同样的如果是调用静态方法应该使用对应的CallStaticTypeMethod, 其中的Type随着返回值类型不同而改变;
    上面的方法都在jni.h中声明(android-sdk-windows\ndk-bundle\platforms\android-24\arch-arm\usr\include\jni.h)

    结语

    在gradle构建的过程中有可能出现这样或那样的异常,查看gradle构建日志,即可知道具体异常,而查看gradle构建日志按钮比较隐蔽。

    比如说,使用c文件或c++文件,往往会有一些不同,使用c++文件可能编译报错,此时则需要打开gradle console查看具体原因。

    相关文章

      网友评论

        本文标题:Android Studio开发之 JNI 篇

        本文链接:https://www.haomeiwen.com/subject/wghiuxtx.html