Android开发踩坑记录:签名管理、Gradle配置与NDK开发
这篇文章是我在2021年进行Android应用开发时遇到的一些问题和解决方案的整理。
APK签名管理
获取APK的SHA1值
SHA1值用于应用的身份验证和第三方SDK集成(如微信登录、支付宝等)。
方法一:从APK提取
1 2 3 4
|
keytool -printcert -file META-INF/CERT.RSA
|
方法二:从签名证书获取
1 2 3 4
| keytool -list -v -keystore D:\my.keystore -storepass 111111
|
方法三:在线工具
可以使用在线工具上传.keystore或APK文件获取SHA1值。
查看APK签名信息
1 2 3 4 5 6 7 8 9 10 11 12 13
|
keytool -printcert -file META-INF/CERT.RSA
|
Gradle高级配置
动态修改包名
通过Gradle脚本实现包名的批量替换,适用于多渠道打包场景。
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 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94
|
ext { applicationIds = 'com.aaa.xxx' toApplicationIds = 'com.bbb.xxx' packageName = 'package ' + applicationIds toPackageName = 'package ' + toApplicationIds fromPath = 'app/src/' + replaceText(applicationIds, "\\.", "\\/") intoPath = 'app/src/' + replaceText(toApplicationIds, "\\.", "\\/") deletePath = 'app/src/' + replaceText(applicationIds, "\\.", "\\/") + '/../' fromChannel = '"AAA"' toChannel = '"' + toApplicationIds + '"' ifReplace = false }
task replaceImportPath { if (!ifReplace) return
FileTree tree = fileTree(dir: 'app') tree.include '**/*.java' tree.include '**/*.xml'
tree.each { File mfile -> fileReader(mfile.path, applicationIds, toApplicationIds) fileReader(mfile.path, fromChannel, toChannel) }
fileReader("app/proguard-rules.pro", applicationIds, toApplicationIds) fileReader("app/build.gradle", applicationIds, toApplicationIds) }
task copyFile(type: Copy) { if (!ifReplace) return
println("replaceImportPath:" + replaceImportPath) dependsOn replaceImportPath
copy { from fromPath into intoPath filter { String line -> if (line.find(packageName)) { line = line.replace(packageName, toPackageName) } "$line" } }
println("fromPath:" + fromPath) println("intoPath:" + intoPath)
File file1 = new File(deletePath) file1.deleteDir() }
def fileReader(path, fromString, toString) { def readerString = "" def hasReplace = false
file(path).withReader('UTF-8') { reader -> reader.eachLine { if (it.find(fromString)) { it = it.replace(fromString, toString) hasReplace = true } readerString <<= it readerString << '\n' }
if (hasReplace) { println(path + " has replace package.") file(path).withWriter('UTF-8') { within -> within.append(readerString) } } } }
def replaceText(String fileText, String key, String value) { def regex = key fileText = (fileText =~ /${regex}/).replaceAll(value) return fileText }
|
库冲突排除
当项目中引入多个库时,可能会出现依赖冲突。
1 2 3 4 5 6 7 8 9 10 11
| configurations { all*.exclude group:'com.qq.e.union', module: 'union' all*.exclude group:'com.android.support', module: 'support-v4' }
dependencies { implementation('com.example:library:1.0.0') { exclude group: 'com.example', module: 'conflict-module' } }
|
Gradle内存配置
解决Expiring Daemon because JVM heap space is exhausted错误:
1 2 3 4 5
| org.gradle.jvmargs=-Xmx4096m -XX:MaxPermSize=2048m org.gradle.daemon=true org.gradle.parallel=true org.gradle.configureondemand=true
|
离线模式切换
问题: Disable offline mode and rerun the build
解决方案:
- 在Android Studio中,选择右侧边栏Gradle
- 点击Toggle Offline Mode切换
或修改gradle.properties:
1
| org.gradle.offline=false
|
NDK开发环境配置
NDK版本下载
历史版本下载链接格式:
1
| https://dl.google.com/android/repository/android-ndk-r{版本号}-{平台}.zip
|
CMake版本问题
错误: CMake报Invalid revision 3.18.1-g262b901-dirty错误
原因: CMake版本过高
解决方案:
解决路径过长问题
错误: cocos2dx_static/scripting/js-bindings/jswrapper/v8/debugger/inspector_socket_server.o.d No such file or directory
解决方案:
- 将项目放在盘符根目录下(如C:\或D:\)
- 缩短项目名称
- 使用subst命令创建虚拟驱动器
1 2 3
| subst X: "C:\Very\Long\Path\To\Project" cd X:\
|
环境变量配置
1 2 3
| ANDROID_NDK_HOME=C:\Users\Administrator\AppData\Local\Android\Sdk\ndk\20.0.5594570 NDK_ROOT=D:\android-ndk-r20b
|
NDK编译配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| android { ndkVersion "21.0.6113669"
defaultConfig { externalNativeBuild { cmake { cppFlags "-std=c++11" arguments "-DANDROID_STL=c++_shared" } }
ndk { abiFilters 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64' } }
externalNativeBuild { cmake { path "src/main/cpp/CMakeLists.txt" version "3.10.2" } } }
|
Cocos2d-x与Android Studio集成
Cocos2d-x-Lua Windows环境配置
环境依赖:
- python-2.7.6
- android-ndk-r9d-windows-x86_64
- apache-ant-1.9.16-bin
- cocos2d-x-3.2
- adt-bundle-windows-x86_64-20140702
- jdk-8u301-windows-x64
环境变量:
1 2 3 4 5 6
| ANDROID_SDK_ROOT=E:\adt-bundle-windows-x86_64-20140702\sdk ANT_ROOT=E:\apache-ant-1.9.16\bin COCOS_CONSOLE_ROOT=E:\cocos2d-x-3.2\tools\cocos2d-console\bin NDK_ROOT=E:\android-ndk-r9d JAVA_HOME=C:\Program Files\Java\jdk1.8.0_301 NDK_MODULE_PATH=E:\adt-bundle-windows-x86_64-20140702\sdk
|
创建工程:
1 2 3 4 5
| cd E:\cocos2d-x-3.9\tools\cocos2d-console\bin
cocos.py new MyGame -p com.game.simple -l lua -d E:\project
|
编译运行:
1 2 3 4 5
| cocos compile -p android --ap 20
|
Visual Studio Lua调试配置
安装BabeLua插件:
配置Lua工程:
- 工具栏 -> Lua -> New Lua Project
- Lua scripts folder: 项目的src目录
- Lua exe path: simulator\win32\exe
- Working path: 项目目录
注意事项:
- 生成的Lua项目设置为启动项
- 可能需要修改working path
- 在AppDelegate.cpp添加搜索路径:
1 2
| LuaStack* stack = engine->getLuaStack(); stack->addSearchPath("src/");
|
常见编译错误与解决方案
Program type already present
错误:
1
| Program type already present: android.support.v4.os.ResultReceiver
|
解决方案:
1 2 3
| android.useAndroidX=true android.enableJetifier=true
|
android.support.v4.content.FileProvider
修改AndroidManifest.xml:
1 2 3 4 5
| android.support.v4.content.FileProvider
androidx.core.content.FileProvider
|
LIBCMTD库冲突
错误: 默认库"LIBCMTD"与其他库的使用冲突
解决方案:
- 项目属性 -> 配置属性 -> 链接器 -> 输入
- 忽略特定库:libcmtd.lib
Ninja构建错误
错误: CMake was unable to find a build program corresponding to "Ninja"
解决方案:
1 2 3 4 5
| classpath 'com.android.tools.build:gradle:3.3.2'
buildToolsVersion "28.0.3"
|
缓存文件只读错误
错误: Could not read entry xxx from cache XXXX.bin
解决方案:
1 2 3 4 5
|
rm -rf .gradle/ ./gradlew clean
|
变体输出文件名冲突
错误: Several variant outputs are configured to use the same file name
解决方案:
1 2 3 4 5
| find . -name "xxxxx" -delete
./gradlew clean assembleRelease
|
cpu-features.c编译错误
错误: android/cpufeatures/cpu-features.c needed by obj/local/
原因: 使用不同版本的NDK编译过同一工程产生冲突
解决方案:
1 2 3 4 5
| rm -rf obj/
./gradlew assembleRelease
|
Text must not be null or empty
解决方案:
- 重启Android Studio
- 切换Build Variants(如从debug切换为release)
SocketException Connection reset
错误: java.net.SocketException: Connection reset
解决方案:
- 设置NDK环境变量
- 移除build.gradle中硬编码的ndkVersion
- 打开Android Studio重新同步项目
SDK依赖更新
友盟SDK迁移
原依赖地址:
1
| maven { url 'https://dl.bintray.com/umsdk/release' }
|
修改为:
1
| maven { url 'https://repo1.maven.org/maven2/' }
|
性能优化
Gradle编译加速
1 2 3 4 5 6
| org.gradle.jvmargs=-Xmx4096m -XX:MaxMetaspaceSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 org.gradle.parallel=true org.gradle.configureondemand=true org.gradle.daemon=true org.gradle.caching=true
|
构建变体优化
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| android { buildTypes { debug { minifyEnabled false shrinkResources false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } release { minifyEnabled true shrinkResources true proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' } } }
|
以上是我在2021年Android开发中遇到的一些问题和解决方案。