Android Studio Library 模块中 Native 代码进行 debug 的一些坑
前言
如果項目中存在多個module,那么在application模塊中依賴library模塊,并且library模塊中有native代碼的時候,假設你需要debug library模塊中的這些native代碼,正常情況下,這部分native代碼是不能直接被debug的。導致這個問題的根本原因是因為即使在運行application模塊的debug構建時,其依賴的library模塊并不是以debug構建,而是以release構建。
項目結構例子如下圖所示
解決不能debug的方式有兩種。
1、不進行StripSymbolDebug
Gralde中有一個Task叫transformNativeLibsWithStripDebugSymbolFor${BuildType},其對應的代碼在com.android.build.gradle.internal.transforms.StripDebugSymbolTransform中,翻看代碼可以發現如下代碼
| 12345678910111213141516171819202122232425262728293031 | if (excludeMatchers.stream().anyMatch(m -> m.matches(Paths.get(path)))) {FileUtils.mkdirs(strippedLib.getParentFile());FileUtils.copyFile(input, strippedLib);} else {stripFile(input, strippedLib, abi);}private void stripFile(@NonNull File input, @NonNull File output, @Nullable Abi abi)throws IOException {FileUtils.mkdirs(output.getParentFile()); if (abi == null) {FileUtils.copyFile(input, output); return;}ProcessInfoBuilder builder = new ProcessInfoBuilder();builder.setExecutable(stripExecutables.get(abi));builder.addArgs("--strip-unneeded");builder.addArgs("-o");builder.addArgs(output.toString());builder.addArgs(input.toString());ILogger logger = new LoggerWrapper(project.getLogger());ProcessResult result = new GradleProcessExecutor(project).execute(builder.createProcess(), new LoggedProcessOutputHandler(logger)); if (result.getExitValue() != 0) {logger.warning("Unable to strip library '%s', packaging it as is.", input.getAbsolutePath());FileUtils.copyFile(input, output);}} |
當滿足excludeMatchers中的正則匹配時,該Task執行的是直接拷貝so文件,而不滿足時,則執行的是strip操作。
而excludeMatchers是在構造函數中被賦值的
| 123456789101112131415161718 | private final Set<PathMatcher> excludeMatchers;public StripDebugSymbolTransform( Project project, NdkHandler ndkHandler, Set<String> excludePattern) { this.excludeMatchers = excludePattern.stream().map(StripDebugSymbolTransform::compileGlob).collect(ImmutableCollectors.toImmutableSet());checkArgument(ndkHandler.isConfigured()); for(Abi abi : ndkHandler.getSupportedAbis()) {stripExecutables.put(abi, ndkHandler.getStripExecutable(abi));} this.project = project;} |
查看其構造函數被調用的地方
| 123456789 | TransformManager transformManager = scope.getTransformManager();GlobalScope globalScope = scope.getGlobalScope();transformManager.addTransform(tasks,scope, new StripDebugSymbolTransform( globalScope.getProject(), globalScope.getNdkHandler(), globalScope.getExtension().getPackagingOptions().getDoNotStrip())); |
從上代碼可以看到忽略列表是從PackagingOptions中的DoNotStrip傳入。
那么問題就好辦了,我們只需要在library模塊和application模塊中加入忽略strip的正則匹配即可,如下
| 12345678910111213 | android {//...packagingOptions {doNotStrip "*/armeabi/*.so"doNotStrip "*/armeabi-v7a/*.so"doNotStrip "*/arm64-v8a/*.so"doNotStrip "*/x86/*.so"doNotStrip "*/x86_64/*.so"doNotStrip "*/mips/*.so"doNotStrip "*/mips64/*.so"//...}} |
值得注意的是,library模塊和application模塊中的gradle都需要加入。
但是問題又來了,我們發布到maven的時候,是不需要執行這個的,因此,最好配置一個開關,且這個開關不會被提交到git中去,因此local.properties是最合適的
| 1234567891011121314151617181920 | boolean isDebug() { boolean ret = false try {Properties properties = new Properties() File file = project.rootProject.file('local.properties') if (!file.exists()) { return false}properties.load(file.newDataInputStream())String debugStr = properties.getProperty("debug") if (debugStr != null && debugStr.length() > 0) {ret = debugStr.toBoolean()}} catch (Throwable throwable) {throwable.printStackTrace()ret = false} project.logger.error("[${project.name}]Debug:${ret}") return ret} |
然后在local.properties中加入debug=true,修改packagingOptions配置為
| 123456789101112131415 | android { //... if (isDebug()) {packagingOptions {doNotStrip "*/armeabi/*.so" doNotStrip "*/armeabi-v7a/*.so" doNotStrip "*/arm64-v8a/*.so" doNotStrip "*/x86/*.so" doNotStrip "*/x86_64/*.so" doNotStrip "*/mips/*.so" doNotStrip "*/mips64/*.so" //... } }} |
2、讓Library模塊的BuildType隨Application模塊的BulidType而構建
除了以上的方式,其實還有一種方式,那就是讓Library不進行默認的release構建,而是隨Application的BuildType而改變,當Application的BuildType為debug時,Library也進行debug構建,當Application的BuildType為release時,Library則進行release構建,要做到這樣,需要顯示聲明一下compile的configuration。
查看Google的相關文檔可以發現怎么做:http://tools.android.com/tech-docs/new-build-system/user-guide#TOC-Library-Publication
application模塊中的依賴原來是這樣的
| 1 | compile project(':library') |
將其修改為
| 12 | releaseCompile project(path:':library',configuration:'release')debugCompile project(path:':library',configuration:'debug') |
然后在library模塊中的gradle中加入一行配置,表示不使用默認的
| 1234567 | android { //... defaultConfig {publishNonDefault true //...}} |
配置完成之后,就可以進行愉快的debug了,但是還沒完,對的,需要判斷是否是debug,那么以上配置就變成了這樣
| 123456 | if (isDebug()) { releaseCompile project(path:':library',configuration:'release') debugCompile project(path:':library',configuration:'debug')} else { compile project(':library')} |
| 123456789 | android { //...defaultConfig { if (isDebug()) {publishNonDefault true} //...}} |
這里之所以加入debug判斷,是因為發maven的時候,如果存在publishNonDefault=true,Maven發布插件將把這些額外的variant作為額外的包發布。這意味著它發布到一個maven倉庫并不是真正的兼容。我們應該只向一個倉庫發布一個單一的 variant。因此發布maven的時候,我們需要關閉這個配置項。
但是為了避免他人不知道這個情況,因此我們最好主動做一次檢測,在項目根目錄下的build.gradle加入檢測代碼
| 123456789101112 | allprojects.each {project -> project.afterEvaluate { def uploadArchivesTask = project.tasks.findByName("uploadArchives") if (uploadArchivesTask) {uploadArchivesTask.doFirst { if (isDebug()) { throw new RuntimeException("uploadArchives must disable debug options in local.properties first!")}}}}} |
一旦運行uploadArchives這個Task的時候,如果是debug,則直接扔異常,不讓其發布。
http://fucknmb.com/2017/05/11/Android-Studio-Library%E6%A8%A1%E5%9D%97%E4%B8%ADNative%E4%BB%A3%E7%A0%81%E8%BF%9B%E8%A1%8Cdebug%E7%9A%84%E4%B8%80%E4%BA%9B%E5%9D%91/總結
以上是生活随笔為你收集整理的Android Studio Library 模块中 Native 代码进行 debug 的一些坑的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: aapt2 资源 compile 过程
- 下一篇: libnghttp2 NDK 交叉编译