一、环境准备
1.JDK
2.NDK
3.android studio
二、准备测试的项目
1.新建app项目
2.编写一个本地方法类,如下参考
/**
* Created by jhone on 2015/11/23.
*/
public class JNIUtils {
private static JNIUtils jniUtils;
public synchronized static JNIUtils getInstants(){
if (jniUtils==null){
jniUtils=new JNIUtils();
}
return jniUtils;
}
//从C中获取信息的方法
public native String getFormC();
//传递信息到C中的方法
public native void sendToC(int a);
}
3.在界面中显示效果,这里只测试了一个方法
public class JNIActivity extends AppCompatActivity {
//加载libhello-jni.so库
static {
System.loadLibrary("hello-jni");
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_jni);
}
//界面buttion的点击事件
public void click(View view){
//调用JNIUtils中的本地方法声明,而该方法在.so库中具体实现
String message=JNIUtils.getInstants().getFormC();
Toast.makeText(this,message,Toast.LENGTH_LONG).show();
}
}
上面的代码实现了java部分的代码,那么里面所涉及的本地方法在哪实现,怎么实现呢?那么NDK就派上用场了
声明的native本地方法在C中实现,NDK把我们写的C代码编译成.so库,也就是像我们在上面activity中加载的libjini-hello.so~
那么问题清楚了,只要我们将相应的.so文件放到我们项目的对应目录下,那么程序跑起来就OK了
三、准备.so库,实现本地方法
1.打开cmd终端,使用javac命令编译我们刚刚写的JNIUtils.java成JNIUtils.class文件(目的就是拿到.calss文件,所以其他方式也行)
2.使用javah命令将JNIUtils.class转换成.h文件,这里注意了,由于我们JNIUtils.java是有包名的
所以 javah JNIUtils这样是不行的
应该根据包名逐层新建文件夹,将JNIUtils.class放到里面,在使用javah 包名.JNIUtils.class,生成”com_example_jhone_j4java_base_JNIUtils.h”
3.在项目的src/main目录下新建jni文件夹,将上面生成的.h文件放在里面:
Android.mk是JNI相关的描述文件,不可少
Application.mk是配置NDK平台版本的,保持默认就行
这两个文件可以从NDK目录的simple目录中的demo中获取
然后新建一个C文件hello-jni.c,hello-jni.h是自动生成的,你懂得
然后修改Android.mk描述文件:
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := hello-jni //.so文件名,自动加上lib
LOCAL_SRC_FILES := hello-jni.c //要编译的文件,多个文件用空格隔开
include $(BUILD_SHARED_LIBRARY)
到这里jni所需要的文件都准备OK了,下面就是实现本地方法了
4.打开com.example_…_JNIUtils.h
#endif
/*
* Class: com_example_jhone_j4java_base_JNIUtils
* Method: getFormC
* Signature: ()Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_com_example_jhone_j4java_base_JNIUtils_getFormC
(JNIEnv *, jobject);
/*
* Class: com_example_jhone_j4java_base_JNIUtils
* Method: sendToC
* Signature: (I)V
*/
JNIEXPORT void JNICALL Java_com_example_jhone_j4java_base_JNIUtils_sendToC
(JNIEnv *, jobject, jint);
#ifdef __cplusplus
}
#endif
#endif
观察可知是两个方法签名,没错就是本地方法映射到C中的方法签名,在hello-jni.c中实现这两个方法,就实现了java中那两个本地方法
5.打开hello-jni.c,开始编写C代码:
//
// Created by jhone on 2015/11/23.
//
#include<stdio.h>
#include<jni.h>
#include "com_example_jhone_j4java_base_JNIUtils.h"//同目录下这样导包
//使用上面映射过来的方法签名
JNIEXPORT jstring JNICALL Java_com_example_jhone_j4java_base_JNIUtils_getFormC(JNIEnv* env,jobject obj){
return (*env)->NewStringUTF(env,"Hello JNI");//jni.h中的声明的方法函数
}
/*
* Class: com_example_jhone_j4java_base_JNIUtils
* Method: sendToC
* Signature: (I)V
*/
JNIEXPORT void JNICALL Java_com_example_jhone_j4java_base_JNIUtils_sendToC(JNIEnv* env,jobject obj, jint a){
}
6.使用NDK项目编译jni这个目录,生成.so库
1>打开NDK目录结构如下:
可以看到ndk-build和ndk-build.cmd这个命令,不要疑惑
ndk-build是给linux用的,ndk-build.cmd是给window用的,大神们都说要安装sgwin,在sgwin中使用命令来编译.so,都说ndk-build.cmd会有什么什么问题,可能吧,但是我用这个挺爽,而且NDK都更新到10了,应该扶持window也不错了,等等,隔壁有动静~~
没错,就是用这个ndk-build.cmd命令来生成.so文件
2>将这个命令添加到环境变量,别问我怎么添加,我会告诉你和JDK是一样的
3>使用cmd命令进入到我们项目中的jni目录中,然后键入ndk-build,稍等片刻,看看项目目录有什么变化,对了,已经成功了
如图,生成了libs和obj两个目录,其中libs目录中就是我们要的.so文件,而obj并没有什么卵用,有大神就笑了,特么之前给我们看的图中也有这两个目录啊,呃呃,这下你可是给我造成了10000+伤害
对,jni就是这么简单,大神一键run,run完之后眼泪都快掉下来了,天呐,闪退了~~
其实还有个知识点没告诉你,在eclipse中,.so是放在libs目录中,而在android studio中,你要把这个libs目录改成jniLibs,注意,是这个libs而不是那个libs,别改错了,你懂的,然后就OK了,不信?不信你run一下就信了