C语言
是一个巨大的宝库,Android是一个以Linux为基础的开源操做系统,系统底层不少的实现都是基于C语言
开发,好比图像处理,加密等。另外一方面C语言
的运行效率也比Java开发要高不少,所以为了高效率的运行有时候也会使用C语言
开发一些功能。再Android上面使用C语言
开发就须要使用NDK,在使用NDK开发的过程当中会使用大量的库,系统自带的库,第三方库以及本身写的库等。
使用Android Studio
调用NDK的库是很是简便,NDK内置了一些库方便开发者使用好比:Log库
,还有一些比较经常使用的第三方库好比:OpenSSL
。下面会分别介绍下,这两种库的使用。javascript
Log
是在Android开发过程用来调试程序必备的工具之一,他会把日志信息输入到Logcat
中,如何在NDK中使用android.util.Log
方便在Logcat
中查看JNI程序的运行状况呢?
这就须要在NDK开发中导入Android系统的Log
库。首先须要在gradle
中引入Log库,引入的方式使用是在ldLibs
中添加:java
model{
....
android {
compileSdkVersion 23
buildToolsVersion "23.0.2"
ndk {
moduleName "experiment"
ldLibs.addAll([ 'log']);
}
}
}复制代码
直接gradle中的ldLIbs
中加入log就能够了,若是还须要引入其余的系统库,只要在数组中直接增长便可。
下面来测试下log库
的使用,先定义一个native
的方法:android
public static native void callLogFromJni();git
在JNI中调用Log库:github
//引入 log
#include <android/log.h>
JNIEXPORT void JNICALL
Java_com_jjz_NativeUtil_callLogFromJni(JNIEnv *env, jclass type) {
__android_log_print(ANDROID_LOG_INFO,"jni-log","from jni log");
}复制代码
第一个参数,ANDROID_LOG_INFO
是Log的级别他包含:数组
typedef enum android_LogPriority {
ANDROID_LOG_UNKNOWN = 0,
ANDROID_LOG_DEFAULT, /* only for SetMinPriority() */
ANDROID_LOG_VERBOSE,
ANDROID_LOG_DEBUG,
ANDROID_LOG_INFO,
ANDROID_LOG_WARN,
ANDROID_LOG_ERROR,
ANDROID_LOG_FATAL,
ANDROID_LOG_SILENT, /* only for SetMinPriority(); must be last */
} android_LogPriority;复制代码
通常咱们经常使用的是架构
ADNROID_LOG_VERBOSE
->Log.vANDROID_LOG_DEBUG
->Log.dANDROID_LOG_INFO
-> Log.iANDROID_LOG_WARN
->Log.wANDROID_LOG_ERROR
->Log.e第二个参数是tag,用来方便的对Log分类。
第三个参数是message,对应Log的具体信息。dom
通常会采用宏定义的方式,定义Log的输出的方法,方便调用,例如:工具
#define LOG_TAG "jni-log"
#define LOGW(...) __android_log_print(ANDROID_LOG_WARN,LOG_TAG,__VA_ARGS__)复制代码
这里定义了一个warning log
的宏,在代码里面能够直接调用:post
LOGW("log from define");复制代码
系统库的调用比较简单方便,使用第三方库就比较麻烦些,第三方库须要使用NDK从新编译才能在JNI中调用。
OpenSSL
是最经常使用的加密库,下面就以OpenSSL
为例,介绍下在gradle-experimental
中如何引入第三方类库。
关于如何编译Android
下的OpenSSL
详见:Andorid Studio NDK 开发 - 编译 OpenSSL 类库。
首先定义对于库的repositories
,用来指定库的基本信息,包括库文件的路径,头文件的路径以及连接的方式等,详见以下代码:
model {
repositories{
libs(PrebuiltLibraries) {
// Configure one pre-built lib: static
openssl {
// 头文件地址
headers.srcDir "/usr/local/ssl/android-23/include"
// 静态连接库的引用,
binaries.withType(StaticLibraryBinary) {
staticLibraryFile = file("libs/libcrypto.a")
}
//动态连接库的引用
// binaries.withType(SharedLibraryBinary) {
// sharedLibraryFile = file("libs/libcrypto.so")
// }
}
}
}
}复制代码
c语言的类库分为静态连接库(.a)
和动态连接库(.so)
,静态类库和动态类库在引入方式上是不同的,分为对应:
StaticLibraryBinary
->静态库SharedLibraryBinary
-> 动态连接库这里引入的库为静态连接库,库的repositories名称为:openssl
.
定义好了一个repositories
,如今就须要调用了,在gradle
能够指定库的依赖:
model{
......
android{
.....
sources {
main {
jni {
dependencies{
//静态连接库
library 'openssl' linkage 'static'
//动态连接库
// library 'openssl' linkage 'shared'
}
source {
srcDir "src/main/jni"
}
}
jniLibs{
source{
srcDir "libs/"
}
}
....
}
}
}
}复制代码
在model.android.sources.main
中指定库的依赖,依赖的是上面定义的openssl
,linkage
类型为static,若是是动态连接库则linkage
就是shared。
由于在编译OpenSSL
设置了只支持arm
结构的cpu,因此还须要指定abi
为对应为arm架构,在model.android
添加配置:
ndk {
moduleName "experiment"
abiFilters.addAll(['armeabi', 'armeabi-v7a'])
}复制代码
定义了库的连接,就能够在代码中测试下OpenSSL
的使用了。
首先定义一个native
方法,该方法的目的是从OpenSSL
中读取随机数:
public static native byte[] getRandom();复制代码
对应的JNI
方法:
//引入OpenSSL的rand
#include <openssl/rand.h>
JNIEXPORT jbyteArray JNICALL
Java_com_jjz_NativeUtil_getRandom(JNIEnv *env, jclass type) {
unsigned char rand_str[128];
//使用OpenSSL的方法
RAND_seed(rand_str, 32);
jbyteArray bytes = (*env)->NewByteArray(env, 128);
(*env)->SetByteArrayRegion(env, bytes, 0, 128, rand_str);
return bytes;
}复制代码
RAND_seed
是OpenSSL的方法,可以读取随机数。这段代码的意思就是读取一个128位的随机数
,而后转换为Java的byte数组
。
在界面上面使用实现出读取的随机数内容:
tv2.setText(Base64.encodeToString(NativeUtil.getRandom(), Base64.DEFAULT));复制代码
运行以后能够在界面看到一段随机的字符串显示:
gradle-experiment
不管是调用系统库仍是第三方库都比较简单。