前言
大家好!我又来了,这次准备着手写一个JNI开发系列,毕竟,现在JNI开发也是在各个公司越来越重要了,如果项目毕竟大,可能涉及的模块较多,比如你作为应用层的开发,难免避免不了需要使用一些库,一些加密操作等等,一般都会放在本地方法里面,比较安全,人家丢给你so文件或者静态a文件。。你不会用岂不是很尴尬。网上资料比较杂,而且很乱,大部分还是在用.mk的方法,本系列就基于CMake形式,希望能够带着一些希望学习JNI开发的小伙伴一起学会JNI开发~
零基础带你吃掉JNI全家桶(二)
零基础带你吃掉JNI全家桶(三)
从一个栗子说起
注意:要支持CMake,此时我们需要勾选 Include C++ support,然后点击Next--->Finish,完成工程的创建。
创建完成后,我们打开工程目录,发现增加了几个不一样的地方:
发现AS已经帮我们生产一个cpp目录以及一个native-lib.cpp的c++文件,在根目录下,也多了一个CMakeLists.txt文件,我们主要来关注 CMakeLists.txt里面的东东
#定义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可以使用,后续再详细说明,也可以看看官方文档,[戳我戳我]
(https://developer.android.google.cn/ndk/guides/cmake)
我们新建一个Helper类来编写native方法
public class NativeHelper {
static {
System.loadLibrary("native-lib");
}
public native String stringFromJNI();
public native int add(int a,int b);
}
打开我们的MainActivity
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++具体是怎么是实现的
#include
#include
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++中的方法。
有的朋友又要说了,这么长方法名也太麻烦了,虽然可以自动生成,但是多不美观,多不优雅。。是滴!有静态注册,那当然就有动态注册了~我们来改一改代码:
#include
#include
#include
#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++中的方法进行动态的绑定:
- 通过env指针,拿到MainActivity的class对象,具体env指针后续会详细说明
- 定义一系列的方法对象,每个包含三个参数,第一个是java中的方法名,第二个是方法对应的签名,第三个是C++中的方法名
- 在JNI_OnLoad方法中,调用动态注册绑定方法进行绑定
有些朋友可能对方法签名不太明白,后续语法会详细说明,这里先简单说下,方法签名也就是一个方法唯一性的一个标准,上面的()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++的方法,但别高兴的太早,这只是第一步,好了,看到这的奖励自己跟辣条吧~,溜了溜了。。