一,Ndk开发【基础】

1.1-基本概念

Android SDK【Java层】

  Android SDK全称是:Android Software Development Kit【Android 软件开发工具包】,Android SDK主要为开发者提供Java层的API调用以及开发过程中所需要的一些构建工具和其它工具;所以说你可以把Android SDK理解成就是Android应用的Java层开发【执行环境:虚拟机】
在这里插入图片描述
在这里插入图片描述

Android NDK【C/C++层】

  Android NDK全称是:Android Native Development Kit【Android 本地开发工具包】,Android NDK主要为开发者提供C/C++层的API接口以及开发过程中所需要的一些构建工具和其它工具,NDK是SDK的一部分;所以说你可以把Android NDK理解成就是Android应用的C/C++底层库开发执行环境:操作系统
在这里插入图片描述
在这里插入图片描述

Android JNI【Java-桥梁-C/C++】

  Android JNI全称:Android native interface【Android 本地接口】,Android JNI主要是为上层Java/Kotlin与本地C/C++ 提供互通互调机制简单点说: Java代码中可以调用Native C/C++代码,Native C/C++代码可以调用上层Java代码;所以说Android JNI就是连接上层Java与本地C/C++互通互调的一个桥梁和机制!
  JNI【最先出来】最早出现在JDK中,Android NDK【后来出来】这个开发工具集集成了JNI!
在这里插入图片描述

在这里插入图片描述

1.2-环境配置

  • 安装Android Studio
    https://developer.android.google.cn/studio 下载地址
    https://developer.android.google.cn/studio/intro/ 帮助文档

  • 创建NDK项目
    在这里插入图片描述

  • 配置NDK
    在这里插入图片描述
    手动配置: https://developer.android.google.cn/ndk/downloads

1.3-JNI基本语法

声明本地方法【Java层:Native方法】

//Native方法-》目的-》调用本地C/C++方法!
public native String stringFromJNI();

实现本地方法【C++层:C++方法】

/*
 * extern "C" 表示:以C语言的方式导出【保持函数名不变,如果是C++,函数名会发生变化】
 * JNIEXPORT 导出【暴露给外部使用】
 * JNICALL 调用约定【stdcall,fastcall,ccall等一系列,调用约定会决定入栈顺序和释放的问题】
 * */
extern "C" JNIEXPORT jstring JNICALL
Java_com_example_ndkdemo_MainActivity_stringFromJNI(
        JNIEnv* env,
        jobject /* this */) {
    std::string hello = "Hello from C++";
    return env->NewStringUTF(hello.c_str());
}

JNI静态注册

Java层: Native声明-》对应一种固定格式-》C++层: C++方法【Java_包名_Java类名_方法名】
注意点: 如果包名中包含_,那么就会给下划线加上一个_1用来标识这是包名自带下划线,不是规则中的下划线!

JNI核心元素

JavaVM

  JavaVM是虚拟机在JNI层的表示,一个进程只有JVM,所有线程共用JavaVM;

JNIEnv

  JNIEnv它是一个与线程相关的用来代表java运行环境的,用来进行java-native互调的一个结构体,内部包含了一个JNI本地接口指针【JNINativeInterface* functions】,这个指针又指向了函数指针数组【定义了JNI相关的函数指针】;
  同一个线程调用本地方法,JNIEnv是同一个,不同线程调用native方法,JNIEnv不相同【java可以在不同线程中调用】

重点说明JNIEnv在C和C++中使用的区别之处:

  • 定义不同
#if defined(__cplusplus)
//C++对JNINativeInterface*进行了二次封装-》_JNIEnv 结构体
typedef _JNIEnv JNIEnv;
/*
struct _JNIEnv {
    /* do not rename this; it does not seem to be entirely opaque */
    const struct JNINativeInterface* functions;
*/
typedef _JavaVM JavaVM;
#else
//C语言直接使用的就是JNINativeInterface*
typedef const struct JNINativeInterface* JNIEnv;
typedef const struct JNIInvokeInterface* JavaVM;
#endif
  • 调用不同
JNIEnv *env
C++中:JNIEnv-》结构体【JNINativeInterface*】
		JNIEnv *env ===  _JNIEnv * 【_JNIEnv一级指针】
C中:JNIEnv-》指针【JNINativeInterface* JNIEnv】
		JNIEnv *env === JNINativeInterface**【JNINativeInterface二级指针】

