Android Native 回调 Java/JVM

背景

网络上大部分文章都是JNI的编写, 但是一些情况下在so里面,也有Native调用回Java的场景,文章相对较少。而且如果照着一些文章写的,可能会出现写的c的每一行代码都执行了,但是Java层就是没执行等奇奇怪怪的问题。做项目时也遇到过这些坑,特总结如下。

部分代码

//TestJNI.kt
class TestJNI{
  init{
    System.loadLibrary("test")
    initTest()
  }

  //防止混淆
  @Keep
  fun nativeInvokeAction(action: Int, contentValue: Int) {
    when (action) {
      1->{}
      2->{}
    }
  }

  private external fun initTest(): Int

}
//test.h
struct object_service{
    jobject serviceObject;
    jmethodID invokeAction;
};

//test.c
JavaVM *jvm;

void service_device_invoke_action(unsigned int action, unsigned long contentValue) {
  JNIEnv *env;
  //判断c层的这个线程是否在JVM环境有对应线程。
  bool detached = (*jvm)->GetEnv(jvm, (void **) &env, JNI_VERSION_1_6) == JNI_EDETACHED;
  //如果没有,则会依附到JVM,其实就是在JVM生成一个对应线程,用于执行方法
  if(detached) (*jvm)->AttachCurrentThread(jvm, &env, NULL);
  //调用JVM层方法无返回值是CallVoidMethod,有返回值就是比如CallObjectMethod这些
  (*env)->CallVoidMethod(env, testService.serviceObject,
               testService.invokeAction, action, (int)contentValue);
  //用完后,放弃使用JVM层的线程,便于JVM回收
  if(detached) (*jvm)->DetachCurrentThread(jvm);
}

struct object_service testService;

JNIEXPORT jint JNICALL
Java_com_test_testjni_TestJNI_initTest(JNIEnv *env, jobject thiz) {
  testService.serviceObject = (*env)->NewGlobalRef(env, thiz);
  jclass testClass = (*env)->FindClass(env, "com/test/testjni/TestJNI");
  testService.invokeAction = (*env)->GetMethodID(env, testClass, "nativeInvokeAction",
                               "(II)V");
}

JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved){
  jvm = vm;
  return JNI_VERSION_1_6;
}

个人理解

Native 回调 Java需要这些东西:jvm, 包含需要执行的方法的object, 方法,参数,还有执行线程。

所以在JVM调用so初始化的时候,先通过JNI_OnLoad拿到jvm。

初始化方法中,通过NewGlobalRef(env, thiz)拿到了对象,通过FindClass和GetMethodID拿到方法。

在service_device_invoke_action方法里面,通过jni提供的GetEnv AttachCurrentThread拿到JVM里的执行线程。

遇到的一些问题

  1. kt中nativeInvokeAction方法被混淆,导致异常,所以添加了 @Keep 或者proguard-rules.pro等
  2. c的代码都执行了,但是没有回调成功,没有反应。原因是线程问题,可查看service_device_invoke_action注释。因为Native层可能会自己新建线程等,这时新建出来的线程在JVM是没有对应的。
  3. 回调的nativeInvokeAction假如有返回值应该怎么做,比如返回String。这时候CallVoidMethod就需要用CallObjectMethod。以此类推。
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值