在Android Studio 2.2开始的Android Gradle Plugin版本中,Google集成了对cmake的完美支持,而原先的ndkBuild的方式支持也变得更加良好。这篇文章就来说说Android Gradle Plugin与交叉编译之间的一些事,即externalNativeBuild相关的task,主要是解读一下gradle构建系统相关的源码。
分类:Android
Android是一种基于Linux的自由及开放源代码的操作系统,主要使用于移动设备,如智能手机和平板电脑,由Google公司和开放手机联盟领导及开发。
Overriding a default option(…) value in CMake from a parent CMakeLists.txt
子 CMakeLists.txt
1 2 3 4 5 |
option(BUILD_FOR_ANDROID "Build For Android" OFF) ? if(SYSTEM.Android AND NOT BUILD_FOR_ANDROID) ????set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${NATIVE_LIBRARY_OUTPUT}/${ANDROID_ABI}) endif() |
父 CMakeLists.txt
1 2 |
set(BUILD_FOR_ANDROID ON) add_subdirectory(${CHILD_ROOT_DIR}/ ${CMAKE_CURRENT_SOURCE_DIR}/build) |
执行如下命令的时候:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
/Users/xxxx/Library/Android/sdk/cmake/3.6.4111459/bin/cmake --trace-expand \ -H/Users/xxxx/Source/example/demo/android/app \ -B/Users/xxxx/Source/example/demo/android/app/.externalNativeBuild/cmake/debug/arm64-v8a \ -DANDROID_ABI=arm64-v8a \ -DANDROID_PLATFORM=android-21 \ -DCMAKE_LIBRARY_OUTPUT_DIRECTORY=/Users/xxxx/Source/example/demo/android/app/build/intermediates/cmake/debug/obj/arm64-v8a \ -DCMAKE_BUILD_TYPE=Debug \ -DANDROID_NDK=/Users/xxxx/Library/Android/android-ndk-r16b \ -DCMAKE_TOOLCHAIN_FILE=/Users/xxxx/Library/Android/android-ndk-r16b/build/cmake/android.toolchain.cmake \ -DCMAKE_MAKE_PROGRAM=/Users/xxxx/Library/Android/sdk/cmake/3.6.4111459/bin/ninja \ -G"Android Gradle - Ninja" \ -DANDROID_ARM_NEON=TRUE \ -DANDROID_TOOLCHAIN=gcc \ -DANDROID_PLATFORM=android-21 \ -DANDROID_STL=gnustl_shared |
会观察到生成的配置文件中 BUILD_FOR_ANDROID
不一定能生效。
需要如下配置才行:
父 CMakeLists.txt
1 2 |
set(BUILD_FOR_ANDROID ON CACHE BOOL "" FORCE) add_subdirectory(${CHILD_ROOT_DIR}/ ${CMAKE_CURRENT_SOURCE_DIR}/build) |
参考链接
Use ccache with CMake for faster compilation
C and C++ compilers aren’t the fastest pieces of software out there and there’s no lack of programmer jokes based on tedium of waiting for their work to complete.
There are ways to fix the pain though - one of them is ccache曾道人吉数. CCache improves compilation times by caching previously built object files in private cache and reusing them when you’re recompiling same objects with same parameters. Obviously it will not help if you’re compiling the code for the first time and it also won’t help if you often change compilation flags. Most C/C++ development however involves recompiling same object files with the same parameters and ccache helps alot.
For illustration, here’s the comparison of first and subsequent compilation times of a largish C++ project:
Original run with empty cache:
1 2 3 4 5 |
$ make -j9 ... real????0m56.684s user????5m31.996s sys???? 0m41.638s |
Recompilation with warm cache:
1 2 3 4 5 |
$ make -j9 ... real????0m5.929s user????0m11.896s sys???? 0m8.722s |
Installation
CCache is available in repositories on pretty much all distributions. On OS X use homebrew:
1 |
$ brew install ccache |
and on Debian-based distros use apt:
1 |
$ apt-get install ccache |
CMake configuration
After ccache is installed, you need to tell CMake to use it as a wrapper for the compiler. Add these lines to your CMakeLists.txt
:
1 2 3 4 5 6 |
# Configure CCache if available find_program(CCACHE_FOUND ccache) if(CCACHE_FOUND) ????????set_property(GLOBAL PROPERTY RULE_LAUNCH_COMPILE ccache) ????????set_property(GLOBAL PROPERTY RULE_LAUNCH_LINK ccache) endif(CCACHE_FOUND) |
Rerun cmake
and next make
should use ccache for wrapper.
Usage with Android NDK
CCache can even be used on Android NDK - you just need to export NDK_CCACHE
environment variable with path to ccache binary. ndk-build
script will automatically use it. E.g.
1 2 3 |
$ export NDK_CCACHE=/usr/local/bin/ccache ? $ ndk-build -j9 |
(Note that on Debian/Ubuntu the path will probably be /usr/bin/ccache
)
CCache statistics
To see if ccache is really working, you can use ccache -s
command, which will display ccache statistics:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
cache directory???????????????????? /Users/jernej/.ccache primary config??????????????????????/Users/jernej/.ccache/ccache.conf secondary config??????(readonly)????/usr/local/Cellar/ccache/3.2.2/etc/ccache.conf cache hit (direct)???????????????? 77826 cache hit (preprocessed)?????????? 17603 cache miss???????????????????????? 46999 called for link?????????????????????? 18 compile failed????????????????????????45 ccache internal error??????????????????1 preprocessor error????????????????????62 unsupported source language??????????204 files in cache???????????????????? 48189 cache size?????????????????????????? 1.2 GB max cache size??????????????????????20.0 GB |
On second and all subsequent compilations the “cache hit” values should increase and thus show that ccache is working.
参考链接
Toast问题深度剖析
题记
Toast
作为 Android
系统中最常用的类之一,由于其方便的api设计和简洁的交互体验,被我们所广泛采用。但是,伴随着我们开发的深入,Toast
的问题也逐渐暴露出来。本文章就将解释 Toast
这些问题产生的具体原因。 本系列文章将分成两篇:
- 第一篇,我们将分析
Toast
所带来的问题 - 第二篇,将提供解决
Toast
问题的解决方案
(注:本文源码基于Android 7.0)
1. 异常和偶尔不显示的问题
当你在程序中调用了 Toast
的 API
,你可能会在后台看到类似这样的 Toast
执行异常:
1 2 3 4 5 6 |
android.view.WindowManager$BadTokenException ????Unable to add window -- token android.os.BinderProxy@7f652b2 is not valid; is your activity running? ????android.view.ViewRootImpl.setView(ViewRootImpl.java:826) ????android.view.WindowManagerGlobal.addView(WindowManagerGlobal.java:369) ????android.view.WindowManagerImpl.addView(WindowManagerImpl.java:94) ????android.widget.Toast$TN.handleShow(Toast.java:459) |
另外,在某些系统上,你没有看到什么异常,却会出现 Toast
无法正常展示的问题。为了解释上面这些问题产生的原因,我们需要先读一遍 Toast
的源码。
macOS Mojave (10.14.3) Android Studio 3.3.1 NDK 19.1.5304403 导入并构建Vuh项目
以前在 Android Studio 3.2.1上vuh库使用的例子 中实现了一个使用 vuh
?库的例子。 那个例子中的 vuh
?库是我们编译好 libvuh.so
?之后直接引用的,我们下面实现通过直接编译代码实现整合。
尝试过使用 ExternalProject_add
跟 include
的方式包含 vuh
?库,但是都不是很成功。
其中 ExternalProject_add
导入的项目只能编译一次,即使指定 BUILD_ALWAYS 1
也没用,这个应该是 Ninja
导致的问题,导致当出现多个 ABI
?或者 vuh
?库代码变动之后,不能重新编译,出现各种编译错误。
使用 include
包含的项目会导致路径信息不正确,无法找到源代码文件。
最后使用 add_subdirectory
实现。
修改之后的几个关键文件如下:
注意:?VUH_ROOT_DIR
这个变量中指定?vuh
?库代码的位置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 |
# 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.8) ? # for Vulkan SET(Vulkan_INCLUDE_DIR ${ANDROID_NDK}/sources/third_party/vulkan/src/include/) SET(Vulkan_LIBRARIES ${ANDROID_NDK}/platforms/${ANDROID_PLATFORM}/arch-${ANDROID_ARCH_NAME}/usr/lib) if(X86_64) ????SET(Vulkan_LIBRARIES ${Vulkan_LIBRARIES}64) endif() SET(Vulkan_LIBRARIES ${Vulkan_LIBRARIES}/libvulkan.so) add_library(vulkan SHARED IMPORTED) set_target_properties(vulkan PROPERTIES IMPORTED_LOCATION ${Vulkan_LIBRARIES}) ? # for vuh add_definitions(-DVK_USE_PLATFORM_ANDROID_KHR=1 -DVULKAN_HPP_TYPESAFE_CONVERSION=1) SET(VUH_ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../../vuh/) SET(VUH_BUILD_TESTS OFF) SET(VUH_BUILD_DOCS OFF) SET(VUH_BUILD_EXAMPLES OFF) ? add_subdirectory(${VUH_ROOT_DIR}src/ ${CMAKE_CURRENT_SOURCE_DIR}/build/) ? # for example SET(Vuh_INCLUDE_PATH ${VUH_ROOT_DIR}src/include) include_directories(${Vulkan_INCLUDE_DIR}) include_directories(${Vuh_INCLUDE_PATH}) ? # 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. ????????native-lib ? ????????# Sets the library as a shared library. ????????SHARED ? ????????# Provides a relative path to your source file(s). ????????src/main/cpp/native-lib.cpp) ? ? add_dependencies(native-lib vuh) ? # 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 ? ????????android) ? # 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. ????????native-lib ? ????????vulkan ? ????????vuh ? ????????${log-lib}) |
注意:由于 vuh
?库需要 CMake 3.8
?。因此,我们需要手工指定CMake
版本为3.10.2
?。
如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
apply plugin: 'com.android.application' ? android { ????compileSdkVersion 28 ????defaultConfig { ????????applicationId "com.mobibrw.vuhandroid" ????????minSdkVersion 24 ????????targetSdkVersion 28 ????????versionCode 1 ????????versionName "1.0" ????????testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" ????????externalNativeBuild { ????????????cmake { ????????????????version "3.10.2" ????????????????cppFlags "-std=c++14 -v -g" ????????????} ????????} ????} ? ????buildTypes { ????????release { ????????????minifyEnabled false ????????????proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' ????????} ????} ????externalNativeBuild { ????????cmake { ????????????version "3.10.2" ????????????path "CMakeLists.txt" ????????} ????} } ? dependencies { ????implementation fileTree(dir: 'libs', include: ['*.jar']) ????implementation 'com.android.support:appcompat-v7:28.0.0' ????implementation 'com.android.support.constraint:constraint-layout:1.1.3' ????testImplementation 'junit:junit:4.12' ????androidTestImplementation 'com.android.support.test:runner:1.0.2' ????androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' } |
如果出现如下错误:
1 2 |
CMake Error: CMake was unable to find a build program corresponding to "Ninja".??CMAKE_MAKE_PROGRAM is not set.??You probably need to select a different build tool. -- Configuring incomplete, errors occurred! |
则执行如下操作:
1 |
$ brew install ninja |
如果出现如下错误:
1 2 3 |
* What went wrong: Execution failed for task ':app:transformNativeLibsWithMergeJniLibsForDebug'. > More than one file was found with OS independent path 'lib/armeabi-v7a/libvuh.so' |
则删除代码中的 jniLibs/armeabi-v7a/libvuh.so
即可解决问题。
完整的例子点击此处下载 vuhAndroid
参考链接
- How to use CMake to add Third Party Libraries to your Project
- Use ExternalProject_Add to include Opus in Android
- Unknown CMake command “ExternalProject_Add”
- How to use CMake ExternalProject_Add or alternatives in a cross platform way?
- ExternalProject
- Custom Directory for CMake Library Output
- [CMake] Force rebuild of external project with Ninja
- Ninja does not always rebuild resource files (*.rc) when they were changed
- How to force an action target to run on every build?
- ExternalProject: Additional command specified by COMMAND stop working
- [CMake] External projects and make clean
- Android 上的 Vulkan 着色器编译器
- How to properly find and link NDK shaderc lib into your project in Android Studio?>
- OpenGL Programming/Android GLUT Wrapper
macOS Mojave (10.14.3) Android Studio 3.3.1 指定使用CMake 3.10.2版本
目前最新版本的 Android Studio 3.3.1
默认使用CMake 3.6
版本,但是已经支持 CMake 3.10.2
版本了。
新版本的 CMake 3.10.2
新增了 FindVulkan.cmake
等一系列的改进,对于很多项目来说,会更友好。
目前默认依旧使用 CMake 3.6
版本,但是可以手工指定使用 CMake 3.10.2
。
继续阅读macOS Mojave (10.14.3) Android Studio 3.3.1 指定使用CMake 3.10.2版本
错误 程序包org.apache.http.conn.util不存在
开发的时候遇到了这个问题,记录下下次就知道了。这个是因为在 sdk22
以后的版本不在支持了!!!
程序包 org.apache.http.conn.util
不存在,然后搜索了下,在 app
的 build.gradle
文件中加上useLibrary 'org.apache.http.legacy'
就可以了。
如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
android { ????compileSdkVersion 23 ????buildToolsVersion '23.0.3' ? ????useLibrary 'org.apache.http.legacy' ? ????defaultConfig { ????????applicationId "com.ah.woyi" ????????minSdkVersion 14 ????????targetSdkVersion 21 ????????versionCode 1 ????????versionName "1.0" } |
参考链接
Android Studio 3.2.1上vuh库使用的例子
Android Studio 3.2.1
上 vuh
库使用的例子,首先使用 基于Vulkan的GPGPU计算框架Vuh 编译出 Android
版本的动态库,然后依照如下步骤建立工程。
使用ccache加速编译Android NDK项目
如果使用cmake
构建项目,配置如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
# macOS `brew install ccache` ubuntu `apt-get install ccache` ? $ export ANDROID_NDK_HOME=~/Library/Android/sdk/ndk-bundle ? $ cmake .. \ -DNDK_CCACHE=ccache \ -DCMAKE_TOOLCHAIN_FILE=$ANDROID_NDK_HOME/build/cmake/android.toolchain.cmake \ -DANDROID_ABI="armeabi-v7a" \ -DCMAKE_BUILD_TYPE=Release \ -DANDROID_STL=c++_static \ -DANDROID_NATIVE_API_LEVEL=android-24??\ -DANDROID_TOOLCHAIN=clang ? $ make -j4 |
也就是定义NDK_CCACHE=ccache
。
如果使用ndk-build
构建项目,配置如下:
1 2 3 4 5 6 7 |
# macOS `brew install ccache` ubuntu `apt-get install ccache` ? $ export NDK_CCACHE=ccache ? $ export USE_CCACHE=1 ? $ ndk-build |
也就是增加两个环境变量。
不指定缓存目录的情况下,缓存文件的目录一般在当前用户名下的.ccache
目录下,时间长了,可能会产生很多的缓存文件,需要定时清理,当然也可以限制缓存的最大大小,让ccache
根据需要进行淘汰。
参考链接
Android Studio 3.2.1编译Vulkan示例项目
最近在学习Vulkan
,根据Google
官方给出的文档,是存在一些问题的,总结一下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
# 官方文档说要检出 git clone https://github.com/googlesamples/vulkan-basic-samples.git # 实际上 vulkan-basic-samples.git 是 VulkanSamples.git 的一个fork,google简化了部分编译流程 # vulkan-basic-samples.git检出后,直接用 Android Studio打开即可 # https://github.com/SaschaWillems/Vulkan.git也存在一个例子工程 # 但是貌似根正苗红的是VulkanSamples这个例子 ? $ git clone https://github.com/LunarG/VulkanSamples.git ? $ cd VulkanSamples ? $ cd API-Samples ? # 最后在当前目录下生成一个 android 的目录,用Android Studio导入这个目录下的工程 # 也就是 "VulkanSamples?/API-Samples?/android?" 这个目录是我们的工程目录 $ cmake -DANDROID=ON -DABI_NAME=armeabi-v7a |
如果代码下载不成功,可以本站下载一份拷贝。 点击这里下载?VulkanSamples vulkan-basic-samples
继续阅读Android Studio 3.2.1编译Vulkan示例项目