JNI使用步骤
先看一下代码示例
public class MainActivity extends AppCompatActivity {
//使用静态块加载.so库文件
static {
System.loadLibrary("hello");
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
RelativeLayout relativeLayout = new RelativeLayout(this);
final TextView textView = new TextView(this);
Button button = new Button(this);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//在此调用native方法
textView.setText(stringFromC());
}
});
RelativeLayout.LayoutParams lp = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.WRAP_CONTENT, RelativeLayout.LayoutParams.WRAP_CONTENT);
lp.addRule(RelativeLayout.ALIGN_PARENT_RIGHT, RelativeLayout.TRUE);
lp.addRule(RelativeLayout.ALIGN_PARENT_TOP, RelativeLayout.TRUE);
relativeLayout.addView(textView);
relativeLayout.addView(button,lp);
setContentView(relativeLayout);
}
//第一步,声明本地方法
public native String stringFromC();
}
来看一下使用的基本步骤,包括但不限于如下:
1、声明native修饰的本地方法
//这里无需实现,此方法在点击后返回一个【字符串数据】
public native String stringFromC();
2、添加C代码的实现,头文件和方法体。
2.1使用javah命令生成C语言要用的头文件(*.h)。
在此目录(java下,包名开始的位置)下使用javah命令,位于%JAVA_HOME%/bin目录下,可能需要配置环境变量后才能使用。这一步的目的是生成“Java_全包名_方法名“格式的方法。
H:_MY_ANDROID\AndroidProjects\1010_jni2\app\src\main\java>
javah -jni com.vincyan.a1010_jni2.MainActivity
2.2右键app-new-folder-JNI folder,在此文件夹编写 hello.c
// Created by vincyan on 2016/10/10.
#include<jni.h>
//引入自定义的库文件,使用双引号;
#include "com_vincyan_a1010_jni2_MainActivity.h"
/**为方法添加实现,此方法为我们返回一个字符串
此处为jni的规则。任意的jni方法,此处都必须有此两个参数,env表示结构体的二级指针,thiz表示调用native方法的Java对象
对于更多参数的方法,在后面添加,如jint,jboolean等。
JNIEXPORT和JNICALL为自动添加的关键字,忽略之。
*/
JNIEXPORT jstring JNICALL Java_com_vincyan_a1010_1jni2_MainActivity_stringFromC
(JNIEnv * env, jobject thiz){
//char* cstr表示C语言中的字符串,传入下面的方法
char* cstr = "hello in c";
return (*env)->NewStringUTF(env,cstr);
}
jni.h文件的路径
H:\android\android-ndk-r10\platforms\android-12\arch-arm\usr\include
//jni.h中的部分代码
typedef const struct JNINativeInterface* JNIEnv;
/**
JNIEnv是结构体struct JNINativeInterface的一级指针,那么JNIEnv* env就是该结构体的二级指针,(*env)就表示其一级指针,可以使用间接引用运算符,故可通过(*env)->NewStringUTF()来调用结构体的函数指针,从而调用其指向的函数。
结构体中定义的大量的函数指针对应着大量的函数实现,在jni开发中会用到这些定义的函数,故将JNIEnv* env作为形参传递进来。
*/
#if defined(__cplusplus)
typedef _JNIEnv JNIEnv;
typedef _JavaVM JavaVM;
#else
//C中的自定义类型
typedef const struct JNINativeInterface* JNIEnv;
typedef const struct JNIInvokeInterface* JavaVM;
#endif
/*
* Table of interface function pointers.
*/
struct JNINativeInterface {
//定义的大量函数指针...
jstring (*NewStringUTF)(JNIEnv*, const char*);
...
}
//*NewStringUTF的实现
jstring NewStringUTF(const char* bytes)
{ return functions->NewStringUTF(this, bytes); }
...
3、引入Android.mk和Application.mk文件
//Android.mk,linux下make file,此文件保存基本配置信息
LOCAL_PATH := $(call my-dir)
//清除上次配置的信息,变量
include $(CLEAR_VARS)
//指定了c源文件编译后生成动态链接库的名字
LOCAL_MODULE := hello
//指定了c的源文件的名字,多个时使用空格隔开
LOCAL_SRC_FILES := hello.c
//指定生成动态链接库文件
include $(BUILD_SHARED_LIBRARY)
/**
Application.mk
指定支持的平台,会生成对应平台的库文件,all表示所有平台,多个使用空格隔开,mips armeabi(默认情况下为此,市面上80-90%的手机) x86(genemotion模拟器)
*/
APP_ABI := all
4、使用ndk-build命令生成.so文件。
4.1 进入工程下的jni路径,使用ndk-build命令,编译生成文件。
H:_MY_ANDROID\AndroidProjects\1010_jni2\app\src\main\java>
cd /d H:_MY_ANDROID\AndroidProjects\1010_jni2\app\src\main\jni
H:\_MY_ANDROID\AndroidProjects\1010_jni2\app\src\main\jni>ndk-build
[armeabi-v7a] Compile thumb : hello <= hello.c
[armeabi-v7a] SharedLibrary : libhello.so
[armeabi-v7a] Install : libhello.so => libs/armeabi-v7a/libhello.so
[armeabi] Compile thumb : hello <= hello.c
[armeabi] SharedLibrary : libhello.so
[armeabi] Install : libhello.so => libs/armeabi/libhello.so
[x86] Compile : hello <= hello.c
[x86] SharedLibrary : libhello.so
[x86] Install : libhello.so => libs/x86/libhello.so
[mips] Compile : hello <= hello.c
[mips] SharedLibrary : libhello.so
[mips] Install : libhello.so => libs/mips/libhello.so
4.2导入生成的so文件,将生成的.so文件,导入该该项目下的libs路径,H:_MY_ANDROID\AndroidProjects\1010_jni2\app\libs
5、配置加载的环境
5.1在build.gradle文件中配置jni
sourceSets{
main{
jniLibs.srcDirs "libs"
//防止干扰(StackOverflow)
jni.srcDirs = []
}
}
5.2使用静态代码块加载库文件
//hello文件为生成的libhello.so,lib与.so为编译器自动添加
static {
System.loadLibrary("hello");
}
5.3在Java代码中调用即可
【WARNING】
<0>配置环境变量,包括jdk/jdk-bin/ndk
最终项目结构
编译过程会生成obj文件夹,事实上jni和obj文件夹只是在编译过程中有用,不会打包到apk中。
若以上有问题,可能还需要添加。
<1> 在app下的build.gradle 中配置 defaultConfig{ndk{moduleName ‘hello’}}
<2>在gradle.properties 文件中配置允许使用过时的ndk:android.useDeprecatedNdk=true