Android平台实践细节
推荐实践路线
- application: 推荐手动建立工程,方便团队里Android工程师使用
- sdk: 手工创建工程,并通过build.gradle引用cmake,方便application工程师开箱即用
- c++: 使用cmake管理,并通sdk层的CMakeLists.txt引用
Java怎么创建库Module
Android平台的Wrapper与通过CMake generate 出来的Xcode不同,需要自己手工建立
最重要的逻辑区别在于AndroidStudio自己调用CMakeLists.txt本身,过程分为
- 创建空的Activity,增加 AndroidLibrary Module
- 对AndroidLibrary 添加CMake的配置文件
- 下载AndroidStudio读取C++必须的工具NDK、Ninjia
上述步骤可以按照Google官方文档的指导方案配置
Android配置中常见错误
- 现象:提示无Ninjia
1 | CMake was unable to find a build program corresponding to "Ninja". CMAKE_MAKE_PROGRAM is not set. |
解决方案:通过Homebrew 或者 AndroidStudio安装Ninjia工具,Ninjia是一个Google的编译工具
- 现象:Sync不成功,提示找不到SDK
1 | ERROR: Unable to resolve dependency for ':app@debug/compileClasspath': Failed to transform artifact 'sdk.aar (project :sdk)' to match attributes {artifactType=jar}. |
解决方案: App使用了错误的build.gradle
setting.gradle应该加载正确的工程路径
1 | include ':app', ':sdk' |
build.gradle 需要使用正确的依赖方式
1 | dependencies { |
- 现象:AndroidStudio未能加载CPP文件
解决方案:在安装Ninjia之后,点击”Build-Refresh Link C++ Project”
JNI中的Env问题
JNI接口中有一个最重要的概念是JNIEnv,作为每一个方法的第一个参数代表着Java环境的全部上下文
根据JNI官方文档原文指出
- 每一次Java虚拟机调用JNI接口,保证同一个线程传入同一个地址的JNIEnv
1 | Native methods receive the JNI interface pointer as an argument. The VM is guaranteed to pass the same interface pointer to a native method when it makes multiple calls to the native method from the same Java thread. |
- JNIEnv和线程强相关,不能够在一个线程访问另一个线程的JNIEnv
1 | The JNI interface pointer is only valid in the current thread. A native method, therefore, must not pass the interface pointer from one thread to another. |
根据以上描述,如果开发者选择在C++开启一个线程,并且使用lambda进行回调的话,则需要注意
- 两个Scope中的JNIEnv并不一致
- 一般使用静态Map,以线程ID为Key值储存JNIEnv的指针
- C++线程需要手动Attach到Java虚拟机上
1 | //保留Java虚拟机 |