2024/07/07
以2021.2.1 patch2版本的Android Studio为例。
1、新建Native C++项目,在之后的选择中选C++11,之后确认,成功创建项目。
2、在main文件夹下,新建jniLibs文件夹,并将.so库放进去。注意:在生产环境中,不必将每一种cpu架构的.so库都放进来,选一种即可。
3、将inc文件夹中的头文件复制进来。
4、在Cmake中集成。
4.1include_directories导入头文件
4.2add_library生成动态库,例libvoicechange.so
#库名的规则: lib + 名称 + .so (lib .so 系统自动拼接的)
add_library( # Sets the name of the library.
voicechange #libvoicechange.so
SHARED #表示动态库 STATIC 表示静态库.静态库的格式是.a
native-lib.cpp
# ${allCPP} # 批量导入
)
4.3 生成的这个动态库libvoicechange.so,最终就可以在Java的MainActivity中来查找加载并引用了。
public class JavaMainActivity extends AppCompatActivity{
static{
System.loadLibrary("voicechange");
}
}
2024/07/28
4.4 find_library() 会查找日志库,把H:\Android\sdk\ndk\21.0.6113669\platforms\android-21\arch-arm\usr\lib中的liblog.so拿过来用,就能打印日志了。
同时还会把liblog.so赋值给变量m-lib。
# 相当于 var log-lib = log (liblog.so 库的路径)
find_library( # Sets the name of the path variable.
m-lib # 变量名
# Specifies the name of the NDK library that
# you want CMake to locate.
# 这里就相当于 定位到ndk的 liblog.so 库的路径
log #查找NDK的日志打印库
)
4.5 然后把m-lib这个变量拿到target_link_libraries()中进行链接,即链接具体库。(如果学过Linux编程的话会发现,是有链接这个过程的)。
链接之后就会生成最终的目标,即在MainActivity中引用的那个。
target_link_libraries( # Specifies the target library.
derryvoicechangeprac2
# Links the target library to the log library
# included in the NDK.
${m-lib})
4.6 链接完log库之后,再链接fmod和fmodL
5、搭建界面,编写Java代码。
6、把fmod.jar复制到libs文件夹下(不要忘了在gradle中声明jar包的引入),然后初始化:
FMOD.init(this);
同时,有init()就有close(),不要忘记在onDestroy中:
FMOD.close();
7、声明native函数:
private native void voiceChangeNative(int modeNormal, String path);
8、不使用enter键,手动生成JavaMainActivity的头文件。
8.1、 右击app\src\main\java文件夹,在openIn中选择 (Open In)Terminal,然后使用javah命令生成头文件。
Windows PowerShell
版权所有 (C) Microsoft Corporation。保留所有权利。
尝试新的跨平台 PowerShell https://aka.ms/pscore6
PS I:\AndroidProjects\202407\DerryVoiceChangePrac22\app\src\main\java> javah com.example.derryvoicechangeprac2.JavaMainActivity
错误: 编码GBK的不可映射字符
2024/07/30
9、在javah生成的头文件中引入fmod的头文件:
#include "fmod.hpp" //fmod的头文件
10、CmakeList.txt中导入头文件和库文件:
# TODO 第一步:导入头文件
include_directories(inc) # CMakeLists.txt 目录下的inc文件夹 放头文件
# TODO 第二步:导入库文件(C++的环境变量)- 最新的方法,以前是 add_xxx(已淘汰)6.0以下才可以
# 环境变量的添加是追加的方式的:%JAVA_HOME%;%ANDROID_HOME%;path1;path2;
# CMAKE_SOURCE_DIR:获得当前CmakeLists.txt的路径
# CMAKE_ANDROID_ARCH_ABI:自动获取 四大平台架构值 也可以在gradle里面进行指定
# 这个set的写法就相当于 path = path + CMAKE_CXX_FLAGS;
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -L${CMAKE_SOURCE_DIR}/../jniLibs/${CMAKE_ANDROID_ARCH_ABI}")
11、把生成的头文件移动到 app\src\main\cpp 文件夹下。这里的头文件只是一个声明,还需要继续写实现。
#include "com_example_derryvoicechangeprac2_JavaMainActivity.h"
using namespace FMOD;//命名空间
//extern "C" 表示必须采用c的标准。原因:1、因为c语言不允许函数重载,如果不写extern "C"关键字,会出现c++函数重载,就会造成混乱,出现bug(当初把一个很简单的java函数名,
// 写成这么长一串,就是为了避免重载)
// 2、JNIEnv的里面所有的函数指针,都是采用的c的标准,所以必须明确是c的定义方式extern "C"。
//JNIEXPORT表示对外暴露JNI
//JNICALL是表示这是一个jni函数的一个标记
extern "C" JNIEXPORT void JNICALL Java_com_example_derryvoicechangeprac2_JavaMainActivity_voiceChangeNative
(JNIEnv * env, jobject thiz, jint mode, jstring path){
char * content = "默认,播放完毕";//相当于Java的字符串String
//Java是万物皆对象,c是万物皆指针
//把 JNIEnv里的几百个函数指针玩透,jni就过关了
//jstring转换成char*
const char* path_ = env->GetStringUTFChars(path, NULL);
System * system = 0;//fmod音效引擎系统
Sound * sound = 0;//fmod声音
Channel * channel = 0;//通道 音轨
DSP * dsp = 0;//数字信号处理(digital signal process)
//第一步 创建系统。执行完后,system就有丰富的值了
System_Create(&system); //因为参数是二级指针,所以要用&再次取出指针所对应的地址
//第二步 系统的初始化,32是最大的音轨数
system->init(32,FMOD_INIT_NORMAL,0);
//第三步 创建声音,最重要的环节是把声音在音轨上播放,需要把声音初始化,如果声音是空指针肯定不行
system->createSound(path_, FMOD_DEFAULT, 0, &sound);
//第四步 播放声音
system->playSound(sound, 0, false, &channel);
bool isPlay = true;
while(isPlay){
channel->isPlaying(&isPlay);//只有通道最清楚有没有播放完成,如果通道播放完成,会自动修改isPlay地址所对应的
// 值为false,跳出循环,走到后面释放资源的步骤
usleep(1000*30); // 休眠30毫秒
}
// 好习惯:释放资源
dsp->release();
sound->release();
system->close();
system->release();
env->ReleaseStringUTFChars(path, path_);
}
12、build.gradle中
externalNativeBuild{
cmake{
// cppFlags "" // 这样写,默认是支持四大平台
// 指定CPU架构是armeabi-v7a
// 指定CMakeLists.txt里面的环境变量CMAKE_ANDROID_ARCH_ABI的值
// 【注意:这里只指定本地库到armeabi-v7a, x86】 这句代码 还不能决定apk生成
abiFilters ("armeabi-v7a", "x86","x86_64")
}
// TODO 第五步:指定CPU的架构 apk/lib/平台
// 下面代码不写,默认是四大CPU架构平台
ndk{
// 指定CPU架构是armeabi-v7a, x86
// 【注意:这里只指定编译所有库到armeabi-v7a, x86 进apk】
abiFilters ("armeabi-v7a", "x86","x86_64")
}
}