JNI基本使用

时光总嘲笑我的痴心妄想 提交于 2019-12-06 08:34:03

概述

从今天开始,准备研究一下JNI相关的开发,其实很早之前就想学习这方面的东西了,一个原因是平时开发中用的很少,就没研究,另外一个原因是因为其他方面的东西还是比较多的,所以没有时间来学习这部门内容。不过从今天起,为了向全面发展的道路上更迈进一步,开始学习JNI.

准备工作

开发JNI我们需要NDK这个东西?什么是NDK呢?简单给大家介绍一下,NDK是一系列工具的集合

  • NDK提供了一系列的工具,帮助开发者快速开发C(或C++)的动态库,并能自动将so和java应用一起打包成apk。这些工具对开发者的帮助是巨大的。
  • NDK集成了交叉编译器,并提供了相应的mk文件隔离CPU、平台、ABI等差异,开发人员只需要简单修改mk文件(指出“哪些文件需要编译”、“编译特性要求”等),就可以创建出so。
  • NDK可以自动地将so和Java应用一起打包,极大地减轻了开发人员的打包工作。

    首先我们去下载NDK,然后放在磁盘的某个路径下,这里我放在了

D:\develop\android-ndk-r10e

这是第一步,然后我们就可以在开发环境中进行配置了,很简单,只需要一句话,我们就可以开始JNI的编程了。具体做法如下,在当前项目的local.properties文件中添加你ndk的路径

# Location of the SDK. This is only used by Gradle.
# For customization when using a Version Control System, please read the
# header note.
sdk.dir=D\:\\develop\\studioSDK
ndk.dir=D\:\\develop\\android-ndk-r10e

或者手动选择NDK目录也可以
这里写图片描述

这时候编译工具会自动在local.properties文件中添加ndk路径

编码

准备工作做完了,我们开始写代码。
在当前项目中新建一个类,如下所示:

public class NdkUtil {
        public native String getString();
}

然后我们在MainActivity中进行方法的调用

public class MainActivity extends AppCompatActivity {

    private TextView textView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        textView = (TextView) findViewById(R.id.textview);
        NdkUtil ndkUtil = new NdkUtil();
        textView.setText(ndkUtil.getString());
    }
}

然后我们选择build-Make Project,然后会产生一个对应的class文件,目录F:\Studio\MyApplication2\MyApplication2\app\build\intermediates\classes\debug\com\example\hecun\myapplication\NdkUtil.class

这里写图片描述

然后接下来的步骤就是根据生成的class文件,利用javah生成对应的 .h头文件。
点开AS的Terminal标签,默认进入到该项目的项目名称文件夹下

然后我们输入如下命令,进入到class文件生成的中间路径

cd app\build\intermediates\classes\debug

然后执行下面的javah命令生成.h文件

javah -jni com.example.hecun.myapplication.NdkUtil

这里写图片描述

这里写图片描述

我们可以看到.h文件已经生成了。其内容为

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

#ifndef _Included_com_example_hecun_myapplication_NdkUtil
#define _Included_com_example_hecun_myapplication_NdkUtil
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     com_example_hecun_myapplication_NdkUtil
 * Method:    getString
 * Signature: ()Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_com_example_hecun_myapplication_NdkUtil_getString
  (JNIEnv *, jobject);

#ifdef __cplusplus
}
#endif
#endif

然后我们再main文件夹下新建一个jni目录,把刚才生成的.h文件剪切过来,接着新建一个.c文件,比如jnitest.c,名字可以随意取,编辑代码如下

#include "com_example_hecun_myapplication_NdkUtil.h"
JNIEXPORT jstring JNICALL Java_com_example_hecun_myapplication_NdkUtil_getString
(JNIEnv *env,jobject obj){
    return (*env)->NewStringUTF(env,"This is a Test for JNI");
}

有的同学可能对上面的步骤有点疑问,为什么不直接把.h文件生成在jni目录下,还要复制过来,这不是多次一举吗,实际情况是这样的,因为我们的NdkUtil.class文件实在debug中的,所以我们只能进入到这个目录中去生成.h文件,如果不进入这个目录,去利用javah命令生成头文件,会出现找不到类的错误

这里写图片描述

最后我们再module的build.gradle文件中加入如下配置信息

 defaultConfig {
     .....
        ndk{
            moduleName "MyJniSo"         //生成的so名字
            abiFilters "armeabi", "armeabi-v7a", "x86"  //输出指定三种abi体系结构下的so库。目前可有可无。
        }
    }

现在生成的so库的名字有了,然后我们去NdkUtil里面去加载这个库

public class NdkUtil {
    public native String getString();
    static {
        System.loadLibrary("MyJniSo");
    }
}

使用gradle的好处是,自动编译生成apk文件,并且把相关的.so文件打包到apk安装包中,一劳永逸

还有一种以前传统的手动编译方式,这里顺便也介绍一下
首先我们要配置环境变量,因为我们要使用ndk命令来进行编译,
接下来,创建两个文件,Android.mk和Application.mk

Androidmk
ndroid.mk文件用来指定源码编译的配置信息,例如工作目录,编译模块的名称,参与编译的文件等,大致内容如下:
这里写图片描述

Application.mk

这里写图片描述

我们导出apk,然后解压,看一下是否有so文件
这里写图片描述

可以看到,so文件已经打包到apk的lib中

好了,到此AS下NDK JNI开发的代码编写和设置就OK了,接下来就是编译工程运行就可以了。

效果图

这里写图片描述

最后总结一下JNI开发流程:

1.在类中定义Native方法
2. 利用javah命令将类所的class文件生成为.h头文件
3. 在main文件夹下新建一个jni目录,将生成的.h文件剪切过来,同时创建一个.c文件,目的就是实现JNI方法
4. 在gradle文件中配置so库的名字以及指定的abi体系

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!