JNI基本使用

来源:互联网 发布:心内事无人知 蔡秋凤 编辑:程序博客网 时间:2024/05/21 18:29

概述

从今天开始,准备研究一下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\\studioSDKndk.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 __cplusplusextern "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体系

0 0