转载请注明出处:(http://blog.csdn.net/qq_35071078/article/details/70338637)
最近闲来无事,想摸索下一下ndk,可是ndk不是块好啃的骨头,但作为一名程序员,什么都要了解下,对吧╮( ̄▽ ̄)╭。首先我想吐槽一下,网上有些博客写的很乱,一上来就贴一段代码,也不告诉是要干什么,代码一写完就完事,这让初学者很难理解jni到底是个什么东西。每次按照他们的方法敲,敲着敲着就不知道要干什么了,然后程序也运行不了。不过有些大神确实总结的很好。所以我想通过这几天的摸索,总结一下。
对于jni,ndk到底是什么,我就不多说了,百度讲的很清楚。对于如何创建jni,其实很简单,在.java文件中声明native方法 -> 编译.java文件得到.class文件 -> 用javah命令生成.h文件 -> 创建一个.c或者.cpp文件,include刚生成的.h文件,然后再实现里面的方法 -> 通过ndk-build(这个需要Android.mk文件和Application.mk文件)或者cmake(需要CMakeLists.txt)方式或者直接这篇文章讲的来生成.so文件,供java层调用。
首先创建一个项目,
接下来创建一个类 MyJni,这个类用来实现native方法:
接下来编译项目:
编译完成之后,就会在app/build/../com/..下生成相应的classes文件了,接下来就是用 javah 这个指令,将刚才编译好过了的MyJni.class编译成 .h 文件,首先打开终端,点击android studio下方的Terminal,这里默认路径是在当前的项目目录下。
在这里输入 cd app/build/intermediates/classes/debug,进入debug目录,然后在这里输入javah指令来生成.h文件。输入 javah com.chenxin.MyJni ,之后就会在当前目录(../debug)下生成一个com.chenxin.MyJni.h文件。为什么要在debug目录下输入这段指令呢?因为我在其他地方试了,不行啊⊙﹏⊙‖∣° 。javah后面就是要接完整的包名+类名.
.h文件里面的代码如下,其他的不用管,只需要知道JNIEXPORT jstring JNICALL Java_com_chenxin_MyJni_getString
(JNIEnv *, jclass);这个就是我们在MyJni里面声明的native方法。
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_chenxin_MyJni */
#ifndef _Included_com_chenxin_MyJni
#define _Included_com_chenxin_MyJni
#ifdef __cplusplus
extern "C" {
#endif
/* * Class: com_chenxin_MyJni * Method: getString * Signature: ()Ljava/lang/String; */
JNIEXPORT jstring JNICALL Java_com_chenxin_MyJni_getString
(JNIEnv *, jclass);
#ifdef __cplusplus
}
#endif
#endif
在app/src/main下创建一个jni文件夹(Directory),因为studio默认的c和c++文件是在这里面,将刚才的那个.h文件剪切到这里面来,然后在jni文件夹里创建一个c/c++ source file,随便叫什么,我这里就叫test,后缀名是都.c或者.cpp都可以,只不过函数的实现上稍微有点差异。在这个.c或者.cpp文件中,首先得导入jni.h,然后导入com_chenxin_MyJni.h,就是刚才用javah生成的.h文件。如果是.cpp的话:
#include "jni.h"
#include "com_chenxin_MyJni.h"
JNIEXPORT jstring JNICALL Java_com_chenxin_MyJni_getString
(JNIEnv *env, jclass jz){
return env->NewStringUTF("this is jni , this is my new life!");
}
如果是.c的话:
#include "jni.h"
#include "com_chenxin_MyJni.h"
JNIEXPORT jstring JNICALL Java_com_chenxin_MyJni_getString
(JNIEnv *env, jclass jz){
return (*env)->NewStringUTF(env,"this is jni , this is my new life!");
}
对了,还得在gradle.properties里面添加一行代码,不然会报错,因为这种方法谷歌现在已经不提倡了。
android.useDeprecatedNdk=true
接下来回到MyJni.java文件,添加代码来来加载so文件,此时还没有生成,等下编译项目时会自动生成,自动生成的.so的默认名字是app。
public class MyJni {
static {
System.loadLibrary("app");
}
public native static String getString();
}
不过如果想自定义生成的so的名字,可以在build.gradle中加入:
ndk{
moduleName "myLib"
}
我这里将生成的so的名字改成了myLib,所以将上面的 “app” 改成 “myLib” ,例如:
build.
android {
compileSdkVersion 25
buildToolsVersion "25.0.2"
defaultConfig {
......
ndk{
moduleName "myLib"
}
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
MyJni.java文件中:
static {
System.loadLibrary("myLib");
}
也可以设置指定生成什么类型的so文件,这里我就不详细讲解了。想加的话直接在modulueName下面添加:
abiFilters "armeabi", "armeabi-v7a"
然后回到MainActivity,使用Log打印以下,看下是否调用成功了:
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Log.e("info","调用jni的结果 :"+TestJni.sayHello());
}
}
编译运行
结果:
而且这时在打开app/build/intermediates下就可以看到对应的so文件了
也可以使用 Analyze 来看当前项目的结构:
ok,大功告成。