android开发-NDK-JNI入门教程

1.简介


NDK:

  • Native Development Kit , Android的一个工具开发包,NDK是属于 Android 的,与java并无直接关系

    作用:

  • 快速开发C、 C++的动态库,并自动将.so和应用一起打包成 APK 
  • 通过 NDK在 Android中 使用 JNI与本地代码(如C、C++)交互

NDK与JNI的关系:

jni是实现最终目的 , 而 ndk 是实现 jni的功能调用。

JNI

是java native interface , java本地接口。如果java代码需要调用底层的c/c++代码,就需要通过jni来实现。android的底层是linux,linux之上是c/c++代码,而我们app是java代码。有些需要高效率的事情,比如音视频编解码,比如3d绘图等就需要用c/c++来实现了。

JNI实现步骤

  1. 在Java代码中声明Native方法(即需要调用的本地方法)
  2. 编译上述 Java源文件javac(得到 .class文件)
  3. 通过 javah 命令对.class文件导出JNI的头文件(.h文件)
  4. 使用.c文件引入.h头文件 并且实现在 Java代码中声明的Native方法 
  5. 如 Java 需要与 C++ 交互,那么就用C++实现 Java的Native方法
  6. 配置app模块的 build.gradle (ndk配置产生的.so的名字,不同的系统) 
  7. sync project
  8. 运行程序

2.步骤详解

以下操作都是在AndroidStudio中进行。


1.首先要准备一个ndk,不要从androidstudio里面直接下载,具体怎么下载配置看另一篇文章:

https://blog.csdn.net/qq_38261174/article/details/83210458 这里讲了如何下载及配置ndk。

然后在androidstudio中配置ndk:

2.androidstudio新建一个工程,不引入c++ Support。

我建的工程名字是 MyJniStudy

包名是 com.liuyan.myjnistudy

3.没有在androidstudio中配置ndk的先按照上面图片配置ndk。

4.在 main 目录下新建一个JNI目录。

上面图片中没有打钩的,别慌,可以在 build.gralde中配置:

apply plugin: 'com.android.application'

android {
    ...
    defaultConfig {
        ...
    }
    buildTypes {
       ...
    }

//这里配置
    sourceSets { main { jni.srcDirs = ['src/main/jni', 'src/main/jni/'] } }

}

dependencies {
    ...
}

5. 在java目录下,新建一个包

我的包名是 my

6. 在 my包下面,新建一个java文件,在java文件中写一个 native方法。

我的java文件名字是 NdkJniTest

方法名字是 mytest

package my;

public class NdkJniTest {

    //要先加载so库,才能调用native方法
    //so库的名字(NdkJniSoName)
    //下面步骤会在 build.gralde中配置so库的名字是这个
    static {
        System.loadLibrary("NdkJniSoName");
    }

    public native String mytest();
}

方法名字是红色的,不要管,接下来可能方法一直是红色的,不要管,按照步骤来就行了。

7. cmd命令 进入到 my包

 执行命令 :  javac NdkJniTest.java   (编译 .java 产生 .class)

这里我产生了一个错误:

D:\androidstudio-xiangmu\MyJniStudy\app\src\main\java\my>javac NdkJniTest.java
NdkJniTest.java:7: 错误: 编码GBK的不可映射字符                                                                                                   
    //涓嬮潰姝ラ浼氬湪 build.gralde涓厤缃畇o搴撶殑鍚嶅瓧鏄繖涓?                                                                            
                                            ^
1 个错误   

我把注释全删 ,然后重新执行一次命令就好了。

这时会在my包下面多出一个文件 

8.cmd命令进入到java 目录,不进入任何包里面

执行命令   javah -jni my.NdkJniTest   ( .class产生 .h文件)

这时会在java目录下产生一个  my_NdkJniTest.h  文件。

9.将 .h 文件移动到 jni文件夹下面。

.h 文件内容如下:

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class my_NdkJniTest */

#ifndef _Included_my_NdkJniTest
#define _Included_my_NdkJniTest
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     my_NdkJniTest
 * Method:    mytest
 * Signature: ()Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_my_NdkJniTest_mytest
  (JNIEnv *, jobject);

#ifdef __cplusplus
}
#endif
#endif