jobject

  Native方法中传递过来的jobject其实就是一个java实例引用,实例就是一个具体的对象,这个native方法是一个具体的java对象调用的!

jclass

  Native方法中传递过来的jclass就是一个java类引用,这个native方法是一个静态类方法,jclass其实这个类引用!

JNI数据类型

基本类型

JNI基本类型,可以直接拿来使用,本质还是C/C++的基本数据类型,只不过使用typedef定义了而已!
在这里插入图片描述

引用类型

引用类型不能直接使用,必须通过JNIEnv中的JNI函数转换后,才能够使用!
在这里插入图片描述
在这里插入图片描述

类型描述

在这里插入图片描述

JNI基本操作

声明native函数:

    public native int senderBaseTypeToJNI(
            boolean boolValue,
            byte byteValue,
            char charValue,
            short shortValue,
            int intValue,
            long longValue,
            float floatValue,
            double doubleValue
    );

调用native函数:

        boolean boolValue = false;
        byte byteValue = 10;
        char charValue = 'a';
        short shortValue = 100;
        int intValue = 200;
        long longValue = 300;
        float floatValue = 3.14f;
        double doubleValue = 5.12;
        Log.d("JavaLog", "onCreate: senderBaseTypeToJNI");
        int iRet = senderBaseTypeToJNI(
                boolValue,
                byteValue,
                charValue,
                shortValue,
                intValue,
                longValue,
                floatValue,
                doubleValue
        );
        Log.d("JavaLog", "onCreate: iRet=" + iRet);

实现native函数:

extern "C"
JNIEXPORT jint JNICALL
Java_com_example_ndk_1study_1base_MainActivity_senderBaseTypeToJNI(
        JNIEnv *env, jobject thiz,
        jboolean bool_value,
        jbyte byte_value,
        jchar char_value,
        jshort short_value,
        jint int_value,
        jlong long_value,
        jfloat float_value,
        jdouble double_value) {
    //Java基本类型:
//    public native void senderBaseTypeToJNI(
//            boolean boolValue,
//            byte byteValue,
//            char charValue,
//            short shortValue,
//            int intValue,
//            long longValue,
//            float floatValue,
//            double doubleValue
//    );
    //JNI 基本类型:
//    typedef uint8_t  jboolean; /* unsigned 8 bits */
//    typedef int8_t   jbyte;    /* signed 8 bits */
//    typedef uint16_t jchar;    /* unsigned 16 bits */
//    typedef int16_t  jshort;   /* signed 16 bits */
//    typedef int32_t  jint;     /* signed 32 bits */
//    typedef int64_t  jlong;    /* signed 64 bits */
//    typedef float    jfloat;   /* 32-bit IEEE 754 */
//    typedef double   jdouble;  /* 64-bit IEEE 754 */

    //一,每个Java基本类型都对应一个j前缀修饰的JNI基本类型
        //例子:int -> jint
    LOGD("boolValue=%d,"
         "byteValue=%d,"
         "charValue=%c,"
         "intValue=%d,"
         "longValue=%ld,"
         "floatValue=%f,"
         "doubleValue=%f\n",
         bool_value,
         byte_value,
         char_value,
         int_value,
         long_value,
         float_value,
         double_value)
    //二,JNI基本类型本质:C/C++基本类型
    //结论:JNI基本类型,可以直接使用!
    int iNum = int_value;
    float fNum = float_value;
    LOGD("\niNum=%d\n,fNum=%f",iNum,fNum);
    return iNum;
}

JNI数组操作

jintArray

声明native函数:

    public native void senderArrayToJNI(int[] ary);
    public native int[] newArrayFromJNI();

调用native函数:

        int[] ary = {1,2,3,4,5};
        senderArrayToJNI(ary);
        for (int num: ary) {
            Log.d("JavaLog", "onCreate:print_array1 num=" + num);
        }

        int[] newAry = newArrayFromJNI();
        for (int num: newAry) {
            Log.d("JavaLog", "onCreate:print_array2 num=" + num);
        }

实现native函数:

