AndroidJNI学习(一)

前言

本章主要记录学习中的JNI简单调用和传值

简介

JNI是Java Native Interface的简称,学会JNI我们可以在开发过程中调用一些使用C/C++编写的库从而实现一些音视频相关、图片渲染、I/O操作等一些功能

开发工具环境准备

Android Studio

CMake :一款跨平台的构建工具,与gradle可以构建原生库

NDK:Android提供的一款支持c/c++的工具集

环境准备好之后我们通过AS可以创建一个最简单模版JNI项目

根据提示点击下一步进行创建完成:

public class MainActivity extends AppCompatActivity {
    // 1.加载native库
    static {
        System.loadLibrary("nativestudy");
    }

    private ActivityMainBinding binding;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        binding = ActivityMainBinding.inflate(getLayoutInflater());
        setContentView(binding.getRoot());

        TextView tv = binding.sampleText;
        tv.setText(stringFromJNI());
    }

    //2.定义native 函数 
    public native String stringFromJNI();
}

native-lib.cpp文件内容:

#include <jni.h>
#include <string>

extern "C"  //添加c语言支持

//JNIEXPORT 与 JNICALL 是 JNI 中定义的两个宏 ,作用是告知虚拟机这是一个返回值是jstring类型jni函数 
JNIEXPORT jstring JNICALL 
//方法名格式为,native方法所在类的包名+方法名使用下划线连接,注意必须和包名+方法名一致!!!
Java_com_phuket_tour_nativestudy_MainActivity_stringFromJNI(
        JNIEnv* env,
        jobject /* this */) {
    std::string hello = "Hello from C++";
    return env->NewStringUTF(hello.c_str());
}

运行成功之后,显示"Hello from C++".


现在有这样一个需求:需要Android把值传递到native层。

怎么办?

不要慌先看看传递什么类型的数据。

  • 数据映射:

  1. 基本数据类型:byte、 short 、int 、long 、char、 float 、double 、boolean

先说基本数据类型,java的基本数据类型可以与c/c++基本数据类型直接映射,如下图所示:

java类型
jni类型
c/c++类型

byte

jbyte

char(有符号8位整型)

short

jshort

short(有符号16位整型)

int

jint

int(有符号32位整型)

long

jlong

long(有符号64位整型)

char

jchar

unsigned short(无符号16位整型)

float

jfloat

float(有符合32位浮点型)

double

jdouble

double(有符合64位双精度型)

boolean

jboolean

unsigned char(无符号8位整型)

直接来操作基本数据类型,定义native函数:

public class MainActivity extends AppCompatActivity {
    static {
        System.loadLibrary("nativestudy");
    }

    private ActivityMainBinding binding;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        binding = ActivityMainBinding.inflate(getLayoutInflater());
        setContentView(binding.getRoot());
        putValuesToJNI(
                (byte) 1,
                (short) 2,
                3,
                4,
                'a',
                5.0f,
                6.0d,
                true
        );
    }
    //声明native函数
    public native void putValuesToJNI(
            byte b,
            short s,
            int i,
            long l,
            char c,
            float f,
            double d,
            boolean bo
    );
}

然后根据映射表,在JNI中接收这些数据:

#include <jni.h>
#include <string>
#include <android/log.h>

#define TAG "native-lib"

#define LOG_TAG "native-lib"
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, TAG,  __VA_ARGS__);

extern "C"
JNIEXPORT void JNICALL
Java_com_phuket_tour_nativestudy_MainActivity_putValuesToJNI(
        JNIEnv *env,
        jobject,
        jbyte jbyte1,
        jshort jshort1,
        jint jint1,
        jlong jlong1,
        jchar jchar1,
        jfloat jfloat1,
        jdouble jdouble1,
        jboolean jboolean1
) {
    char c_byte = jbyte1;
    LOGD("jbyte-------->%d", c_byte);

    short s_short = jshort1;
    LOGD("jshort-------->%d", s_short);

    int i_int = jint1;
    LOGD("jint-------->%d", i_int);

    long l_long = jlong1;
    LOGD("jlong-------->%d", l_long);

    char c_char = jchar1;
    LOGD("jchar-------->%d", c_char);

    float f_float = jfloat1;
    LOGD("jfloat-------->%f", f_float);

    double d_double = jdouble1;
    LOGD("jdouble-------->%f", jdouble1);


    unsigned char b_boolean = jboolean1;
    LOGD("jboolean-------->%d", jboolean1);
}

点击运行:

  1. 引用数据类型:String、Class、Object、Object[]和一些类

引用数据类型映射如下图。列举几个常用的类型,它们的数据结构不能直接和native代码产生映射。

java

native

java.long.class

jclass

java.long.string

jstring

java.long.Throwable

jthrowable

java.long.Object[]

jobjectArray

Other object

jobject

byte[]

jbyteArray

short[]

jshortArray

int[]

jintArray

long[]

jlongArray

float[]

jfloatArray

double[]

jdoubleArray

char[]

jcharArray

Other arrays

jarray

1.定义native方法

