Android NDK 开发之JNI常规操作

image-20220715173544457

1. JNI函数详解

java中native方法在C++代码中一般如下:

extern "C" JNIEXPORT jstring JNICALL
Java_com_lucky_jnidemo_MainActivity_stringFromJNI(
        JNIEnv* env,
        jobject /* this */) {
    std::string hello = "Hello from C++";
    return env->NewStringUTF(hello.c_str());
}
1. extern "C" 说明

表示下面的代码,都采用C的编译方式。

之所以用C的编译方式,是因为 JNIEnv 是C语言代码写的,避免一些函数重载等C语言中不支持的

查看JNIEnv源码,进入到jni.h,可以看到:

#if defined(__cplusplus)
typedef _JNIEnv JNIEnv; // 如果是C++代码,用这个宏定义
typedef _JavaVM JavaVM;
#else
typedef const struct JNINativeInterface* JNIEnv;  //如果是C代码,用这个宏定义
typedef const struct JNIInvokeInterface* JavaVM;
#endif

继续跟踪 _JNIEnv,可以看到:

struct _JNIEnv {
    /* do not rename this; it does not seem to be entirely opaque */
    const struct JNINativeInterface* functions;

因此,env的调用方式如下:

// C++的情况如下: 
Java_com_lucky_jnidemo_MainActivity_stringFromJNI(JNIEnv * env, ...) 
{
   env->NewStringUTF();  //详细看源码中的结构体定义
}

C的情况如下: 
Java_com_lucky_jnidemo_MainActivity_stringFromJNI(JNIEnv * env, ...) 
{ 
  (*env)->NewStringUTF(); //二级指针
}
2. JNIEXPORT 宏定义说明

针对Linux平台:该声明的作用是保证在本动态库中声明的方法 , 能够在其他项目中可以被调用 ;

3. JNICALL 宏定义说明

此宏定义为空,用来表示函数的调用规范。

4. jobjectjclass
jobject // java传递下来的对象,就是本项目中 MainActivity 对象 
jclass // java传递下来的 class 对象,就是本项目中的 MainActivity class

2. JNI中函数操作

1. 在C层中修改Java/Kotlin层String变量(引用类型遍历)

Kotlin代码:

//即将要修改的代码熟悉
private var mName: String = "Lucky" 

//native 修改String name
external fun changeStringName()

//点击按钮进行修改
binding.btn1.setOnClickListener {
   
    Log.i("MainActivity", "修改前:$mName")
    changeStringName()
    Log.i("MainActivity", "修改后:$mName")
}

C层中的代码

extern "C"
JNIEXPORT void JNICALL
Java_com_lucky_jnidemo_MainActivity_changeStringName(JNIEnv *env, jobject thiz) {

    //获取Class方式一
    jclass mainActivityCls = env->FindClass("com/lucky/jnidemo/MainActivity");

    //获取Class方式二
    //jclass mainActivityCls = env->GetObjectClass(thiz);

    // jfieldID GetFieldID(jclass clazz, const char* name, const char* sig)
    jfieldID nameFid = env->GetFieldID(mainActivityCls, "mName", "Ljava/lang/String;");

    // void SetObjectField(jobject obj, jfieldID fieldID, jobject value)
    jstring value = env->NewStringUTF("Update Lucky");

    //void SetObjectField(jobject obj, jfieldID fieldID, jobject value)
    env->SetObjectField(thiz, nameFid, value); //修改值
}

打印结果:

com.lucky.jnidemo I/MainActivity: 修改前:Lucky
com.lucky.jnidemo I/MainActivity: 修改后:Update Lucky

2. 在C层中修改Kotlin层静态Int变量

通过apk查看编译后的MainActivity 类,可以看到静态mAege是在MainActivity 中

image-20220714112416060

因此,这里跟修改MainActivity中的变量一样的操作

Kotlin代码:

companion object {
   
    private var mAge: Int = 666
    init {
   
        System.loadLibrary("jnidemo")
    }
    external fun changeIntAge() //修改Int age
}


binding.btn2.setOnClickListener {
   
    Log.i("MainActivity", "修改前:${
     mAge}")
    changeIntAge()
    Log.i("MainActivity", "修改后:${
     mAge}")
}

C层中的代码

extern "C"
JNIEXPORT void JNICALL
Java_com_lucky_jnidemo_MainActivity_000
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值