extern "C"
JNIEXPORT void JNICALL
Java_com_example_ndk_1study_1base_MainActivity_senderArrayToJNI(
        JNIEnv *env,
        jobject thiz,
        jintArray jary) {
    //一,通过env获取数组长度【jsize->本质->jint->int】
    jsize jaryCount = env->GetArrayLength(jary);
    //二,获取数组指针,不拷贝
    jint* pJary = env->GetIntArrayElements(jary,NULL);
    //三,访问修改打印数组元素
    for(int i=0;i<jaryCount;i++){
        LOGD("before update the value : index=%d,value=%d",i,*(pJary + i));
        *(pJary + i) = *(pJary + i) + 10000;
        LOGD("after update the value : index=%d,value=%d",i,*(pJary + i));
    }
    //四,释放数组指针,并将C++数组的修改通过env同步至JVM java数组
        //1.pJary必须为数组初始位置,如果修改了,需要回到数组头
        //2.标记
            //JNI_OK【同步C++修改至JVM,并释放C++层数组】
            //JNI_COMMIT【同步C++修改至JVM,不释放C++层数组】
            //JNI_ABORT【释放C++层数组】
    env->ReleaseIntArrayElements(jary,pJary,JNI_OK);
}

extern "C"
JNIEXPORT jintArray JNICALL
Java_com_example_ndk_1study_1base_MainActivity_newArrayFromJNI(
        JNIEnv *env,
        jobject thiz) {
    //一,指定长度
    jsize jaryCount = 5;
    //二,创建数组
    jintArray jary = env->NewIntArray(jaryCount);
    //三,获取数组指针
    jint* pJary =  env->GetIntArrayElements(jary,NULL);
    for(int i=0;i<jaryCount;i++){
        *(pJary + i) = *(pJary + i) + 20000 + i;
    }
    //四,同步,并释放指针
        //不同步,返回的jary,只有默认初始值,没有修改后的元素数据
    env->ReleaseIntArrayElements(jary,pJary,JNI_OK);
    return jary;
}

jobjectArray

声明native函数:

    public native void senderStrArrayToJNI(String[] strs);
    public native String[] newStrArrayToJNI();

调用native函数:

        String[] strAry = {"wawa","dada","gaga"};
        senderStrArrayToJNI(strAry);
        for (String str: strAry) {
            Log.d("JavaLog", "onCreate:print_array3 str=" + str);
        }

        String[] newStrAry = newStrArrayFromJNI();
        for (String str: newStrAry) {
            Log.d("JavaLog", "onCreate:print_array4 str=" + str);
        }

实现native函数:

extern "C"
JNIEXPORT void JNICALL
Java_com_example_ndk_1study_1base_MainActivity_senderStrArrayToJNI(
        JNIEnv *env,
        jobject thiz,
        jobjectArray jStrs) {
    //一,获取数组长度
    jsize jStrsCount = env->GetArrayLength(jStrs);
    //二,遍历数组元素
    for (int i = 0; i < jStrsCount; i++) {
        //获取对应index的字符串元素
        jstring jstr = (jstring)env->GetObjectArrayElement(jStrs,i);
        const char* strc = env->GetStringUTFChars(jstr,NULL);
        LOGD("修改前: index=%d,strValue=%s",i,strc);
        env->ReleaseStringUTFChars(jstr,strc);

        jstring jstrValue = env->NewStringUTF("lalala");
        env->SetObjectArrayElement(jStrs,i,jstrValue);
        env->DeleteLocalRef(jstrValue);

        jstring jstrUpdate = (jstring)env->GetObjectArrayElement(jStrs,i);
        const char* strcUpdate = env->GetStringUTFChars(jstrUpdate,NULL);
        LOGD("修改后: index=%d,strValue=%s",i,strcUpdate);
        env->ReleaseStringUTFChars(jstrUpdate,strcUpdate);
    }
}

extern "C"
JNIEXPORT jobjectArray JNICALL
Java_com_example_ndk_1study_1base_MainActivity_newStrArrayFromJNI(
        JNIEnv *env,
        jobject thiz) {
    //一,声明数组长度
    jsize jAryCount = 5;
    //二,查找String类
    jclass jstrCls = env->FindClass("java/lang/String");
    //三,创建字符串jstring,作为初始默认对象
    jstring jstr = env->NewStringUTF("auto_value");
    //四,创建一个object数组
    jobjectArray jstrAry = env->NewObjectArray(jAryCount,jstrCls,jstr);
    //释放jstr本地引用
    env->DeleteLocalRef(jstr);
    //释放jstrcls本地引用
    env->DeleteLocalRef(jstrCls);

    for (int i = 0; i < jAryCount; i++) {
        //获取对应index的字符串元素
        jstring jstr = (jstring)env->GetObjectArrayElement(jstrAry,i);
        const char* strc = env->GetStringUTFChars(jstr,NULL);
        LOGD("默认值: index=%d,strValue=%s",i,strc);
        env->ReleaseStringUTFChars(jstr,strc);

        jstring jstrValue = env->NewStringUTF("xixixi");
        env->SetObjectArrayElement(jstrAry,i,jstrValue);
        env->DeleteLocalRef(jstrValue);

        jstring jstrUpdate = (jstring)env->GetObjectArrayElement(jstrAry,i);
        const char* strcUpdate = env->GetStringUTFChars(jstrUpdate,NULL);
        LOGD("修改值: index=%d,strValue=%s",i,strcUpdate);
        env->ReleaseStringUTFChars(jstrUpdate,strcUpdate);
    }
    return jstrAry;
}