public class MainActivity extends AppCompatActivity {
    static {
        System.loadLibrary("nativestudy");
    }

    private ActivityMainBinding binding;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        binding = ActivityMainBinding.inflate(getLayoutInflater());
        setContentView(binding.getRoot());
        putRefValueToJNI(
                "我来自Java",
                new byte[]{1, 2, 3},
                new short[]{4, 5, 6},
                new int[]{7, 8, 9},
                new long[]{10, 11, 12},
                new float[]{1f, 2f, 3f},
                new String[]{"a", "b", "c"},
                new boolean[]{true, true, false},
                new Book("《道德经》", 100)
        );
    }
    public native void putRefValueToJNI(
            String name,
            byte[] bytes,
            short[] shorts,
            int[] i,
            long[] longs,
            float[] floats,
            String[] str,
            boolean[] bool,
            Book book
    );
}

创建Java数据类

public class Book {
    private String name;
    private int num;

    public Book() {
    }

    public Book(String name, int num) {
        this.name = name;
        this.num = num;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getNum() {
        return num;
    }

    public void setNum(int num) {
        this.num = num;
    }

    @Override
    public String toString() {
        return "Book{" +
                "name='" + name + '\'' +
                ", num=" + num +
                '}';
    }
}

定义jni方法

extern "C"
JNIEXPORT void JNICALL
Java_com_phuket_tour_nativestudy_MainActivity_putRefValueToJNI(
        JNIEnv *env,
        jobject thiz,
        jstring jstring1,
        jbyteArray jbyteArray1,
        jshortArray jshortArray1,
        jintArray jintArray1,
        jlongArray jlongArray1,
        jfloatArray jfloatArray1,
        jobjectArray strArray,
        jbooleanArray jbooleanArray1,
        jobject book) {

    //接收Java传递的String类型
    const char *j_string = env->GetStringUTFChars(jstring1, 0);
    LOGD("string----->%s", j_string);

    //接收byte[]
    jbyte *byteArray = env->GetByteArrayElements(jbyteArray1, NULL);
    //获取数组长度
    jsize byteArraySize = env->GetArrayLength(jbyteArray1);
    for (int i = 0; i < byteArraySize; ++i) {
        LOGD("byte数组:第%d个值========%d", i, byteArray[i]);
    }
    //释放数组
    env->ReleaseByteArrayElements(jbyteArray1, byteArray, 0);


    //接收short[]
    jshort *shortArray = env->GetShortArrayElements(jshortArray1, NULL);
    //获取数组长度
    jsize shortArraySize = env->GetArrayLength(jshortArray1);
    for (int i = 0; i < byteArraySize; ++i) {
        LOGD("short数组:第%d个值========%d", i, shortArray[i]);
    }
    //释放数组
    env->ReleaseShortArrayElements(jshortArray1, shortArray, 0);

    //.... int[] long[] float[]与上面byte和short[]类似

    //获取String[]
    jsize jsize1 = env->GetArrayLength(strArray);
    for (int i = 0; i < jsize1; ++i) {
        jobject str_obj = env->GetObjectArrayElement(strArray, i);
        //强制转换 jni string
        jstring j_string = static_cast<jstring>(str_obj);
        //转换成 C string类型
        const char *valueStr = env->GetStringUTFChars(j_string, 0);
        LOGD("String[%d]的值=====%s", i, valueStr);
        //回收
        env->ReleaseStringUTFChars(j_string, valueStr);
    }
    //获取 boolean[]
    jsize bool_size = env->GetArrayLength(jbooleanArray1);
    jboolean *j_boolean = env->GetBooleanArrayElements(jbooleanArray1, 0);
    for (int i = 0; i < bool_size; ++i) {
        bool b1 = j_boolean[i];
        jboolean b2 = j_boolean[i];
        LOGD("boolean:%d", b1);
        LOGD("jboolean:%d", b2);
    }
    //回收
    env->ReleaseBooleanArrayElements(jbooleanArray1, j_boolean, 0);

    //获取 Object 对象 Java bean Book
    //1.类名全路径
    const char *book_class_str = "com/phuket/tour/nativestudy/Book";

    //2.根据类名路径获取 class 对象
    jclass book_class = env->FindClass(book_class_str);

    //3.获取方法签名 此签名是在jvm中存储的命名 使用javap -a命令可以获取 获取步骤在文章末尾图
    const char *sign = "()Ljava/lang/String;";
    jmethodID jmethodId = env->GetMethodID(book_class, "getName", sign);

    //调用book的getName() 获取到 object对象
    jobject obj_string = env->CallObjectMethod(book, jmethodId);
    //强转成字符串
    jstring bookNameStr = static_cast<jstring>(obj_string);
    const char *targetStr = env->GetStringUTFChars(bookNameStr, NULL);
    LOGD("Book的名字: %s", targetStr);
    env->DeleteLocalRef(book_class);
    env->DeleteLocalRef(book);
}

运行结果:

获取类方法签名步骤如下:

总结

学习没有捷径只有多加练习才能更好的掌握这项技能 通过不同数据类型,可以轻松实现JNI传值

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值