NDK開發——C++層調用java層函數

做NDK項目,不僅會用到java層通過jni調用C++層函數的情況,也會有需要C++可以調用java層函數的需求,所以我簡單整理了C++反調java函數的流程,直接進入正文:

一、創建java類,聲明native函數、用於可供C++調用的函數:

NDKUtils.java
package com.cwang.ndkdemo;

import android.util.Log;

/**
 * @author: cwang
 * @time: 2020/5/15 15:51
 * @Description:
 */
public class NDKUtils {
    private final String Tag = "NDKUtils";

    // 加載SO
    static {
        System.loadLibrary("NDKUtils");
    }

    /**
     * 構造函數
     */
    public NDKUtils() {
        super();
    }

    /**
     * 該初始化函數裏完成對JavaVM的初始化
     */
    public native void init();

    /**
     * 供C++調用的函數,java層傳輸一個字符串
     * @param str
     */
    public void strFromC(String str) {
        Log.i(Tag, "這是來自C++層的字符串 = " + str);
    }

}

二、生成NDKUtils.java對應jni的.h和.cpp

NDKUtils.h
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_cwang_ndkdemo_NDKUtils */

#ifndef _Included_com_cwang_ndkdemo_NDKUtils
#define _Included_com_cwang_ndkdemo_NDKUtils
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     com_cwang_ndkdemo_NDKUtils
 * Method:    init
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_com_cwang_ndkdemo_NDKUtils_init
  (JNIEnv *, jclass);

#ifdef __cplusplus
}
#endif
#endif

NDKUtils.cpp

#include "NDKUtils.h"
#include "LogUtil.h"

JavaVM *gJavaVM = NULL;
jobject G_obj = NULL;

bool CStrFromC(const char *CStr);

/*
 * Class:     com_cwang_ndkdemo_NDKUtils
 * Method:    init
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_com_cwang_ndkdemo_NDKUtils_init
  (JNIEnv * env, jclass obj) {
    if (gJavaVM == NULL) env->GetJavaVM(&gJavaVM);
    G_obj = env->NewGlobalRef(obj);

    if (CStrFromC("我是一個來自C++層的字符串")) {
        LOGI("CStrFromC Success");
    }
}

/**
 * 調用java層函數向java層傳輸一個字符串
 * @return 是否調用成功
 */
bool CStrFromC(const char *CStr) {
    //獲取obj中對象的class對象

    if(gJavaVM == NULL) return false;
    LOGD("CStrFromC");
    JNIEnv *G_env = NULL;


    bool isAttached = false;
    int status = gJavaVM->GetEnv((void **) (&G_env), JNI_VERSION_1_4);
    if (status < 0) {
        gJavaVM->AttachCurrentThread(&G_env,NULL);
        isAttached = true;
    }

    if (!G_env) return false;
    if (!G_obj) return false;

    //到java代碼的 class文件
    jclass clazz = G_env->GetObjectClass(G_obj);
    {
        jthrowable exc = G_env->ExceptionOccurred();
        if (exc) {
            G_env->ExceptionClear();
            G_env->DeleteLocalRef(clazz);
            return false;
        }
    }
    if (clazz == 0) {
        G_env->DeleteLocalRef(clazz);
        if (isAttached)
            gJavaVM->DetachCurrentThread();
        return false;
    }
    // 尋找class裏面的方法
    jmethodID id_StrFromC = G_env->GetMethodID(clazz, "strFromC", "(Ljava/lang/String;)V");
    {
        jthrowable exc = G_env->ExceptionOccurred();
        if (exc) {
            G_env->ExceptionClear();
            G_env->DeleteLocalRef(clazz);
            if (isAttached)
                gJavaVM->DetachCurrentThread();
            return false;
        }
    }
    if (id_StrFromC == 0) {
        G_env->DeleteLocalRef(clazz);
        if (isAttached)
            gJavaVM->DetachCurrentThread();
        return false;
    };


    jstring utf8CStr = G_env->NewStringUTF(CStr);
    //調用strFromC方法
    G_env->CallVoidMethod(G_obj, id_StrFromC, utf8CStr);
    G_env->DeleteLocalRef(clazz);
    G_env->DeleteLocalRef(utf8CStr);
    return true;
}

三、NDKUtils類的初始化

MainActivity.java
package com.cwang.ndkdemo;

import android.app.Activity;
import android.os.Bundle;

/**
 * @author cwang
 * @time: 2020/5/15 15:51
 * @Description:NDK Jni
 */
public class MainActivity extends Activity {
    private NDKUtils ndkUtils;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ndkUtils = new NDKUtils();
        ndkUtils.init();
    }

}

四、當然不要忘記CMakeLists.txt裏的配置

# For more information about using CMake with Android Studio, read the
# documentation: https://d.android.com/studio/projects/add-native-code.html

# Sets the minimum version of CMake required to build the native library.

cmake_minimum_required(VERSION 3.4.1)

# Creates and names a library, sets it as either STATIC
# or SHARED, and provides the relative paths to its source code.
# You can define multiple libraries, and CMake builds them for you.
# Gradle automatically packages shared libraries with your APK.

add_library( # Sets the name of the library.
        NDKUtils

        # Sets the library as a shared library.
        SHARED

        # Provides a relative path to your source file(s).
        NDKUtils.cpp
        JniStrUtils.cpp)

# Searches for a specified prebuilt library and stores the path as a
# variable. Because CMake includes system libraries in the search path by
# default, you only need to specify the name of the public NDK library
# you want to add. CMake verifies that the library exists before
# completing its build.

find_library( # Sets the name of the path variable.
        log-lib

        # Specifies the name of the NDK library that
        # you want CMake to locate.
        log)

# Specifies libraries CMake should link to your target library. You
# can link multiple libraries, such as libraries you define in this
# build script, prebuilt third-party libraries, or system libraries.

target_link_libraries( # Specifies the target library.
        NDKUtils

        # Links the target library to the log library
        # included in the NDK.
        ${log-lib})

五、沒有做UI,給大家看一下打印信息吧

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章