当前位置:   article > 正文

在APK打包过程中,Assets资源漏编译漏打包的本质_文件打包进assets

文件打包进assets

背景

作为 Androider,我们平时在 Assets 资源目录下都放点啥呢,字体、预置数据、图片、配置文件…等等,那大家有没有想过,万一哪天我在 Assets 目录下新增了一个子目录放了点自己的资源文件,打包之后再解包发现 Apk 包里没有找到这部分文件,怎么办呢?

原理分析

我们都知道典型的 Android 应用构建流程第一步就是 Android 编译器将应用的源代码转换成 DEX 文件(即 Dalvik 可执行文件),并且把其他所有内容转换成为编译后的资源,然后打包器将 DEX 文件和编译后的资源文件组合成 APK。

这里的资源文件就包括 Assets 目录下的文件在内,还有 res 目录下的所有文件和 AndroidManifest.xml 文件,我们刚刚提到 APK 的资源编译是编译过程中的一项主要工作,AGP3.0.0 之后默认通过 AAPT2 来编译资源。

受到 Android 系统 AAPT 配置的影响,如果走系统默认配置打包,我们的 Assets 目录合并过程中会走一些判断逻辑,如果根据系统规则判定该文件夹是需要被忽略的,那么也就意味着打不进 Apk 里了。

规则是这样的:

# Assets 目录合并忽略模式
!.svn:!.git:!.ds_store:!*.scc:.*:<dir>_*:!CVS:!thumbs.db:!picasa.ini:!*~

  • 1
  • 2
  • 3

规则判断的源码是这样的:

// 忽略模式核心源码
static bool isHidden(const char *root, const char *path)
{
    if (strcmp(path, ".") == 0 || strcmp(path, "..") == 0) {
        return true;
    }

    const char *delim = ":";
    const char *p = gUserIgnoreAssets;
    if (!p || !p[0]) {
        p = getenv("ANDROID_AAPT_IGNORE");
    }
    if (!p || !p[0]) {
        p = gDefaultIgnoreAssets;
    }
    char *patterns = strdup(p);

    bool ignore = false;
    bool chatty = true;
    char *matchedPattern = NULL;

    String8 fullPath(root);
    fullPath.appendPath(path);
    FileType type = getFileType(fullPath);

    int plen = strlen(path);

    // Note: we don't have strtok_r under mingw.
    for(char *token = strtok(patterns, delim);
        !ignore && token != NULL;
        token = strtok(NULL, delim)) {
            chatty = token[0] != '!';
            if (!chatty) token++; // skip !
            if (strncasecmp(token, "<dir>" , 5) == 0) {
                if (type != kFileTypeDirectory) continue;
                token += 5;
            }
            if (strncasecmp(token, "<file>", 6) == 0) {
                if (type != kFileTypeRegular) continue;
                token += 6;
            }

            matchedPattern = token;
            int n = strlen(token);

            if (token[0] == '*') {
                // Match *suffix
                token++;
                n--;
                if (n <= plen) {
                    ignore = strncasecmp(token, path + plen - n, n) == 0;
                }
            } else if (n > 1 && token[n - 1] == '*') {
                // Match prefix*
                ignore = strncasecmp(token, path, n - 1) == 0;
            } else {
                ignore = strcasecmp(token, path) == 0;
            }
        }

    if (ignore && chatty) {
        fprintf(stderr, "    (skipping %s '%s' due to ANDROID_AAPT_IGNORE pattern '%s')\n",
            type == kFileTypeDirectory ? "dir" : "file",
            path,
            matchedPattern ? matchedPattern : "");
    }

    free(patterns);
    return ignore;
}

  • 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

源码挺长,我们直接看忽略模式解读图: image.png

举个例子

原理分析比较长,我们看一个详细的例子,更好地理解这个判断规则

随便找一个子 Module,在对应 Assets 目录下增加一个新的子目录,命名为 “__testxxx”,在这个目录下随便放一个测试文件,“test.txt”。

我们尝试打 SNAPSHOP 包然后更新依赖并且打 APK 包,解包以后好像并没有找到这个目录,更没有这个测试文件。

为什么呢?

我们回过头看下规则,其中 “:” 是分隔符,

_* 就是今天的重点了,啥意思呢,当判断到 “__testxxx” 是一个目录时,按照规则会接着判断后一位,恰好后一位是 “_” 下划线,和我们的文件夹名称开头一致,也就被认为需要被忽略合并。

简单来说,就是下划线开头的文件夹目录在打包时将会被认做需要忽略合并的文件夹,不予合并。

解决方案

两种方式,第一种很简单,就是换名字,换个和规则不匹配的名称即可

当然,有些情形下是没办法换组件名字的,不急,还有第二种方案,自定义 AAPT 配置,像这样:

android {
    aaptOptions {
        noCompress ' test1', 'test2'
        ignoreAssetsPattern "!.svn:!.git:!.ds_store:!*.scc:.*:!CVS:!thumbs.db:!picasa.ini:!*~"
    }
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

接下来,就看你自己的诉求了,如果是需要打到子 Module 的 SNAPSHOP 中,就把 ignoreAssetsPattern 加到对应 Module 的 android 配置中,如果是需要打到安装包 APK 中,就需要到壳工程的 build.gradle 中去同步一份配置了。

影响面评估

这份配置修改的是打包过程中 Assets 资源合并的规则,那么相对地,就会有一些绕开默认规则且符合自定义规则的资源被合并进 APK 包中了。简单地说:

  • 对于加了配置的 Module,会有一些 _* 规则以外的文件会被合并进去
  • 对于没有加配置的 Module,也就没有影响

大家可以针对 app/build/intermediates/incremental/mergeDebugAssets/merger.xml XML 文件做加配置前后的 DIFF 对比,在合并之前检查是否有不该合并的文件被合并进去。

影响完全可控,并且也达到了我们的初心,让合理资源被正常打包,不合理的资源被忽略,彻底解决漏编译漏打。

作者:闫宇威
本文转自 [https://juejin.cn/post/7192418675939344441]

最后

如果想要成为架构师或想突破20~30K薪资范畴,那就不要局限在编码,业务,要会选型、扩展,提升编程思维。此外,良好的职业规划也很重要,学习的习惯很重要,但是最重要的还是要能持之以恒,任何不能坚持落实的计划都是空谈。

如果你没有方向,这里给大家分享一套由阿里高级架构师编写的《Android八大模块进阶笔记》,帮大家将杂乱、零散、碎片化的知识进行体系化的整理,让大家系统而高效地掌握Android开发的各个知识点。
在这里插入图片描述
相对于我们平时看的碎片化内容,这份笔记的知识点更系统化,更容易理解和记忆,是严格按照知识体系编排的。

全套视频资料:

一、面试合集在这里插入图片描述
二、源码解析合集
在这里插入图片描述

三、开源框架合集
在这里插入图片描述
欢迎大家一键三连支持,若需要文中资料,直接点击文末CSDN官方认证微信卡片免费领取↓↓↓

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/你好赵伟/article/detail/266389
推荐阅读
相关标签
  

闽ICP备14008679号