JNI对象操作

JNI操作对象

package com.example.ndk_study_base;

import android.util.Log;

public class Student {

    private String name;
    private int age;
    private char sex;

    public String getName() {
        Log.d("Student", "getName:run ");
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public char getSex() {
        return sex;
    }

    public void setSex(char sex) {
        this.sex = sex;
    }

    @Override
    public String toString() {
        return "com.example.ndk_study_base.Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", sex=" + sex +
                '}';
    }
}

声明Native函数:

    public native void senderStudentToJNI(Student stu,String name);
    public native Student newStudentFromJNI();

调用Native函数:

        Student stu = new Student();
        stu.setName("娃哈哈");
        stu.setAge(18);
        stu.setSex('M');
        senderStudentToJNI(stu,"奥利给给");
        stu.getName();

        Student stu2 = newStudentFromJNI();
        Log.d("JavaLog", "RunJNIObjectCode: " + stu2.toString());

实现Native函数:

extern "C"
JNIEXPORT void JNICALL
Java_com_example_ndk_1study_1base_MainActivity_senderStudentToJNI(
        JNIEnv *env,
        jobject thiz,
        jobject stu,
        jstring name) {
    //一,修改Student name属性
    //1.获取类引用
    //方式1:获取jclass
    jclass stuCls = env->GetObjectClass(stu);
    //2.获取属性id
    jfieldID stuNameFieldId = env->GetFieldID(stuCls,"name", "Ljava/lang/String;");
    //3.获取属性
    jstring jstrName = (jstring)env->GetObjectField(stu,stuNameFieldId);
    //获取
    const char* cstrName = env->GetStringUTFChars(jstrName,NULL);
    //打印
    LOGD("student name=%s",cstrName);
    //释放
    env->ReleaseStringUTFChars(jstrName,cstrName);
    //4.修改属性
    env->SetObjectField(stu,stuNameFieldId,name);
    //二,调用Student func方法
    jmethodID getNameMethodId = env->GetMethodID(stuCls,"getName", "()Ljava/lang/String;");
    jstring jstrRet = (jstring)env->CallObjectMethod(stu,getNameMethodId);//调用返回类型是object的方法API
    //获取
    const char* cstrNameRet = env->GetStringUTFChars(jstrRet,NULL);
    //打印
    LOGD("student cstrNameRet=%s",cstrNameRet);
    //释放
    env->ReleaseStringUTFChars(jstrRet,cstrNameRet);

    env->DeleteLocalRef(stuCls);
}

extern "C"
JNIEXPORT jobject JNICALL
Java_com_example_ndk_1study_1base_MainActivity_newStudentFromJNI(
        JNIEnv *env,
        jobject thiz) {
    //方式2:获取jclass
    jclass stuCls = env->FindClass("com/example/ndk_study_base/Student");
    //开辟对象控件
    jobject jstuObj = env->AllocObject(stuCls);
    //获取方法id
    jmethodID setNameMethodId = env->GetMethodID(stuCls,"setName", "(Ljava/lang/String;)V");
    jmethodID setAgeMethodId = env->GetMethodID(stuCls,"setAge", "(I)V");
    jmethodID setSexMethodId = env->GetMethodID(stuCls,"setSex", "(C)V");
    //参数-调用-释放
    jstring jstrName = env->NewStringUTF("JNIStudentLala");
    env->CallVoidMethod(jstuObj,setNameMethodId,jstrName);//调用返回值void的API
    env->DeleteLocalRef(jstrName);

    env->CallVoidMethod(jstuObj,setAgeMethodId,99);
    env->CallVoidMethod(jstuObj,setSexMethodId,'M');

    env->DeleteLocalRef(stuCls);
    return jstuObj;
}

JNI字符串

在这里插入图片描述

JNI引用问题

