概述
从今天开始,准备研究一下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体系
来源:CSDN
作者:lxn_李小牛
链接:https://blog.csdn.net/Small_Lee/article/details/51471002