Android随手录#JNI-->build.gradle,Cmakelists.txt基本配置

本文详细介绍了在Android项目中使用JNI进行本地代码集成的步骤,包括在appbuild.gradle和CMakeLists.txt中的基本配置,如设置ABI过滤器,链接库等。同时,文章讲解了JNI的日志宏定义,动态注册方法以及如何从JNI回调Java类的方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

整体项目结构

项目文件目录结构

app build.gradle :

plugins {
    id 'com.android.application'
}

android {
    namespace 'com.ming.ffmpeg'
    compileSdk 32

    defaultConfig {
        applicationId "com.ming.ffmpeg"
        minSdk 26
        targetSdk 32
        versionCode 1
        versionName "1.0"

        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"

        externalNativeBuild {
            cmake {
                abiFilters "arm64-v8a", "armeabi-v7a"
//                abiFilters "arm64-v8a"
            }
        }
    }

    //set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -L${CMAKE_SOURCE_DIR}/libs/${ANDROID_ABI}")
    //配合cmakelist引入多个动态库,但也可不配置这个,但需要再cmakelist配置so库具体路径,比较麻烦
    sourceSets {
        main {
            jniLibs.srcDirs = ['src/main/jni/libs']
        }
    }

    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('src/main/jni/CMakeLists.txt')
            version '3.18.1'
        }
    }
    buildFeatures {
        viewBinding true
    }
}

dependencies {

    implementation 'com.android.support:appcompat-v7:28.0.0'
    implementation 'com.android.support.constraint:constraint-layout:2.0.4'
    testImplementation 'junit:junit:4.13.2'
    androidTestImplementation 'com.android.support.test:runner:1.0.2'
    androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
}

CMakeList :

# 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.18.1)

# Declares and names the project.

project("ffmpeg")


#######################ffmpeg libs config##########################

#add h files
include_directories(${CMAKE_SOURCE_DIR}/h/ffmpeg)
include_directories(${CMAKE_SOURCE_DIR}/h/self_jni)

#add libs
#this way need config "sourceSets {
#main {
#jniLibs.srcDirs = ['src/main/jni/libs']
#}
#}" in app build.gradle
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -L${CMAKE_SOURCE_DIR}/libs/${ANDROID_ABI}")

