JNI code joins two memory and type systems. These older notes collect small examples from that boundary: finding native sources, loading libraries, passing arrays, and releasing acquired resources.
Historical fragments. These snippets come from different application contexts. They are not a complete native library and have not been compiled for this revision. Application classes, OpenCV setup, and resource ownership must be reviewed together before reuse.
Build and link native code
The source recorded a directory/glob example and an include-path note:
My project directory was like this:
--project
---Classes
----Application
-----.h and .c files
----OtherFolders
--main.cpp
And what I used to include the files in all those folders:
file(GLOB source_files
"*.h"
"*.cpp"
"Classes/*/*.cpp"
"Classes/*/*.h"
)
add_executable(Server ${source_files})
Add include_directories("/your/path/here").
This will be similar to calling gcc with -I/your/path/here/ option.
Make sure you put double quotes around the path.
Its Android shared-library configuration linked native bitmap and logging support:
cmake_minimum_required(VERSION 3.4.1)
add_library(
MagicJni
SHARED
MagicJni.cpp
beautify/MagicBeautify.cpp
beautify/MagicBeautify.h
bitmap/BitmapOperation.cpp
bitmap/BitmapOperation.h
bitmap/Conversion.cpp
bitmap/Conversion.h
bitmap/JniBitmap.h
)
find_library(
log-lib
log)
target_link_libraries( # Specifies the target library.
MagicJni
-ljnigraphics
${log-lib})
Enter the native boundary
Native logging, a JNI entry-point skeleton, and a string return were recorded separately:
#include <android/log.h>
#define log_print __android_log_print
#define LOG_TAG "MagicJni"
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG,LOG_TAG,__VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__)
….
#Using
LOGE("no bitmap data was stored. returning null...”);
log_print(ANDROID_LOG_VERBOSE, "stabilize", "Pointf %f %f", tp.GetPoint().x, tp.GetPoint().y);
//
// Original example, 2019-06-18.
//
#include <jni.h>
#include <string>
#include <stddef.h>
#include <opencv2/opencv.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <android/bitmap.h>
#include <android/log.h>
#define LOG_TAG "native-lib"
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG,LOG_TAG,__VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__)
#define log_print __android_log_print
using namespace cv;
extern "C" JNIEXPORT void JNICALL
Java_com_nhancv_facemask_tracking_PointState_init(JNIEnv *env, jobject /* this */) {
}
extern "C" JNIEXPORT jstring JNICALL
Java_com_nhancv_facemask_tracking_PointState_stringFromJNI(JNIEnv *env, jobject /* this */) {
std::string hello = "Hello from C++";
return env->NewStringUTF(hello.c_str());
}
The symbol names encode the Java package and method names. Renaming only one side breaks that binding. The logging excerpt includes explanatory text rather than being a standalone translation unit.
Work with a bitmap
This fragment locks bitmap pixels, uses OpenCV, and releases the acquired pixel storage:
extern "C" JNIEXPORT void JNICALL
Java_com_nhancv_facemask_tracking_PointState_canny(
JNIEnv *env,
jobject /* this */,
jobject bitmap,
jstring destination) {
// Get information about format and size
AndroidBitmapInfo info;
AndroidBitmap_getInfo(env, bitmap, &info);
// Get pointer to pixel buffer
void *pixels = 0;
AndroidBitmap_lockPixels(env, bitmap, &pixels);
{
Mat input(info.height, info.width, CV_8UC4, pixels);
Mat dst;
// Convert to gray
cvtColor(input, input, COLOR_BGR2GRAY);
// Histogram equalization
equalizeHist(input, dst);
// Saturation by 10%
float alpha = 1.1f;
float beta = 12.75f;
dst.convertTo(dst, -1, alpha, beta);
// Save to destination
const char *dest = env->GetStringUTFChars(destination, 0);
imwrite(dest, dst);
env->ReleaseStringUTFChars(destination, dest);
}
// Release the Bitmap buffer once we have it inside our Mat
AndroidBitmap_unlockPixels(env, bitmap);
}
Bitmap format, error handling, stride, and lifetime all need explicit consideration; the excerpt alone does not validate those conditions.
Transfer numeric arrays
Load the corresponding native library before calling its methods:
static {
System.loadLibrary("SuperpoweredAudio");
}
The Java conversion example chooses little-endian byte order:
public short[] toShorts(byte[] bytes) {
short[] out = new short[bytes.length / 2];
ByteBuffer.wrap(bytes).order(ByteOrder.LITTLE_ENDIAN).asShortBuffer().get(out);
return out;
}
public byte[] toBytes(short[] shorts) {
byte[] out = new byte[shorts.length * 2];
ByteBuffer.wrap(out).order(ByteOrder.LITTLE_ENDIAN).asShortBuffer().put(shorts);
return out;
}
The short-array example acquires elements, creates a result, and releases the acquired elements:
extern "C" JNIEXPORT jshortArray
Java_com_nhancv_opentokeffects_SuperpoweredAudio_processData(
JNIEnv *__unused env,
jobject __unused obj,
jshortArray data
) {
// Take data from java
jshort *tempPointer = env->GetShortArrayElements(data, JNI_FALSE);
int dataSize = env->GetArrayLength(data);
// Return
jshortArray result = (env)->NewShortArray(dataSize);
(env)->SetShortArrayRegion(result, 0, dataSize, (jshort *) tempPointer);
(env)->ReleaseShortArrayElements(data, tempPointer, JNI_ABORT);
return result;
}
Allocation and vectors
The remaining fragments show allocation, vector flattening, and a float-array result:
static float *floatBuffer;
// allocate buffer
floatBuffer = (float *) malloc(sizeof(float) * 2 * bufferSize);
std::vector<PointState> trackPoints;
std::vector<float> resultVector;
for (const PointState &tp : trackPoints) {
resultVector.push_back(tp.GetPoint().x);
resultVector.push_back(tp.GetPoint().y);
}
extern "C" JNIEXPORT jfloatArray JNICALL
Java_com_nhancv_facemask_tracking_PointState_stabilize(JNIEnv *env, jobject /* this */,
jfloatArray landmark_points) {
// Read
jfloat *tempPointer = env->GetFloatArrayElements(landmark_points, JNI_FALSE);
int dataSize = env->GetArrayLength(landmark_points);
// Populate data to vector
std::vector<cv::Point2f> landmarks;
for (int i = 0; i < dataSize; i += 2) {
landmarks.emplace_back(Point2f(*(tempPointer + i), *(tempPointer + i + 1)));
}
for (const cv::Point2f &lp : landmarks) {
trackPoints.emplace_back(lp);
}
std::vector<float> resultVector;
for (const PointState &tp : trackPoints) {
resultVector.push_back(tp.GetPoint().x);
resultVector.push_back(tp.GetPoint().y);
}
// Format data to return
jfloatArray result = (env)->NewFloatArray(dataSize);
(env)->SetFloatArrayRegion(result, 0, dataSize, (jfloat *) &*resultVector.begin());
(env)->ReleaseFloatArrayElements(landmark_points, tempPointer, JNI_ABORT);
return result;
}
Allocation must have an owner and matching cleanup. Empty vectors, element counts, JNI failures, and application-specific tracking objects are outside the small examples’ guarantees. For the recorded size_t include error, the source added:
#include <stddef.h>