我们平时使用的动态库都是由C/C++开发最后生成的.so文件。
可以先看看一个JNI的开发过程。
一. 开发JNI
有两种方式,现在一种比较快的方式是AndroidStudio你在创建项目选择Module的时候它会给你个JNI的模板,直接使用那个就行。
但是我还是比较喜欢传统的方法。
简单来说传统的方式就是你用命令来把java文件变成C++的头文件
简单演示一遍,先写个java类
public class TestJni { static { System.loadLibrary("KylimTest"); } public static native String getMsg(); }
定义了一个native修饰的方法,在代码调用这个方法之后JNI就会自动调用到动态库中相应的方法。
将这个类用命令生成头文件,来到文件夹路径下输入命令
javah -jni 包名.类名
可以看到默认会生成一个.h的头文件,自动命名为 包名_类名.h
/* DO NOT EDIT THIS FILE - it is machine generated */ #include <jni.h> /* Header for class com_kylim_nativetest_TestJni */ #ifndef _Included_com_kylim_nativetest_TestJni #define _Included_com_kylim_nativetest_TestJni #ifdef __cplusplus extern "C" { #endif /* * Class: com_kylim_nativetest_TestJni * Method: getMsg * Signature: (I)Ljava/lang/String; */ JNIEXPORT jstring JNICALL Java_com_kylim_nativetest_TestJni_getMsg (JNIEnv *, jclass); #ifdef __cplusplus } #endif #endif
主要的核心就是这句
JNIEXPORT jstring JNICALL Java_com_kylim_nativetest_TestJni_getMsg (JNIEnv *, jclass);
其它的我也不清楚,都是C相关的, 如果你嫌用命令生成麻烦,你可以自己创建一个.h文件然后方法命名就按照这样的规范去写
头文件只是为了定义,我们需要自己写原文件,所以要创建一个.cpp结尾的文件
#include "com_kylim_nativetest_TestJni.h" JNIEXPORT jstring JNICALL Java_com_kylim_nativetest_TestJni_getMsg (JNIEnv *env, jclass cls){ jstring result = env->NewStringUTF("结果是"); return result; }
方法命名是有规范的,看Demo也知道怎么规范了,没必要多解释,这样两端的代码就写完了,但是仅仅这样是无法运行项目的。
还需要些一些配置,因为在AndroidStudio中是Gradle去帮我们编译C++的代码,所以需要写这些配置。如果你不是用AS开发,你用其它工具开发直接生成.so文件再丢进AS中的话,可以忽略这一步。
先看看我的Jni目录
要创建一个Android.mk
#固定写法 LOCAL_PATH:=$(call my-dir) #固定写法 include $(CLEAR_VARS) #生成so名称 LOCAL_MODULE := KylimTest LOCAL_SRC_FILES := testone.cpp #固定写法 include $(BUILD_SHARED_LIBRARY)
具体的配置可自行去查找,这里不是主要讲JNI的,所以就不讲这么细。
还需要一个Application.mk
# 选择不同的 ABI,多个使用空格作为分隔符,全部是all # APP_ABI := armeabi armeabi-v7a APP_ABI := armeabi-v7a # 指定要使用的运行时 APP_STL := c++_static
当然这样还不行,都说了是Gradle进行编译,那么肯定还要在Gradle中写一些配置
android { defaultConfig { ndkBuild { //指定 Application.mk 的路径 arguments "NDK_APPLICATION_MK:=src/main/jni/Application.mk" //指定生成哪些平台的 so 文件 abiFilters "armeabi-v7a" //cFlags 和 cppFlags 是用来设置环境变量的, 一般不需要动 cFlags "-DTEST_C_FLAG1", "-DTEST_C_FLAG2" cppFlags "-DTEST_CPP_FLAG2", "-DTEST_CPP_FLAG2" } } sourceSets { main { jni.srcDirs = ['src/main/jni'] } } externalNativeBuild { ndkBuild { path file('src/main/jni/Android.mk') } } }