  • 局部引用
    Native方法作用域中创建的引用对象,返回的引用被称之为局部引用,只能在本Native方法以及调用Native方法的范围内使用,执行完毕离开作用域会被自动释放,没有被释放前,是不能被垃圾回收的,不能保存局部引用至全局【内存异常或系统崩溃】!
    **局部引用手动释放:**DeleteLocalRef
  • 全局引用
    NewGlobalRef唯一创建全局引用API【局部引用的区别】
    DeleteGlobalRef 释放全局引用
    都是需要手动创建和释放!

JNI动态注册

Java层: native方法声明

    public native void DynamicFromJNI();
    public native int DynamicIntFromJNI(int nums);
    public native String DynamicStrFromJNI();

C++层: JNI方法实现

extern "C" JNIEXPORT void DynamicFuncCall(){
    LOGD("DynamicFuncCall successes!");
}

extern "C" JNIEXPORT int DynamicFuncIntCall(jint nums){
    LOGD("DynamicFuncIntCall successes!");
    return 200;
}

extern "C" JNIEXPORT jstring DynamicFuncStrCall(
        JNIEnv* env,
        jobject obj){
    LOGD("DynamicFuncStrCall successes!");

    jstring jStr = env->NewStringUTF("哈哈");

    return jStr;
}

桥梁打通Native-》JNI方法:
加载库:Java
static {
System.loadLibrary(“ndk_study_dynamic”);
}
自动调:JNI
extern “C” JNIEXPORT jint JNI_OnLoad(JavaVM* javaVm,void* param)

....
// 日志输出
#include <android/log.h>
#define TAG "NDKLog"
// __VA_ARGS__ 代表...的可变参数
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, TAG, __VA_ARGS__);
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, TAG, __VA_ARGS__);
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, TAG, __VA_ARGS__);

JavaVM* jvm;

extern "C" JNIEXPORT void DynamicFuncCall(){
    LOGD("DynamicFuncCall successes!");
}

extern "C" JNIEXPORT int DynamicFuncIntCall(jint nums){
    LOGD("DynamicFuncIntCall successes!");
    return 200;
}

extern "C" JNIEXPORT jstring DynamicFuncStrCall(
        JNIEnv* env,
        jobject obj){
    LOGD("DynamicFuncStrCall successes!");

    jstring jStr = env->NewStringUTF("哈哈");

    return jStr;
}

//    typedef struct {
//        const char* name;
//        const char* signature;
//        void*       fnPtr;
//    } JNINativeMethod;

//(void*)DynamicFuncCall void不是返回值类型,就是一个函数强转void*指针类型
	//你写:int*,char*,void*都是可以赋值给void,但是直接将函数赋值给void*是不可以的,需要指针类型强制转换!
//void* pVoid = (void*)DynamicFuncIntCall;
//将Java Native方法和JNI方法进行绑定:
static const JNINativeMethod jniNativeMethods[] = {
        {"DynamicFromJNI","()V",(void*)DynamicFuncCall},
        {"DynamicIntFromJNI","(I)I",(void*)DynamicFuncIntCall},
        {"DynamicStrFromJNI","()Ljava/lang/String;",(void*)DynamicFuncStrCall}
};

extern "C" JNIEXPORT jint JNI_OnLoad(JavaVM* javaVm,void* param){
	//保存:jvm指针
    ::jvm = javaVm;
	//获取JNIEnv
    JNIEnv* env = nullptr;
    jint jRet = javaVm->GetEnv(reinterpret_cast<void **>(&env), JNI_VERSION_1_6);
    if(jRet != JNI_OK){
        return -1;
    }

    LOGD("JNI_OnLoad init successes!");
    //注册绑定
//    jint RegisterNatives(jclass clazz, const JNINativeMethod* methods,
//                         jint nMethods)
    jclass mainActiveCls = env->FindClass("com/example/ndk_study_dynamic/MainActivity");
    env->RegisterNatives(mainActiveCls,jniNativeMethods,sizeof(jniNativeMethods)/sizeof(JNINativeMethod));
	//返回版本
    return JNI_VERSION_1_6;
}

总结:

  • 静态绑定优缺点
    • 方便使用,快速生成
    • 名称过长,暴露信息
    • 效率低于动态绑定一丢对
  • 动态绑定优缺点
    • JNI方法名可以任意起,只要进行绑定注册即可
    • 一开始进行所有JNI方法的注册