10.在 jni 文件夹下面新建一个 . C 文件 ,不是 .cpp文件

名字自己取。

我的 c文件名字是 jnitest.c

创建好 .c文件之后,编写这个 .c文件。

.c文件 要引入之前的 .h头文件,并实现头文件其中的方法。

我的 .c文件其中的代码:

#include "my_NdkJniTest.h"

JNIEXPORT jstring JNICALL Java_my_NdkJniTest_mytest
        (JNIEnv *env, jobject obj) {
    return (*env)->NewStringUTF(env, "This is my Jni test!!!");
}

11.到这一步,库文件和 jni接口文件都已经在上面全部准备好了

接下来

配置 gradle.properties 文件,在末尾加入:

android.useDeprecatedNdk=true

配置ndk  修改app模块的 build.gradle文件,如下:

apply plugin: 'com.android.application'

android {
    ...
    defaultConfig {
        ...

        ndk {
            moduleName "NdkJniSoName"			         //生成的so名字
            abiFilters "armeabi", "armeabi-v7a", "x86"	//输出指定三种abi体系结构下的so库。
        }

    }
    buildTypes {
        ...
    }
    sourceSets { main { jni.srcDirs = ['src/main/jni', 'src/main/jni/'] } }
}

dependencies {
    ...
}

12.所有准备工作都做好了

sync project

13.开始使用

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        NdkJniTest ndkJniTest = new NdkJniTest();
        String s = ndkJniTest.mytest(); //native方法
        Log.i("TAG",s);

    }
}

14.结果,打印如下:

15.是不是还有一个疑惑?

我们产生的.so库在哪儿呢?自动生成的哦!!!

想要了解更多的,可以自己研究下 Android.mk文件,其实就是我们在 build.gradle 中配置的 ndk

3. 过瘾


按照上面的流程,相信你已经成功了!

是不是还不过瘾?好,我们接着来

我们在 NdkJniTest.java 中,再加入一个 native 方法。

package my;

public class NdkJniTest {

    static {
        System.loadLibrary("NdkJniSoName");
    }

    public native String mytest();

    //新加入的方法
    public native int AddAB(int a , int b);

}

这个native方法将要 实现两数之和 ,并将这个和返回。

重新 生成 .class 文件,重新生成 .h文件。

如果你不想要重新生成,你可以自己直接修改 .h文件,在其中加入:

JNIEXPORT jint JNICALL Java_my_NdkJniTest_AddAB
   (JNIEnv *, jobject, jint, jint);

如果你的包名和我的不一样,自己根据情况修改上面的代码内容。

所以现在的.h文件所有内容如下:

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class my_NdkJniTest */

#ifndef _Included_my_NdkJniTest
#define _Included_my_NdkJniTest
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     my_NdkJniTest
 * Method:    mytest
 * Signature: ()Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_my_NdkJniTest_mytest
  (JNIEnv *, jobject);

JNIEXPORT jint JNICALL Java_my_NdkJniTest_AddAB
   (JNIEnv *, jobject, jint, jint);

#ifdef __cplusplus
}
#endif
#endif

编写 .c文件,一定要引入 .h头文件。

之前我们实现了头文件的方法,新加入的方法还没有实现,所以我们要去实现新加入的方法。

所以.c文件所有内容如下:

#include "my_NdkJniTest.h"

JNIEXPORT jstring JNICALL Java_my_NdkJniTest_mytest
        (JNIEnv *env, jobject obj) {
    return (*env)->NewStringUTF(env, "This is my Jni test!!!");
}

//实现新的方法
JNIEXPORT jint JNICALL Java_my_NdkJniTest_AddAB
        (JNIEnv *env, jobject obj, jint a, jint b) {
    return (a+b);
}

sync project

开始使用新方法,并查看结果:

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        NdkJniTest ndkJniTest = new NdkJniTest();
        String s = ndkJniTest.mytest(); //native方法
        Log.i("TAG",s);
        
        //测试新方法
        int result = ndkJniTest.AddAB(2018,10);
        Log.i("TAG",""+result);
        

    }
}

到此为止了!!!

猜你喜欢

转载自blog.csdn.net/qq_38261174/article/details/83239718