#source cpp files
file(GLOB source_files ${CMAKE_SOURCE_DIR}/cpp/*.cpp)


#######################ffmpeg libs config##########################



# 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.
        ffmpeg

        # Sets the library as a shared library.
        SHARED

        # Provides a relative path to your source file(s).
       ${source_files})

# 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.
        ffmpeg

        #FFmpeg libs
        avcodec
        avfilter
        avformat
        avutil
        swresample
        swscale

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

JNI 有用的基本配置:

1.Log的定义,base.h

//
// Created by mingzz_ on 2023/2/7.
//

#ifndef FFMPEG_BASE_H
#define FFMPEG_BASE_H

#include <jni.h>
#include <android/log.h>

#define DEBUG true

#define LOGI(...) \
if (DEBUG)        \
__android_log_print(ANDROID_LOG_INFO,"mingzz-JNINative",__VA_ARGS__)

#define LOGD(...) \
if (DEBUG)        \
__android_log_print(ANDROID_LOG_DEBUG,"mingzz-JNINative",__VA_ARGS__)

#define LOGW(...) \
if(DEBUG) \
__android_log_print(ANDROID_LOG_WARN,"mingzz-JNINative",__VA_ARGS__)

#define LOGE(...) \
if(DEBUG)         \
__android_log_print(ANDROID_LOG_ERROR,"mingzz-JNINative",__VA_ARGS__)


#endif //FFMPEG_BASE_H

2.jni动态注册

#include "base.h"

jstring sayHello(JNIEnv *env, jclass clazz) {
    std::string hello = "hello FFmpeg!!!!";
    return env->NewStringUTF(hello.c_str());
}

/***********************************************************************************************************
 * register native methods
 * ********************************************************************************************************/

/*
 * Class and package name
 * static const char *classPathName = "com/qualcomm/qti/usta/core/AccelCalibrationJNI";
 * */
//com.mingzz__.h26x.rtmp.RTMPActivity
static const char *classPathName_1 = "com/ming/ffmpeg/MainActivity";

/*
 * List of native methods
 *  {"startCalibration" , "()V", (void *)startCalibration},
 *  {"getAccelCalFromNative" , "()Ljava/lang/String;", (void *)getAccelCalFromNative},
 * {"stopCalibration" , "()V", (void *)stopCalibration},
 * */
static JNINativeMethod methods_1[] = {
        {"stringFromJNI", "()Ljava/lang/String;", (void *) sayHello},
};

/*
 * Register several native methods for one class.
 *
 *
 * */
static int
registerNativeMethods(JNIEnv *envVar, const char *inClassName, JNINativeMethod *inMethodsList,
                      int inNumMethods) {
    jclass javaClazz = envVar->FindClass(inClassName);
    if (javaClazz == NULL) {
        return JNI_FALSE;
    }
    if (envVar->RegisterNatives(javaClazz, inMethodsList, inNumMethods) < 0) {
        return JNI_FALSE;
    }
    return JNI_TRUE;
}

/*
   * Register native methods for all classes we know about.
   *
   * Returns JNI_TRUE on success
   *
   * */
static int registerNatives(JNIEnv *env) {
    if (!registerNativeMethods(env, classPathName_1, methods_1,
                               sizeof(methods_1) / sizeof(methods_1[0]))) {
        return JNI_FALSE;
    }
    return JNI_TRUE;
}


typedef union {
    JNIEnv *env;
    void *venv;
} UnionJNIEnvToVoid;

/*
* This is called by the VM when the shared library is first loaded.
* */
JNIEXPORT jint
JNI_OnLoad(JavaVM *vm, void *reserved) {
    UnionJNIEnvToVoid uenv;
    uenv.venv = NULL;
    JNIEnv *env = NULL;

    if (vm->GetEnv(&uenv.venv, JNI_VERSION_1_4) != JNI_OK) {
        return -1;
    }
    env = uenv.env;

    if (registerNatives(env) != JNI_TRUE) {
        return -1;
    }
    LOGI("JNI_OnLoad Register Natives Methods Success!!!");
    return JNI_VERSION_1_4;
}

JNI回调java的CallJava class

//CallJava.h
//
// Created by mingzz on 2023/2/22.
//

#ifndef FFMPEG_CALLJAVA_H
#define FFMPEG_CALLJAVA_H

#include "base.h"

#define MAIN_THREAD 0
#define CHILD_THREAD 1

class CallJava {
public:
    JavaVM *javaVm;
    JNIEnv *mainThreadEnv;
    jobject globalJavaObject;

    void CallVoidMethodV(int where, const char *name, const char *sign, ...);

    jint CallIntMethodV(int where, const char *name, const char *sign, ...);

    jlong CallLongMethodV(int where, const char *name, const char *sign, ...);

    jdouble CallDoubleMethodV(int where, const char *name, const char *sign, ...);

    jfloat CallFloatMethodV(int where, const char *name, const char *sign, ...);

    jbyte CallByteMethodV(int where, const char *name, const char *sign, ...);

    jchar CallCharMethodV(int where, const char *name, const char *sign, ...);

    jshort CallShortMethodV(int where, const char *name, const char *sign, ...);

    jboolean CallBooleanMethodV(int where, const char *name, const char *sign, ...);

    jobject CallObjectMethodV(int where, const char *name, const char *sign, ...);

private:
    JNIEnv *callPrepare(int where, jmethodID *methodId, jboolean *hasThreadAttach, const char *name,
                        const char *sign) const;

    void callFinish(va_list *args, jboolean hasThreadAttach) const;
};

#endif //FFMPEG_CALLJAVA_H

//CallJava.cpp
//
// Created by mingzz on 2023/2/22.
//
#include "CallJava.h"

JNIEnv *CallJava::callPrepare(int where, jmethodID *methodId,
                              jboolean *hasThreadAttach, const char *name,
                              const char *sign) const {
    if (nullptr == javaVm || nullptr == mainThreadEnv || nullptr == globalJavaObject) {
        LOGE("should not be like this : 0 == javaVm || 0 == mainThreadEnv || 0 == mainThreadJavaObject!");
        return nullptr;
    }
    JNIEnv *env;
    *hasThreadAttach = false;
    if (MAIN_THREAD == where) {
        env = mainThreadEnv;
    } else if (CHILD_THREAD == where) {
        jint ret = javaVm->GetEnv((void **) &env, JNI_VERSION_1_6);
        LOGI("CHILD_THREAD javaVm->GetEnv : %d", ret);
        if (JNI_EDETACHED == ret) {
            ret = javaVm->AttachCurrentThread(&env, nullptr);
            if (JNI_OK == ret) {
                *hasThreadAttach = true;
            } else {
                LOGE("CHILD_THREAD AttachCurrentThread fail : %d", ret);
                return nullptr;
            }
        }
    } else {
        LOGE("the where must be MAIN_THREAD or CHILD_THREAD!");
        return nullptr;
    }
    *methodId = env->GetMethodID(
            env->GetObjectClass(globalJavaObject),
            name, sign);
    return env;
}

void CallJava::callFinish(va_list *args, jboolean hasThreadAttach) const {
    va_end(*args);
    if (hasThreadAttach) {
        javaVm->DetachCurrentThread();
    }
}

void CallJava::CallVoidMethodV(int where, const char *name, const char *sign, ...) {
    jmethodID methodId;
    jboolean hasThreadAttach;
    JNIEnv *env = callPrepare(where, &methodId, &hasThreadAttach, name, sign);
    if (nullptr == env) {
        LOGI("call fail env should not be null!");
        return;
    }
    va_list args;
    va_start(args, sign);
    env->CallVoidMethodV(globalJavaObject, methodId, args);
    callFinish(&args, hasThreadAttach);
}

jint CallJava::CallIntMethodV(int where, const char *name, const char *sign, ...) {
    jmethodID methodId;
    jboolean hasThreadAttach;
    JNIEnv *env = callPrepare(where, &methodId, &hasThreadAttach, name, sign);
    if (nullptr == env) {
        LOGI("call fail env should not be null!");
        return -1;
    }
    va_list args;
    va_start(args, sign);
    jint result = env->CallIntMethodV(globalJavaObject, methodId, args);
    callFinish(&args, hasThreadAttach);
    return result;
}

jlong CallJava::CallLongMethodV(int where, const char *name, const char *sign, ...) {
    jmethodID methodId;
    jboolean hasThreadAttach;
    JNIEnv *env = callPrepare(where, &methodId, &hasThreadAttach, name, sign);
    if (nullptr == env) {
        LOGI("call fail env should not be null!");
        return -1;
    }
    va_list args;
    va_start(args, sign);
    jlong result = env->CallLongMethodV(globalJavaObject, methodId, args);
    callFinish(&args, hasThreadAttach);
    return result;
}

jdouble CallJava::CallDoubleMethodV(int where, const char *name, const char *sign, ...) {
    jmethodID methodId;
    jboolean hasThreadAttach;
    JNIEnv *env = callPrepare(where, &methodId, &hasThreadAttach, name, sign);
    if (nullptr == env) {
        LOGI("call fail env should not be null!");
        return -1;
    }
    va_list args;
    va_start(args, sign);
    jdouble result = env->CallDoubleMethodV(globalJavaObject, methodId, args);
    callFinish(&args, hasThreadAttach);
    return result;
}

jfloat CallJava::CallFloatMethodV(int where, const char *name, const char *sign, ...) {
    jmethodID methodId;
    jboolean hasThreadAttach;
    JNIEnv *env = callPrepare(where, &methodId, &hasThreadAttach, name, sign);
    if (nullptr == env) {
        LOGI("call fail env should not be null!");
        return -1;
    }
    va_list args;
    va_start(args, sign);
    jfloat result = env->CallFloatMethodV(globalJavaObject, methodId, args);
    callFinish(&args, hasThreadAttach);
    return result;
}

jbyte CallJava::CallByteMethodV(int where, const char *name, const char *sign, ...) {
    jmethodID methodId;
    jboolean hasThreadAttach;
    JNIEnv *env = callPrepare(where, &methodId, &hasThreadAttach, name, sign);
    if (nullptr == env) {
        LOGI("call fail env should not be null!");
        return -1;
    }
    va_list args;
    va_start(args, sign);
    jbyte result = env->CallByteMethodV(globalJavaObject, methodId, args);
    callFinish(&args, hasThreadAttach);
    return result;
}

jchar CallJava::CallCharMethodV(int where, const char *name, const char *sign, ...) {
    jmethodID methodId;
    jboolean hasThreadAttach;
    JNIEnv *env = callPrepare(where, &methodId, &hasThreadAttach, name, sign);
    if (nullptr == env) {
        LOGI("call fail env should not be null!");
        return -1;
    }
    va_list args;
    va_start(args, sign);
    jchar result = env->CallCharMethodV(globalJavaObject, methodId, args);
    callFinish(&args, hasThreadAttach);
    return result;
}

jshort CallJava::CallShortMethodV(int where, const char *name, const char *sign, ...) {
    jmethodID methodId;
    jboolean hasThreadAttach;
    JNIEnv *env = callPrepare(where, &methodId, &hasThreadAttach, name, sign);
    if (nullptr == env) {
        LOGI("call fail env should not be null!");
        return -1;
    }
    va_list args;
    va_start(args, sign);
    jshort result = env->CallShortMethodV(globalJavaObject, methodId, args);
    callFinish(&args, hasThreadAttach);
    return result;
}

jboolean CallJava::CallBooleanMethodV(int where, const char *name, const char *sign, ...) {
    jmethodID methodId;
    jboolean hasThreadAttach;
    JNIEnv *env = callPrepare(where, &methodId, &hasThreadAttach, name, sign);
    if (nullptr == env) {
        LOGI("call fail env should not be null!");
        return -1;
    }
    va_list args;
    va_start(args, sign);
    jboolean result = env->CallBooleanMethodV(globalJavaObject, methodId, args);
    callFinish(&args, hasThreadAttach);
    return result;
}

jobject CallJava::CallObjectMethodV(int where, const char *name, const char *sign, ...) {
    jmethodID methodId;
    jboolean hasThreadAttach;
    JNIEnv *env = callPrepare(where, &methodId, &hasThreadAttach, name, sign);
    if (nullptr == env) {
        LOGI("call fail env should not be null!");
        return nullptr;
    }
    va_list args;
    va_start(args, sign);
    jobject result = env->CallObjectMethodV(globalJavaObject, methodId, args);
    callFinish(&args, hasThreadAttach);
    return result;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值