你们好!我又来了,此次准备着手写一个JNI开发系列,毕竟,如今JNI开发也是在各个公司愈来愈重要了,若是项目毕竟大,可能涉及的模块较多,好比你做为应用层的开发,不免避免不了须要使用一些库,一些加密操做等等,通常都会放在本地方法里面,比较安全,人家丢给你so文件或者静态a文件。。你不会用岂不是很尴尬。网上资料比较杂,并且很乱,大部分仍是在用.mk的方法。java
本系列就基于CMake形式,但愿可以带着一些但愿学习JNI开发的小伙伴一块儿学会JNI开发~比心❤android
#定义cmake支持的最小版本号
cmake_minimum_required(VERSION 3.4.1)
add_library( # 设置生成so库的文件名称,例如此处生成的so库文件名称应该为:libnative-lib.so
native-lib
# 设置生成的so库类型,类型只包含两种:
# STATIC:静态库,为目标文件的归档文件,在连接其余目标的时候使用
# SHARED:动态库,会被动态连接,在运行时被加载
SHARED
# 设置源文件的位置,能够是不少个源文件,都要添加进来,注意相对位置
src/main/cpp/native-lib.cpp )
# 从系统里查找依赖库,可添加多个
find_library( # 例如查找系统中的log库liblog.so
log-lib
# liblog.so库指定的名称即为log,如同上面指定生成的libnative-lib.so库名称为native-lib同样
log )
# 配置目标库的连接,即相互依赖关系
target_link_libraries( # 目标库(最终生成的库)
native-lib
# 依赖于log库,通常状况下,若是依赖的是系统中的库,须要加 ${} 进行引用,
# 若是是第三方库,能够直接引用库名,例如:
# 引用第三方库libthird.a,引用时直接写成third;注意,引用时,每一行只能引用一个库
${log-lib} )
复制代码
这里我把注释进行了缩减,标注了中文注释,比较详细,不明白的能够看下每一个的做用,固然还有不少API可使用,后续再详细说明,也能够看看官方文档,[戳我戳我] (developer.android.google.cn/ndk/guides/…)bash
咱们新建一个Helper类来编写native方法app
public class NativeHelper {
static {
System.loadLibrary("native-lib");
}
public native String stringFromJNI();
public native int add(int a,int b);
}
复制代码
打开咱们的MainActivityide
public class MainActivity extends AppCompatActivity {
// Used to load the 'native-lib' library on application startup.
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Example of a call to a native method
TextView tv = (TextView) findViewById(R.id.sample_text);
tv.setText(stringFromJNI());
}
}
复制代码
能够看到最上面,静态代码块引用了native-lib这个库,而后直接调用native本地方法,将C++中返回的字符串拿到进行显示。而后看看C++具体是怎么是实现的post
#include <jni.h>
#include <string>
extern "C" JNIEXPORT jstring
JNICALL
Java_com_example_hik_cmake_MainActivity_stringFromJNI(
JNIEnv *env,
jobject /* this */) {
std::string hello = "Hello from C++";
return env->NewStringUTF(hello.c_str());
}
复制代码
代码很简单,引用两个头文件,而后定义了一个方法,返回“Hello from C++”这个字符串,那有的朋友要问了,为何java层直接调用stringFromJNI()方法可以直接映射到C++里面的方法呢,细心的小伙伴可能发现了,C++里面的这个方法名很长并且很熟悉。。这不是Java包名加上方法名拼凑而成的字符串吗,这种方式呢叫作静态注册,这样就能经过这个映射方式找到C++中的方法。学习
有的朋友又要说了,这么长方法名也太麻烦了,虽然能够自动生成,可是多不美观,多不优雅。。是滴!有静态注册,那固然就有动态注册了~咱们来改一改代码:ui
#include <jni.h>
#include <string>
#include <android/log.h>
#define TAG "JNI_"
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG,TAG,__VA_ARGS__)
JNICALL
jstring backStringToJava(JNIEnv *env, jobject /* this */) {
std::string hello = "Hello from C++";
return env->NewStringUTF(hello.c_str());
}
//动态注册
jint registerMethod(JNIEnv *env) {
jclass clz = env->FindClass("com/example/taolin/jni_project/NativeHelper");
if (clz == NULL) {
LOGD("con't find class: com/example/taolin/jni_project/NativeHelper");
}
JNINativeMethod jniNativeMethod[] = {{"stringFromJNI", "()Ljava/lang/String;", (void *) backStringToJava}};
return env->RegisterNatives(clz,jniNativeMethod, sizeof(jniNativeMethod)/ sizeof(jniNativeMethod[0]));
}
jint JNI_OnLoad(JavaVM *vm, void *reserved){
JNIEnv * env = NULL;
if (vm->GetEnv((void**)&env,JNI_VERSION_1_6)!=JNI_OK){
return JNI_ERR;
}
jint result = registerMethod(env);
LOGD("RegisterNatives result: %d", result);
return JNI_VERSION_1_6;
}
复制代码
这里呢,为了在C++中打印日志,咱们须要引入log.h头文件,而后咱们把以前的方法名改为backStringToJava,而后由于没有了静态注册的规则,Java层调用的使用固然就找不到咱们对应的方法了,咱们定义一个动态注册的方法,将Java中的方法和C++中的方法进行动态的绑定:
有些朋友可能对方法签名不太明白,后续语法会详细说明,这里先简单说下,方法签名也就是一个方法惟一性的一个标准,上面的()Ljava/lang/String;就是stringFromJNI的签名,前面的括号里面是参数的签名,由于这里没有参数,因此为空,紧接着后面是返回值得签名,规则是,若是是基本数据类型就是相对应的基本数据类型,若是不是基本数据类型,那么就是L+对象包名+“;”,注意这里的分号不可省略!!根据这个规则,下面那个方法的签名就是(II)I,依次类推,没明白的也不要紧,后面会详细对JNI中的语法详细解释,先知道有这么回事就行了。
public native String stringFromJNI();
//签名:()Ljava/lang/String;
public native int add(int a int b)
//签名:"(II)I"
复制代码
而后咱们直接运行APP,能够发现页面上显示出来了“Hello from C++”字符串,而后看看咱们生成的so文件在哪:
OK!大功告成,咱们的第一步就完成了,成功的完成了Java调用C++的方法,但别高兴的太早,这只是第一步,好了,看到这的奖励本身跟辣条吧~,溜了溜了。。