Erste Schritte mit Android JNI/NDK von eins bis zwei

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 .JNIjintC++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, JavaBasistyp entspricht JNIBasistyp

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

JavaDas 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 aufrufenenv->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

definiertJNIschließ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 zwischenJavaReferenztypen undJNIAnwendungstypen 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[]JNIjobject

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.

definiertJNIschließ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 ObjectDer erste Index im ArrayObjectjstring
JNIAPIenv->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_castEs 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 gespiegelteBitmapBild an dieJavaEbene 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.

Fügen Sie hier eine Bildbeschreibung ein

7. Andere

7.1 CMake

ÜberCMakeSie können meinen anderen Blog lesen: Android NDK CMakeLists.txt Allgemeine Befehlsanweisungen

7.2 Referenz

DankeAndroid CMake und NDK praktische Basis

Guess you like

Origin blog.csdn.net/EthanCo/article/details/134062657