NDK<第四篇>:c++调用so库

【第一步】 编写C++代码

Test.cpp

int test() {
    return 10086;
}

【第二步】 生成so库

在AS中配置好cmake环境,将 Test.cpp 放入工程,编译出 so 库。(第一章节有详细介绍)

【第三步】 声明 extern int test(),并调用test函数

#include <jni.h>
#include <string>
#include <pthread.h>
#include <android/log.h>
#define LOG_TAG "native-lib"
#define LOGV(...) __android_log_print(ANDROID_LOG_VERBOSE, LOG_TAG, __VA_ARGS__)
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
#define LOGW(...) __android_log_print(ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)

using namespace std;

// 如果是C,需要添加 extern "C"{}
extern int test();

// 全局变量
JavaVM* javaVm;
struct Context {
    jobject obj;
};

/**
 * 子线程
 * @param args
 * @return
 */
void* threadTask(void* args) {
    LOGI("start thread task");
    LOGE("执行test函数:%d", test());
    if (javaVm == NULL) {
        LOGE("javaVm is null");
        return JNI_FALSE;
    }
    JNIEnv* env;
    // 将native线程添加到JVM中
    jint isAttach = javaVm->AttachCurrentThread(&env, 0);
    if (isAttach != JNI_OK) {
        LOGE("attact thread error");
        return JNI_FALSE;
    }
    Context* context = static_cast<Context *>(args);
    // 得到字节码
    jclass clazz = env->GetObjectClass(context->obj);
    // 得到methodId
    jmethodID methodId = env->GetMethodID(clazz, "updateUI", "()V");
    // 执行方法
    env->CallVoidMethod(context->obj, methodId);
    // 分离
    javaVm->DetachCurrentThread();
    delete context;
    context = NULL;
    return JNI_FALSE;
}

JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm,void*) {
    LOGI("in JNI_Onload");
    javaVm = vm;
    return JNI_VERSION_1_6;
}

extern "C" JNIEXPORT void JNICALL
Java_com_nobug_jniproject_MainActivity_testThread(JNIEnv *env, jobject obj) {
    LOGI("testThread from C");
    Context* context = new Context;
    context->obj = env->NewGlobalRef(obj);
    pthread_t pid;
    // 启动一个线程
    pthread_create(&pid, 0,threadTask, context);
}

【第四步】 Java 调用 testThread

public class MainActivity extends AppCompatActivity {

    private ActivityMainBinding binding;
    private JNI jni = new JNI();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        binding = ActivityMainBinding.inflate(getLayoutInflater());
        setContentView(binding.getRoot());

        binding.sampleText.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                testThread();
            }
        });
    }

    public native void testThread();

    public void updateUI() {
        if (Looper.getMainLooper() == Looper.myLooper()) {
            Toast.makeText(MainActivity.this, "updateUI", Toast.LENGTH_SHORT).show();
        } else {
            runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    Toast.makeText(MainActivity.this, "updateUI", Toast.LENGTH_SHORT).show();
                }
            });
        }
    }
}

【第五步】 注意Gradle配置

plugins {
    id 'com.android.application'
}

android {
    compileSdk 32

    defaultConfig {
        applicationId "com.nobug.jniproject"
        minSdk 21
        targetSdk 32
        versionCode 1
        versionName "1.0"

        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
        externalNativeBuild {
            cmake {
                abiFilters 'armeabi-v7a'
                cppFlags "-std=c++11 -frtti -fexceptions -Os -Wall"
            }
        }
        ndk { // "armeabi-v7a", "arm64-v8a"
            abiFilters 'armeabi-v7a'
        }
    }

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }
    externalNativeBuild {
        cmake {
            path file('CMakeLists.txt')
            version '3.18.1'
        }
    }
    buildFeatures {
        viewBinding true
    }
}

dependencies {

    implementation 'androidx.appcompat:appcompat:1.3.0'
    implementation 'com.google.android.material:material:1.4.0'
    implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
    testImplementation 'junit:junit:4.13.2'
    androidTestImplementation 'androidx.test.ext:junit:1.1.3'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
}

【第六步】 cmake配置

 cmake_minimum_required(VERSION 3.18.1)

 # project("jniproject")

 # 导入库目录
 # include_directories("C:/Program Files/Java/jdk-18.0.1.1/include")

 # 生成一个动态库(windows:dll,android:so)
 add_library(jniproject SHARED src/main/cpp/native-lib.cpp)

 # 设置一个变量
 # CMAKE_CXX_FLAGS c++参数
 # CMAKE_C_FLAGS c参数
 # CMAKE_C_FLAGS = --sysroot=XX
 set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -L${CMAKE_SOURCE_DIR}/src/main/jniLibs/armeabi-v7a")
 #add_library(Test SHARED IMPORTED)
 #set_target_properties(Test PROPERTIES IMPORTED_LOCATION ${CMAKE_SOURCE_DIR}/src/main/jniLibs/armeabi-v7a/libTest.so)


 # 生成可执行文件(生成exe文件, 在VS工具上可用)
 # add_executable (jniproject1 native-lib.cpp)

 # log-lib 是变量名称  log是动态库名称 将liblog.so或liblog.a的路径赋值给log-lib
 find_library(log-lib log)

 # 链接ndk自带的库
 target_link_libraries(
         jniproject        # 链接 libjniproject.so
         Test              # 链接 libTest.so
         ${log-lib})       # 链接 liblog.so

其中

set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -L${CMAKE_SOURCE_DIR}/src/main/jniLibs/armeabi-v7a")

可以改成:

 add_library(Test SHARED IMPORTED)
 set_target_properties(Test PROPERTIES IMPORTED_LOCATION ${CMAKE_SOURCE_DIR}/src/main/jniLibs/armeabi-v7a/libTest.so)

【第七步】 运行app,查看工作台中是否有相关日志

image.png

[本章完...]

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容