1. Einleitung
Für den grundlegendsten Vorgang zum Erstellen einer SchnittstelleJNI
können Sie diesen Artikel direkt lesen: Das erste Android-JNI-Projekt
< a i =3>, Dieser Artikel beginnt basierend auf 掌握创建JNI接口的操作
. JNI/NDK
2. Drucken Sie Protokolle in JNI
2.1 Protokollmodul hinzufügen
Denken Sie daran, dassCMake
das Modullog
enthält, sonst wird es nicht kompiliert
target_link_libraries(
#...省略
android
log)
2.2 Header-Dateien hinzufügen
#include <android/log.h>
2.3 Protokollmethode definieren
#define LOG_TAG "CPPLOG"
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG , __VA_ARGS__) // 定义LOGD类型
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,LOG_TAG , __VA_ARGS__) // 定义LOGE类型
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG , __VA_ARGS__) // 定义LOGE类型
2.4 Anrufe tätigen
LOGD("java int value is %p", value);
3. Grundlegende Typkonvertierung
JNI
und Java
Basistypen können direkt konvertiert werden
In jni.h
können wir sehen < Die Basis Zu den Typen von a i=4> gehören diese. Beispielsweise entspricht tatsächlich dem Typ in .JNI
jint
C++
int32_t
/* Primitive types that match up with Java equivalents. */
typedef uint8_t jboolean; /* unsigned 8 bits */
typedef int8_t jbyte; /* signed 8 bits */
typedef uint16_t jchar; /* unsigned 16 bits */
typedef int16_t jshort; /* signed 16 bits */
typedef int32_t jint; /* signed 32 bits */
typedef int64_t jlong; /* signed 64 bits */
typedef float jfloat; /* 32-bit IEEE 754 */
typedef double jdouble; /* 64-bit IEEE 754 */
In C++ ist _t eine Namenskonvention, die einen bestimmten Typ darstellt. Es ist üblich, _t als Suffix eines Typs bei der Benennung zu verwenden, um zu unterscheiden, dass es sich bei dem Namen um einen Typ und nicht um eine andere Entität (z. B. eine Variable oder Funktion) handelt.
Ich habe es in einer Tabelle organisiert, Java
Basistyp entspricht JNI
Basistyp
Java | Einheimisch |
---|---|
Boolescher Wert | jboolean |
Byte | jbyte |
verkohlen | jchar |
kurz | jkurz |
int | jint |
lang | lang |
schweben | jfloat |
doppelt | jdouble |
3.1 JNI-Methoden schreiben
Schreiben von JNI-Methoden in Java-Klassen
external fun callNativeInt(value:Int) : Int
external fun callNativeByte(value:Byte) : Byte
external fun callNativeChar(value:Char) : Char
external fun callNativeLong(value:Long) : Long
external fun callNativeFloat(value:Float) : Float
external fun callNativeDouble(value:Double) : Double
3.2 Schreiben Sie die entsprechende Methode in C++
extern "C"
JNIEXPORT jint JNICALL
Java_com_heiko_myopencvtest2023_NativeLib_callNativeInt(JNIEnv *env, jobject thiz, jint value) {
LOGD("value:%d", value);
return value + 1;
}
extern "C"
JNIEXPORT jbyte JNICALL
Java_com_heiko_myopencvtest2023_NativeLib_callNativeByte(JNIEnv *env, jobject thiz, jbyte value) {
LOGD("value:%d", value);
return value + 1;
}
extern "C"
JNIEXPORT jchar JNICALL
Java_com_heiko_myopencvtest2023_NativeLib_callNativeChar(JNIEnv *env, jobject thiz, jchar value) {
LOGD("value:%d", value);
return value + 1;
}
extern "C"
JNIEXPORT jlong JNICALL
Java_com_heiko_myopencvtest2023_NativeLib_callNativeLong(JNIEnv *env, jobject thiz, jlong value) {
LOGD("value:%d", value);
return value + 1;
}
extern "C"
JNIEXPORT jfloat JNICALL
Java_com_heiko_myopencvtest2023_NativeLib_callNativeFloat(JNIEnv *env, jobject thiz, jfloat value) {
LOGD("value:%f", value);
return value + 1.0;
}
extern "C"
JNIEXPORT jdouble JNICALL
Java_com_heiko_myopencvtest2023_NativeLib_callNativeDouble(JNIEnv *env, jobject thiz,
jdouble value) {
LOGD("value:%f", value);
return value + 1.0;
}
3.3. Anrufe tätigen
Log.i(TAG, "result:${
nativeLib.callNativeInt(1)}")
Log.i(TAG, "result:${
nativeLib.callNativeByte(2)}")
Log.i(TAG, "result:${
nativeLib.callNativeChar('c')}")
Log.i(TAG, "result:${
nativeLib.callNativeLong(4)}")
Log.i(TAG, "result:${
nativeLib.callNativeFloat(5F)}")
Log.i(TAG, "result:${
nativeLib.callNativeDouble(6.0)}")
3.4 Führen Sie das Projekt aus
Das Druckprotokoll sieht wie folgt aus
10:16:36.815 D value:1
10:16:36.815 I result:2
10:16:36.815 D value:2
10:16:36.815 I result:3
10:16:36.815 D value:99
10:16:36.815 I result:d
10:16:36.815 D value:4
10:16:36.815 I result:5
10:16:36.815 D value:5.000000
10:16:36.815 I result:6.0
10:16:36.816 D value:6.000000
10:16:36.816 I result:7.0
4. Zeichenfolge
Java
Das Konvertieren von string in Native
string kann nicht direkt durchgeführt werden. Sie müssen env->GetStringUTFChars()
,
entsprechend aufrufen. Ja, Sie Sie müssen env->ReleaseStringUTFChars()
aufrufen, um Ressourcen freizugeben.
Standardmäßig ist Java UTF-codiert. Wenn es nicht UTF-codiert ist, müssen Sie es aufrufen
env->GetStringChars()
4.1 Java/Native-String-Konvertierung
external fun callNativeString(value:String) : String
extern "C"
JNIEXPORT jstring JNICALL
Java_com_heiko_myopencvtest2023_NativeLib_callNativeString(JNIEnv *env, jobject thiz,
jstring value) {
//Java字符串转成Native的字符串,并不能直接做转换
const char *str = env->GetStringUTFChars(value, NULL); //Java的字符串是UTF编码的
//env->GetStringChars(); //如果不是UTF编码,就用这个
LOGD("str:%s", str);
env->ReleaseStringUTFChars(value, str);
jstring result = env->NewStringUTF("hello world!");
return result;
}
Einen Anruf tätigen
Log.i(TAG, "result:${
nativeLib.callNativeString("你好呀")}")
nativeLib.stringMethod("hello world!")
Ergebnisse der
10:45:45.849 D str:你好呀
10:45:45.849 I result:hello world!
4.2 Verwendung von C++-Strings
definiertJNI
schließen
external fun stringMethod(value:String)
EchteC++
Methode
extern "C"
JNIEXPORT void JNICALL
Java_com_heiko_myopencvtest2023_NativeLib_stringMethod(JNIEnv *env, jobject thiz, jstring value) {
const char *str = env->GetStringUTFChars(value, 0);
int length = env->GetStringLength(value);
LOGD("length:%d", length);
char buf[256];
env->GetStringUTFRegion(value, 0, length, buf); //拷贝字符串数据到char[]中
LOGD("text:%s", buf);
env->ReleaseStringUTFChars(value, str);
}
Einen Anruf tätigen
Log.i(TAG, "result:${
nativeLib.callNativeString("你好呀")}")
nativeLib.stringMethod("hello world!")
Ergebnisse der
10:45:45.849 D length:12
10:45:45.849 D text:hello world!
5. Verwendung von Referenztypen
Hier wird die Entsprechung zwischenJava
Referenztypen undJNI
Anwendungstypen aufgeführt.
Es ist erwähnenswert, dass nicht alle Java
Referenztypen entsprechende JNI
Referenztypen haben.
Beispielsweise hat das String-Array in Java
keinen entsprechenden Verweis auf den Typ , in diesem Fall wird einheitlich als klassifiziert. String[]
JNI
jobject
Java-Referenz | Einheimisch |
---|---|
Alle Objekte | Jobobjekt |
java.lang.Class | jclass |
java.lang.String | jstring |
Objekt[] | jobjectArray |
boolean[] | jbooleanArray |
Byte[] | jbyteArray |
java.lang.Throwable | jwerfbar |
verkohlen[] | jcharArray |
kurz[] | jshortArray |
int[] | jintArray |
lang[] | jlongArray |
schweben[] | jfloatArray |
doppelt[] | jdoubleArray |
5.1 Übergabe von String-Daten
Die Java-Ebene übergibt ein String-Array und nachdem dieC++
-Ebene es empfangen hat, ruft sie die erste Zeichenfolge des String-Arrays ab und gibt sie aus.
definiertJNI
schließen
external fun callNativeStringArray(array:Array<String>)
implementiert die Methode C++
. Da es sich um ein String-Array handelt, gibt es in JNI keinen entsprechenden Typ, daher muss er zuerst über env->GetObjectArrayElement()
abgerufen werden , direkt auf, z. B. einen entsprechenden Typ hat, rufen Sie einfach den relevanten Wenn umgewandelt. und dann in den Typ Object
Der erste Index im ArrayObject
jstring
JNI
API
env->GetIntArrayElements()
env->GetFloatArrayElements()
extern "C"
JNIEXPORT void JNICALL
Java_com_heiko_myopencvtest2023_NativeLib_callNativeStringArray(JNIEnv *env, jobject thiz,
jobjectArray array) {
int len = env->GetArrayLength(array);
LOGD("len:%d",len);
//env->GetIntArrayElements() //获取Int数组
//env->GetFloatArrayElements() //获得Float数组
//env->GetObjectArrayElement() //获得JNI数组
jstring result = static_cast<jstring>(env->GetObjectArrayElement(array, 0)); //获取index为0的值
const char * str = env->GetStringUTFChars(result,NULL);
LOGD("text[0]:%s",str);
env->ReleaseStringUTFChars(result,str);
}
static_cast
Es handelt sich um eine Zwangsübertragung dieser Art
Einen Anruf tätigen
val array = arrayOf("ABC", "DEF", "GHI", "JKL", "MNO")
nativeLib.callNativeStringArray(array)
Ergebnisse der
13:27:06.865 D len:5
13:27:06.865 D text[0]:ABC
6. Bitmap übergeben
Hier nehmen wir das SpiegelbildBitmap
als Beispiel, übertragen das Bitmap
-Bild auf die JNI
-Ebene und Führen Sie dann den Spiegelungsvorgang aus und geben Sie das gespiegelteBitmap
Bild an dieJava
Ebene zurück
6.1 Erhalten Sie Bitamp-Informationen
AufrufAndroidBitmap_getInfo()
, um Bitmap-Informationen zu erhalten.
AndroidBitmapInfo bitmapInfo;
AndroidBitmap_getInfo(env, bitmap, &bitmapInfo);
6.2 Bitmap-Pixelinhalt abrufen
AufrufAndroidBitmap_lockPixels()
, um den Pixelinhalt vonBitmap
zu erhalten.
Denken Sie gleichzeitig daran, AndroidBitmap_unlockPixels()
aufzurufen, um Ressourcen freizugeben. Diese beiden API
werden paarweise verwendet.
//拿到像素内容
void *bitmapPixels;
AndroidBitmap_lockPixels(env, bitmap, &bitmapPixels);
//释放资源
AndroidBitmap_unlockPixels(env, bitmap);
6.3 Bitamp in JNI erstellen
Kopieren Sie einfach diese gekapselte Methode und rufen Sie sie auf
jobject generateBitmap(JNIEnv *env, uint32_t width, uint32_t height) {
// 获取Bitmap类引用
jclass bitmapCls = env->FindClass("android/graphics/Bitmap");
// 获取Bitmap构造方法的引用
jmethodID createBitmapFunction = env->GetStaticMethodID(bitmapCls, "createBitmap",
"(IILandroid/graphics/Bitmap$Config;)Landroid/graphics/Bitmap;");
jstring configName = env->NewStringUTF("ARGB_8888");
jclass bitmapConfigClass = env->FindClass("android/graphics/Bitmap$Config");
jmethodID valueOfBitmapConfigFunction = env->GetStaticMethodID(
bitmapConfigClass, "valueOf", "(Ljava/lang/String;)Landroid/graphics/Bitmap$Config;"
);
jobject bitmapConfig = env->CallStaticObjectMethod(bitmapConfigClass,
valueOfBitmapConfigFunction, configName);
jobject newBitmap = env->CallStaticObjectMethod(bitmapCls, createBitmapFunction, width, height,
bitmapConfig);
return newBitmap;
}
6.4 Implementieren Sie den Bitmap-Spiegelungsvorgang
DefinitionJNI
external fun mirrorBitmap(bitmap: Bitmap) : Bitmap
realistischC++
daigo
extern "C"
JNIEXPORT jobject JNICALL
Java_com_heiko_myncnnlib_NcnnNativeLib_mirrorBitmap(JNIEnv *env, jobject thiz, jobject bitmap) {
AndroidBitmapInfo bitmapInfo;
AndroidBitmap_getInfo(env, bitmap, &bitmapInfo);
__android_log_print(ANDROID_LOG_DEBUG, "jniBitmap", "width:%d,height:%d", bitmapInfo.width,
bitmapInfo.height);
//拿到像素内容
void *bitmapPixels;
AndroidBitmap_lockPixels(env, bitmap, &bitmapPixels);
uint32_t newWidth = bitmapInfo.width;
uint32_t newHeight = bitmapInfo.height;
uint32_t *newBitmapPixels = new uint32_t[newWidth * newHeight];
int index = 0;
//遍历Bitmap像素,将左右的像素进行互换 (镜像操作)
for (int y = 0; y < newHeight; y++) {
for (int x = newWidth - 1; x >= 0; x--) {
uint32_t pixel = ((uint32_t *) bitmapPixels)[index++];
newBitmapPixels[newWidth * y + x] = pixel;
}
}
AndroidBitmap_unlockPixels(env, bitmap);
//生成新的Bitmap
jobject newBitmap = generateBitmap(env, newWidth, newHeight);
void *resultBitmapPixels;
AndroidBitmap_lockPixels(env, newBitmap, &resultBitmapPixels);
//拷贝
memcpy((uint32_t *)resultBitmapPixels, newBitmapPixels, sizeof(uint32_t) * newWidth * newHeight);
AndroidBitmap_unlockPixels(env,newBitmap);
delete [] newBitmapPixels;
return newBitmap;
}
Einen Anruf tätigen
var bitmap = BitmapFactory.decodeResource(resources,R.drawable.img_test)
binding.img1.setImageBitmap(bitmap)
binding.btnMirrorImage.setOnClickListener {
bitmap = nativeLib.mirrorBitmap(bitmap)
binding.img1.setImageBitmap(bitmap)
}
Öffnen Sie das Programm, klicken Sie aufButton
und Sie werden feststellen, dass das Bild gespiegelt wurde.
7. Andere
7.1 CMake
ÜberCMake
Sie können meinen anderen Blog lesen: Android NDK CMakeLists.txt Allgemeine Befehlsanweisungen