2018-08-25经常记不清的在Java中添加C++代码的操作
总结:
我们要在Java里调用C++代码,必须借助JNI工具, 这是他们之间交流的工具。
我们通常自己写项目调C/C++的流程:
1.在JavaClass
中写要调用未来要在C++中实现的函数头:
public static native void xxxx();
2.在terminal中cd进入java文件的路径下,如:
cd C:\Users\changjianhui\Desktop\VCL_INTERN\githubDemo\FaceExpression\app\src\main\java
3.使用javah编译生成.h文件:
javah -d ../jni -jni cjh.faceexpression.env.ImageUtils
4.第2,3步也可以不要,替换成自己按规则来写出文件头,甚至.h可以没有,直接写.cpp文件,在.cpp中直接声明并实现。
但是函数名必须按照 Java_包名_类名_函数名
来写。
比如宏定义一个统一的函数名前缀:
#define IMAGEUTILS_METHOD(METHOD_NAME) \ Java_cjh_faceexpression_env_ImageUtils_##METHOD_NAME
在每个要写的函数里就可以用统一的格式写,只需要替换自己的method_name:
JNIEXPORT void JNICALL
IMAGEUTILS_METHOD(convertYUV420SPToARGB8888)(
JNIEnv* env, jclass clazz, jbyteArray input, jintArray output,
jint width, jint height, jboolean halfSize);
5.最后就是实现方法了,参数、返回值等等都要遵循JNI编程的规则,但是我们也可以添加普通的熟知的纯C++式代码来调用,但是需要给出正确的函数头,尤其参数声明,例如:
#ifndef FACEEXPRESSION_RGB2YUV_H
#define FACEEXPRESSION_RGB2YUV_H
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
void ConvertARGB8888ToYUV420SP(const uint32_t* const input,
uint8_t* const output, int width, int height);
void ConvertRGB565ToYUV420SP(const uint16_t* const input, uint8_t* const output,
const int width, const int height);
#ifdef __cplusplus
}
#endif
#endif //FACEEXPRESSION_RGB2YUV_H
遵循这个函数头我们可以再重新来写我们熟悉的函数,例如:
void ConvertRGB565ToYUV420SP(const uint16_t* const input, uint8_t* const output,
const int width, const int height) {
uint8_t* pY = output;
uint8_t* pUV = output + (width * height);
const uint16_t* in = input;
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
const uint32_t rgb = *in++;
const int r5 = ((rgb >> 11) & 0x1F);
const int g6 = ((rgb >> 5) & 0x3F);
const int b5 = (rgb & 0x1F);
// Shift left, then fill in the empty low bits with a copy of the high
// bits so we can stretch across the entire 0 - 255 range.
const int r8 = r5 << 3 | r5 >> 2;
const int g8 = g6 << 2 | g6 >> 4;
const int b8 = b5 << 3 | b5 >> 2;
WriteYUV(x, y, width, r8, g8, b8, pY++, pUV);
}
}
}
然后就可以在遵循JNI的复杂函数体里直接调用它而不必重写:
JNIEXPORT void JNICALL
IMAGEUTILS_METHOD(convertRGB565ToYUV420SP)(
JNIEnv* env, jclass clazz, jbyteArray input, jbyteArray output,
jint width, jint height) {
jboolean inputCopy = JNI_FALSE;
jbyte* const i = env->GetByteArrayElements(input, &inputCopy);
jboolean outputCopy = JNI_FALSE;
jbyte* const o = env->GetByteArrayElements(output, &outputCopy);
ConvertRGB565ToYUV420SP(reinterpret_cast<uint16_t*>(i),
reinterpret_cast<uint8_t*>(o), width, height);
env->ReleaseByteArrayElements(input, i, JNI_ABORT);
env->ReleaseByteArrayElements(output, o, 0);
}
6.修改cmakeList并重新编译。
当有多个cpp文件需要添加时,可以设一个统一的文件名,如下:
file(GLOB native_srcs "src/main/jni/*.cpp")
修改add_library参数:
add_library( # Sets the name of the library.
imageUtils
# Sets the library as a shared library.
SHARED
# Provides a relative path to your source file(s).
#src/main/jni/native-lib.cpp
${native_srcs})
别忘记target_link_libraries也需要修改
target_link_libraries( # Specifies the target library.
imageUtils
#native-lib
# Links the target library to the log library
# included in the NDK.
${log-lib} )
7.make project
gradle 里配置不再赘述,如果有错误按照提示信息来查改。