前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >NDK 开发中的几个重要知识点

NDK 开发中的几个重要知识点

作者头像
字节流动
发布2020-06-03 10:20:13
9930
发布2020-06-03 10:20:13
举报
文章被收录于专栏:字节流动

有时候,你觉得世界对你不公平,并不是因为你错了,而是因为你还没获得足够的地位。

1. 在 Native 层访问 Java 对象的一般步骤

在 Native 层中通过 JNI 可以自由地访问 Java 对象,访问 Java 对象一般分为 3 步。

I. 通过 GetObjectClass 或 FindClass 获取到 class 对象的引用; II. 通过 GetXXMethodID、GetXXFieldID 方法分别获取到 Java 对象方法和属性的访问 ID ; III. 通过 CallXXMethod、GetXXField 等方法分别实现对 Java 对象方法的调用和属性的访问。

一个简单的例子。

代码语言:javascript
复制
package com.byteflow.framework;

public interface NDKCallback {
    void onProcessorCallback(int processorUId, int errorCode, Object objResult);
}
代码语言:javascript
复制
//1. 获取到 class 对象的引用
jclass callbackClass = env->GetObjectClass(callbackObj);

//2. 获取到 Java 对象方法的访问 ID
mCallbackMID = env->GetMethodID(callbackClass, "onProcessorCallback", "(IILjava/lang/Object;)V");

//3. 调用 Java 对象的方法
env->CallVoidMethod(callbackObj, mCallbackMID, processorUID, errCode, objResult);

利用 JNI 访问 Java 对象时,需要注意值传递和引用传递的区分,如 jint、jchar、jdouble 等基本类型是值传递,而 jstring、jobject 等属于引用传递。

另外,注意区分 FindClass 和 GetObjectClass 两个 JNI 方法的使用。FindClass 只需要完整的类名便可获得 class 对象的引用,而 GetObjectClass 通过 JNI 传入的一个 Java 对象的引用来获取 class 对象的引用

2. 在 Native 层中 Java 对象的引用类型

在 JVM 中 Java 对象的引用类型分为 “强、软、弱、虚” ,我们常用的一般是弱引用,而在 Native 层中 Java 对象的引用类型一般分为 3 种,即 Local ReferenceGlobal ReferenceWeak Global Reference

Local Reference 称为本地引用或局部引用,JNI 传入的 jobject 以及利用 JNI 函数创建的临时 jobject 对象一般是本地引用,其特点是一旦 JNI 调用完成,jobject 对象就会被回收掉,但可能不会被立即回收,需要注意其生命周期,强制立即回收调用 env->DeleteLocalRef(obj);。

Global Reference 称为全局引用,其特点是如果不主动释放,在进程生命周期里其对应的对象一直不会被回收。由此可见,全局引用若使用不当容易造成内存泄漏,全局引用的使用和释放应成对出现:

//创建 env->NewGlobalRef(g_obj); ... //释放 env->DeleteGlobalRef(g_obj);

其使用场景是,在 JNI 调用完成后,想要继续使用 jobject 对象,需要将其设置为 Global Reference 。

Weak Global Reference 称为弱全局引用,其特点是在程序运行期间,该引用对应的对象随时可能会被 GC 回收(如内存不足时),需要谨慎使用。

3. JNIEnv 和 JavaVM 类型的作用域

JNIEnv 这个结构体比较特殊,主要提供 JNI 调用环境(JNI Environment),JNIEnv 类型的变量是线程相关,即一个线程会对应一个 JNIEnv 变量,也就是说 JNIEnv 类型的指针不能在不同的线程中共用。

若要在一个子线程里访问 Java 对象,就需要获得对应 JNIEnv 类型变量的指针,一般通过一对 JNI 方法进行获取和释放:

//获取 jvm->AttachCurrentThread(&env); ... //释放 jvm->DetachCurrentThread();

JavaVM 类型的变量是进程相关,即一个 Java 虚拟机对应一个 JavaVM 类型的变量,通过 env->GetJavaVM(&g_jvm) 可获取当前进程 JavaVM 的指针。

一个简单的例子说明下 JNIEnv 和 JavaVM 类型变量的作用域。

代码语言:javascript
复制
public class MainActivity extends AppCompatActivity {
    private static final String TAG = "MainActivity";

    // Used to load the 'native-lib' library on application startup.
    static {
        System.loadLibrary("native-lib");
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

    /**
     * A native method that is implemented by the 'native-lib' native library,
     * which is packaged with this application.
     */
    public native String stringFromJNI();

    public void callbackFromJNI(int errCode, Object result) {
        Log.d(TAG, "callbackFromJNI() called with: errCode = [" + errCode + "], result = [" + result + "]");
    }
}
代码语言:javascript
复制
//全局变量
JavaVM *g_jvm = NULL;
jobject g_obj = NULL;

void *posix_run(void* arg)
{
    JNIEnv *env;
    jclass cls;
    jmethodID mid;

    //Attach 当前线程获取 JNIEnv
    if(g_jvm->AttachCurrentThread(&env, NULL) != JNI_OK)
    {
        return NULL;
    }

    //获取 class 对象
    cls = env->GetObjectClass(g_obj);

    //获取 MethodID
    mid = env->GetMethodID(cls, "callbackFromJNI", "(ILjava/lang/Object;)V");

    //调用对象方法
    env->CallVoidMethod(cls, mid , 0, NULL);

    //Detach 当前线程
    g_jvm->DetachCurrentThread();

    return NULL;
}

extern "C" JNIEXPORT jstring JNICALL
Java_com_byteflow_ndk_MainActivity_stringFromJNI(JNIEnv* env, jobject obj) {

    //获取 JavaVM
    env->GetJavaVM(&g_jvm);

    //创建对象的全局引用
    g_obj = env->NewGlobalRef(obj);

    pthread_t tid;
    //创建子线程
    pthread_create(&tid, NULL, &posix_run, NULL);

    //阻塞主线程等待子线程结束
    pthread_join(tid, NULL);

    //释放对象的全局引用
    env->DeleteGlobalRef(g_obj);
    return NULL;
}

-- END --

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2019-03-08,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 字节流动 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档