AS Library 使用NDK 的一些坑 Unable to strip library (+深入了解部分gradle机制)

Stella981
• 阅读 745

转自: https://www.twblogs.net/a/5c53f1d5bd9eee06ee217e42#

https://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/

1、这文章写得很好,读完后可以更了解gradle的机制。

与下面文章一起阅读,可以更好理解,其中有一句:

Android Library按照编译方式可分为“release”、“debug”版本;按照不同的产品可分为“demo”、“full”版本,例如有demoDebug 和 fullRelease等版本

https://blog.csdn.net/tantion/article/details/86686234

2、解决坑的重点:在library模块和application模块中加入忽略strip的正则匹配即可

PS:如何在build.gradle自定义和调用方法。https://blog.csdn.net/tantion/article/details/86686234

---------------------------------------------------------------------------------

AS Library 模块中 Native 代码进行 debug 的一些坑

前言

如果项目中存在多个module,那么在application模块中依赖library模块,并且library模块中有native代码的时候,假设你需要debug library模块中的这些native代码,正常情况下,这部分native代码是不能直接被debug的。导致这个问题的根本原因是因为即使在运行application模块的debug构建时,其依赖的library模块并不是以debug构建,而是以release构建。

项目结构例子如下图所示

AS Library 使用NDK 的一些坑 Unable to strip library (+深入了解部分gradle机制)

解决不能debug的方式有两种。

1、不进行StripSymbolDebug

Gralde中有一个Task叫transformNativeLibsWithStripDebugSymbolFor${BuildType},其对应的代码在com.android.build.gradle.internal.transforms.StripDebugSymbolTransform中,翻看代码可以发现如下代码

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是在构造函数中被赋值的

@NonNull
private final Set<PathMatcher> excludeMatchers;

public StripDebugSymbolTransform(
        @NonNull Project project,
        @NonNull NdkHandler ndkHandler,
        @NonNull 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;
}

查看其构造函数被调用的地方

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的正则匹配即可,如下

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是最合适的

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配置为

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模块中的依赖原来是这样的

compile project(':library')

将其修改为

releaseCompile project(path:':library',configuration:'release')
debugCompile project(path:':library',configuration:'debug')

然后在library模块中的gradle中加入一行配置,表示不使用默认的

android {
    //...
    defaultConfig {
        publishNonDefault true
        //...
    }
}

配置完成之后,就可以进行愉快的debug了,但是还没完,对的,需要判断是否是debug,那么以上配置就变成了这样

if (isDebug()) {
    releaseCompile project(path:':library',configuration:'release')
    debugCompile project(path:':library',configuration:'debug')
} else {
    compile project(':library')
}

android {
    //...
    defaultConfig {
        if (isDebug()) {
            publishNonDefault true
         }
        //...
    }
}

这里之所以加入debug判断,是因为发maven的时候,如果存在publishNonDefault=true,Maven发布插件将把这些额外的variant作为额外的包发布。这意味着它发布到一个maven仓库并不是真正的兼容。我们应该只向一个仓库发布一个单一的 variant。因此发布maven的时候,我们需要关闭这个配置项。

但是为了避免他人不知道这个情况,因此我们最好主动做一次检测,在项目根目录下的build.gradle加入检测代码

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,则直接扔异常,不让其发布。

原文:https://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%8

本文同步分享在 博客“oncealong”(CSDN)。
如有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。

点赞
收藏
评论区
推荐文章
blmius blmius
2年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Karen110 Karen110
2年前
一篇文章带你了解JavaScript日期
日期对象允许您使用日期(年、月、日、小时、分钟、秒和毫秒)。一、JavaScript的日期格式一个JavaScript日期可以写为一个字符串:ThuFeb02201909:59:51GMT0800(中国标准时间)或者是一个数字:1486000791164写数字的日期,指定的毫秒数自1970年1月1日00:00:00到现在。1\.显示日期使用
Jacquelyn38 Jacquelyn38
2年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
Stella981 Stella981
2年前
Android So动态加载 优雅实现与原理分析
背景:漫品Android客户端集成适配转换功能(基于目标识别(So库35M)和人脸识别库(5M)),导致apk体积50M左右,为优化客户端体验,决定实现So文件动态加载.!(https://oscimg.oschina.net/oscnet/00d1ff90e4b34869664fef59e3ec3fdd20b.png)点击上方“蓝字”关注我
Wesley13 Wesley13
2年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Wesley13 Wesley13
2年前
00:Java简单了解
浅谈Java之概述Java是SUN(StanfordUniversityNetwork),斯坦福大学网络公司)1995年推出的一门高级编程语言。Java是一种面向Internet的编程语言。随着Java技术在web方面的不断成熟,已经成为Web应用程序的首选开发语言。Java是简单易学,完全面向对象,安全可靠,与平台无关的编程语言。
Stella981 Stella981
2年前
Django中Admin中的一些参数配置
设置在列表中显示的字段,id为django模型默认的主键list_display('id','name','sex','profession','email','qq','phone','status','create_time')设置在列表可编辑字段list_editable
Wesley13 Wesley13
2年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
Python进阶者 Python进阶者
3个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这