前言:
Android开发中经常会使用到第三方的.so库,在使用.so库的时候就要用到JNI编程。JNI是Java Native Interface的缩写,它提供了若干的API实现了Java和其他语言的通信(主要是C&C++)。开发中一般都用第三方的库,很多时候也可能我们自己来编写C或者C++来实现JNI编程,这篇博客主要是介绍Android studio 使用NDK工具来开发JNI编程。
环境配置
JNI编程需要满足如下前提:
- 你必须有安装Android studio开发idea编辑器(本人使用的是android studio 1.3版本)。
- 下载最新版本的NDK工具,然后解压到一个目录即可(配置NDK环境变量时使用到该路径)。我的ndk版本为 android-ndk-r10e。
- 配置ndk环境变量,计算机–>属性–>高级系统设置–>环境变量–>系统变量–>新建 变量名:NDK_ROOT;变量值:F:\Android\SDK1\android2\android-ndk-r10e(此处是我的ndk路径,换成你自己的ndk路径即可)。
- 当然前提是你要配置了jdk环境变量,配置如同第三点:变量名:JAVA_HOME;变量值:F:\Android\SDK1\android2\Java\jdk1.7.0_67(此处是我的jdk路径,换成你自己的jdk路径即可)。除此之外,你还得新建一个CLASSPATH环境变量,变量名:CLASSPATH;变量值为 .;%HOME_PATH%\lib; 一开始的 . 和;和最后的;都是需要的。
- 你还需要在gradle.progerties文件下添加如下代码
android.useDeprecatedNdk=true
如果没有gradle.progerties文件,请直接在当前工程下新建该文件即可。
JNI编程
1.新建JniUtils类,用来调用jni方法。代码如下:
package com.example.xjp.myjnidmoe;
public class JniUtils {
//加载静态库
static {
System.loadLibrary("Test");//此处加载的是相应的模块库,名称必须和 ndk的moduleName名一样。
}
//定义本地方法
public native String getValue();
}
2.Build 编译当前工程,最后会在build目录下生成如下文件
该目录下生成了 JniUtils.class文件。然后打开AS的Terminal终端,跳到debug目录下:
然后执行 javah -jni *(此处为你的目标文件路径,即JniUtils路径)
最后会在debug目录下面生成 com_example_xjp_myjnidmoe_JniUtils.h 头文件。代码如下:
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_example_xjp_myjnidmoe_JniUtils */
#ifndef _Included_com_example_xjp_myjnidmoe_JniUtils
#define _Included_com_example_xjp_myjnidmoe_JniUtils
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: com_example_xjp_myjnidmoe_JniUtils
* Method: getValue
* Signature: ()Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_com_example_xjp_myjnidmoe_JniUtils_getValue
(JNIEnv *, jobject);
#ifdef __cplusplus
}
#endif
#endif
以上代码是自动生成的,无需开发者去修改。
3.在你的main目录下新建jni子目录,如下:
将刚才生成的 .h头文件复制到该jni目录下,然后新建一个 test.c文件,test.c文件代码如下:
//
// Created by 850302 on 2016/4/26.
//
#include "com_example_xjp_myjnidmoe_JniUtils.h"
JNIEXPORT jstring JNICALL Java_com_example_xjp_myjnidmoe_JniUtils_getValue
(JNIEnv *env, jobject obj) {
return (*env)->NewStringUTF(env, "This is my first JNI demo!");
}
由于jni编程对方法的名称有一定规则要求,开头需要Java_ 后面依次是jni方法的java类路径,以“_”分割。开发者为了不写错,可以直接将刚才的.h头文件中的方法 copy下来直接实现即可。
4.配置Gradle文件
首先你得配置当前工程的 build.gradle文件,其实配置很简单,直接在原来的build.gradle文件里添加 ndk的配置即可。
apply plugin: 'com.android.application'
android {
compileSdkVersion 23
buildToolsVersion "22.0.1"
defaultConfig {
applicationId "com.example.xjp.myjnidmoe"
minSdkVersion 21
targetSdkVersion 23
versionCode 1
versionName "1.0"
//这里是配置ndk
ndk{
//定义库的模块名称。该名称用于System.loadLibrary("Test")
moduleName "Test"
}
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
}
是不是很简单,仅仅是定义了 ndk 的moduleName的名称而已。
其次是为当前工程配置指定的ndk,配置方法如下:右键当前工程名,选择Open Module Settings,弹出如下窗口。
如图,1:sdk路径;2:jdk路径;3:ndk路径;点击ok之后。会在local.properties文件下配置指定的sdk和ndk路径:
#Tue Apr 26 18:36:18 CST 2016
ndk.dir=F\:\\Android\\SDK1\\android2\\android-ndk-r10e
sdk.dir=F\:\\Android\\SDK1\\android2\\sdk
如此,所有的准备工作都已经完成。现在可以在你的代码中使用Test库了,调用代码如下:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
TextView textView = (TextView) findViewById(R.id.text);
//start=====
JniUtils jniUtils = new JniUtils();
String content = jniUtils.getValue();
//end=====
textView.setText(content);
}
结果如下:
可能会有人问,代码生成的libTest.so库在哪里呢?其实生成的libTest.so库已经打包到apk中去了,那么假如我要把这个so库给别人使用怎么办?很简单,在你编译的过程其实已经生成了libTest.so,在哪里呢?目录如下:bulid/intermediates/ndk目录下,该目录是编译成功之后生成的。
看到没?生成了不同平台下的libTest.so库。
总结
该博客主要是记录Android studio 下使用NDK工具实现JNI编程和生成so库的整个过程,以便开发者了解JNI编程的一个完整的过程。