1.4-Ndk导库流程【第三方库】

CPU架构:

指令派系:
  • 精简指令集【RISC】
    Intel,AMD
  • 复杂指令集【CISC】
    IBM,ARM
常见架构:

x86,x86-64/x64
arm64-v8a,armeabi-v7a【32位】,armeabi【32位】

API-ABI:
  • API
    API【Application Program Interface 应用程序接口】其实就是一套预先定义的,无需了解内部的实现机理和细节,与硬件系统无关的一套调用接口!
  • ABI
    ABI【Application Binary Interface 应用程序二进制接口】定义了一套二进制规则,它允许在所有兼容该ABI编译的目标代码的操作系统和硬件系统中,无须改动即可运行,从这里我们就看出,ABI其实就是一套约束底层,系统,硬件,编译规则的一套二进制接口!
    【EABI : 是 arm 对于 ABI规范的较新(2005年)的实现】

配置支持ABI

大厂适配ABI文章:https://mp.weixin.qq.com/s/jnZpgaRFQT5ULk9tHWMAGg

module模块: build.gradle

    defaultConfig {
        applicationId "com.example.ndkdemo"
        minSdk 21
        targetSdk 31
        versionCode 1
        versionName "1.0"

        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
        externalNativeBuild {
            cmake {
                cppFlags ''
//                方法1:externalNativeBuild-》cmake:
//                abiFilters 'armeabi-v7a'
            }
        }
//        方法2:defaultConfig-》ndk:
        ndk{
            abiFilters 'armeabi-v7a','arm64-v8a','x86'
        }
    }

配置输出目录

默认输出目录:
在这里插入图片描述

#不同Android Studio版本有的生效有的不生效
#方式1:
#set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/../jniLibs/${CMAKE_ANDROID_ARCH_ABI})
#方式2:
#set(LIBRARY_OUTPUT_PATH ${CMAKE_CURRENT_SOURCE_DIR}/../jniLibs/${CMAKE_ANDROID_ARCH_ABI})

引用外部库流程:

cmake_minimum_required(VERSION 3.10.2) #cmake 最低版本号

# Declares and names the project.

project("ndk_study_fmod") #项目工程名

#以音频音效库fmod作为Demo介绍导库流程:

#一,导入头文件所在目录
#解释:include_directories 默认路径:CMakeLists所在目录,inc与它同级所以相对目录可以直接写
include_directories(inc) #inc我们放在了CMakeLists同级目录下
#二,导入库文件所在目录【将库文件所在目录配置到环境变量中,找库会自动去环境变量中配置的目录中寻找】
#解释:set 赋值【环境变量赋值】
#先获取环境变量中已有的目录,拼接上我们的目录,重新赋值给环境变量
#fmod库文件:放在了与cpp同级的jniLibs目录下:
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -L${CMAKE_CURRENT_SOURCE_DIR}/../jniLibs/${CMAKE_ANDROID_ARCH_ABI}") 

#定义一个变量allCpp 代表所有的.c,.h,.cpp
file(GLOB allCpp *.c *.h *.cpp)

add_library( # Sets the name of the library.
        ndk_study_fmod #库名称 libndk_study_fmod.so

        # Sets the library as a shared library.
        SHARED #库类型 -> .a 还是 .so

        # Provides a relative path to your source file(s).
        #native-lib.cpp #源文件 通用写法:
        ${allCpp}
        )

#查找库 并起一个别名 目的:查找一次,缓存下来,避免反复查找
find_library( # Sets the name of the path variable.
        log-lib
        
        log)

#链接库:环境变量中配置的目录中,按照libxxx.so libxxx.a去找对应库,链接到一起
target_link_libraries( # Specifies the target library.
        ndk_study_fmod 

        xxx库 #只需要写库名称,不需要写lib,.so前后缀,它会自动添加匹配对应的库
        
        # Links the target library to the log library
        # included in the NDK.
        ${log-lib})

#总结:导入库流程
#一,项目工程添加头文件,添加库文件
    #【.jar放在libs里面,动态库放在cpp同级目录,创建一个jniLibs目录,这个目录是gradle默认寻找的目录】
#二,设置头文件所在目录
#三,设置库文件所在目录-》环境变量-》追加方式不是覆盖
#四,链接库-》只写库名称省略前缀lib和后缀库类型.so
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值