很久没用玩jni ndk编译so库了,为了防止手生就用最近需要实现的一个高斯模糊算法类练手。当然这个算法不是我实现的,我只是把这个算法C代码编译成了一个so库,因为C执行效率比java更高,作为这个吃CPU的算法很适合做成SO文件。
这里不再介绍JNI和NDK了,我要说的重点是一个SO库从编译到使用的这个过程。
上面图片可以清除看到这个SO库的生成和java层代码通过KJNI环境调用C/C++代码的过程。
我是用android studio编译的,因为SO都是一个库并且需要对应的java层加载库文件和申明方法接口所以我们可以直接在工程里面新建一个依赖库模块,下面开始讲解完成步骤:
(1)新建一个依赖库根据需要填写包名
(2)配置编译环境
- 在依赖库项目中找到local.properties文件添加ndk工具位置。
ndk.dir=/home/zhuxingchong/Android/Sdk/ndk-bundle
ndk位置file->project setting可以在下面找到
- 为了兼容低版本ndk在依赖库gradle.properties文件中添加
android.useDepreCateNdk=true
依赖库build.gradle文件中添加ndk编译模块和so文件编译mk文件
apply plugin: 'com.android.library'
android {
compileSdkVersion 28
defaultConfig {
minSdkVersion 23
targetSdkVersion 28
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
ndk{
moduleName "jni_blur"//生成so文件名
}
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
externalNativeBuild{
ndkBuild{
path "src/main/java/jni/Android.mk"//mk文件路径
}
}
}
(3)java文件中申明函数
(4)生成.h头文件
终端进入mylibrary/src/main/java目录下执行javah -d jni com.data.mylibrary.StackNative命令生成.h文件
命令执行完成后会在java目录下生成jni目录并且在目录下生成
具体.h文件代码如下
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_data_mylibrary_StackNative */
#ifndef _Included_com_data_mylibrary_StackNative
#define _Included_com_data_mylibrary_StackNative
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: com_data_mylibrary_StackNative
* Method: blurPixels
* Signature: ([IIII)V
*/
JNIEXPORT void JNICALL Java_com_data_mylibrary_StackNative_blurPixels
(JNIEnv *, jclass, jintArray, jint, jint, jint);
/*
* Class: com_data_mylibrary_StackNative
* Method: blurBitmap
* Signature: (Ljava/lang/Object;I)V
*/
JNIEXPORT void JNICALL Java_com_data_mylibrary_StackNative_blurBitmap
(JNIEnv *, jclass, jobject, jint);
#ifdef __cplusplus
}
#endif
#endif
(5)编写具体逻辑.c或cpp文件,文件命名可以和.h文件一样后缀改为.c或cpp即可,因为是对图片做高斯模糊处理代码比较多。
#include <android/log.h>
#include <android/bitmap.h>
#include "stackblur.h"
#define TAG "com_data_mylibrary_StackNative"
#define LOG_D(...) __android_log_print(ANDROID_LOG_DEBUG,TAG ,__VA_ARGS__)
JNIEXPORT void JNICALL
Java_com_data_mylibrary_StackNative_blurPixels
(JNIEnv
*env,
jclass obj, jintArray
arrIn,
jint w, jint
h,
jint r
) {
jint *pixels;
// cpp:
// pix = (env)->GetIntArrayElements(arrIn, 0);
pixels = (*env)->GetIntArrayElements(env, arrIn, 0);
if (pixels == NULL) {
LOG_D("Input pixels isn't null.");
return;
}
// Start
pixels = blur_ARGB_8888(pixels, w, h, r);
// End
(*env)->ReleaseIntArrayElements(env, arrIn, pixels, 0);
}
JNIEXPORT void JNICALL
Java_com_data_mylibrary_StackNative_blurBitmap
(JNIEnv
*env,
jclass obj, jobject
bitmapIn,
jint r
) {
AndroidBitmapInfo infoIn;
void *pixels;
// Get image info
if (AndroidBitmap_getInfo(env, bitmapIn, &infoIn) != ANDROID_BITMAP_RESULT_SUCCESS) {
LOG_D("AndroidBitmap_getInfo failed!");
return;
}
// Check image
if (infoIn.format != ANDROID_BITMAP_FORMAT_RGBA_8888 &&
infoIn.format != ANDROID_BITMAP_FORMAT_RGB_565) {
LOG_D("Only support ANDROID_BITMAP_FORMAT_RGBA_8888 and ANDROID_BITMAP_FORMAT_RGB_565");
return;
}
// Lock all images
if (AndroidBitmap_lockPixels(env, bitmapIn, &pixels) != ANDROID_BITMAP_RESULT_SUCCESS) {
LOG_D("AndroidBitmap_lockPixels failed!");
return;
}
// height width
int h = infoIn.height;
int w = infoIn.width;
// Start
if (infoIn.format == ANDROID_BITMAP_FORMAT_RGBA_8888) {
pixels = blur_ARGB_8888((int *) pixels, w, h, r);
} else if (infoIn.format == ANDROID_BITMAP_FORMAT_RGB_565) {
pixels = blur_RGB_565((short *) pixels, w, h, r);
}
// End
// Unlocks everything
AndroidBitmap_unlockPixels(env, bitmapIn);
}
(6)编写mk文件
Android.mk
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := jni_blur //生成so文件名
LOCAL_SRC_FILES := stackblur.c com_data_mylibrary_StackNative.c load.c //所有需要编译的c/cpp文件
LOCAL_LDLIBS := -lm -llog -ljnigraphics
include $(BUILD_SHARED_LIBRARY)
Aplication.mk
APP_ABI := all
APP_PLATFORM:= android-23
APP_OPTIM := release
(7)编译so文件和aar包文件
点击android studio右侧gradle->mylibrary->build->build
生成相应库文件位置
(8)将响应so文件和aar文件拷贝到你的项目libs项目
(9)最后在build.gradle里面配置相关依赖即可使用了
注意:
生成头文件的时候执行javah -d jni com.data.mylibrary.StackNative出错
错误: 无法确定Bitmap的签名
public static native void blurBitmap(Bitmap bitmap, int r);改为
public static native void blurBitmap(Object bitmap, int r);
最后源码:Android-Jni_demo