2BAB's Engineering Blog

Android插件化笔记-8-PackagePluginJavaCode

Demo: https://github.com/2BAB/Android-Plugin-Dev-Notes

背景

为什么打包还要讲?因为我发现大家都没说过这回事啊…而且有些开源的插件化框架并没有看到有仔细处理这块,导致自己研究的时候很困惑。具体来说,打包这块也是分两块,插件代码和插件资源的打包,这节先看插件代码的打包。

一般地,我们的插件,和普通的 Android Library 不同,是直接声明成 Application。因为只有这样,才能简单地借助原有的(Application)打包插件来打出 Dex 和资源编译。而打包 Application,对于插件而言,会打入很多不必要的二方、三方依赖。有人会说,用 provided 声明插件模块的依赖不就好了,但这仅仅只能是解决 .jar 的模块,.aar 的依赖是不支持 provided 的。所以我们要解决的问题,也就很明显了,实现一个通用的打包仲裁:

Android插件化笔记-7-MultiClassloader

Demo: https://github.com/2BAB/Android-Plugin-Dev-Notes

为什么要有 Multi Classloader

如上篇所说,我们不管要起 Activity、Service,其实都是需要注入自定义的 Classloader。而 Service 没有一个很好的简单注入点,所以才有了 Hook 上层 Classloader 的方案。这种方案有两种,都是解决多 Dex 加载的情况(不管插件化与否其实只要方法数超 65535 都是需要做多 Dex 加载):

  1. 单一 Classloader:就是上篇说到的 MultiDex 使用的反射注入 DexPathList 的前部,这种是利用了 BaseDexClassloader 的 findClass 特性,由前往后查找 Dex 文件并加载 Class;
  2. 多 Classloader:利用双亲委派的 Classloader 机制,使得我们的 Classloader 可以优先于系统 Classloader 查找到 Class 并返回,通常会伴随着每个模块一个 Classloader,再由一个 HookClassloader 统一 Dispatch;

Gradle 日常崩(7)debuggable 属性无效

问题回顾

不久前,在接手手头上这个老工程时,发现 build.gradle 中设置 debuggable 属性是无效的,只能手动在 AndroidManifest.xmlapplication 节点写死该属性(之前团队里就这样默默干了两年,发版前去掉这个属性,发版后再加上…),最近得空花了两三个周末研究了下缘由。

默认看此文章的人已经知道这点:

Android 系统判断一个 App 是否可 Debug 的标准是,AndroidManifest.xml 中的 application 节点是否存在 debuggable 属性,并且其值为 true。(可参考官方文档

Android源码笔记-3-AAPT编译

碰到个问题,需要 debug aapt 里的一些东西,本来以为按照 Android源码笔记-1-编译&烧录的一些坑 的办法去设置环境再把 make 命令目标改一下即可,但是发现还是有些小问题,写下记录一哈。

先来结论,源码准备的步骤不叙,正确的步骤是:

1
2
3
4
$ make clobber
$ source build/envsetup.sh
$ lunch sdk-eng
$ make -j9 aapt

Gradle 日常崩(6)JDK 内的某个包失踪了

最近在改一个之前的 Annotation Processor,想要实现一个这样的需求:对一个 Class 所 implement 的 interface 做判断——这些 interface 是否 extends 自一个统一的父 Interface。于是 Debug + Evaluate Expression 挖一下 TypeElement 的实例里都有些啥,看看有没好使的 API。

Gradle 日常崩(5)含 buildscript 闭包的脚本执行顺序问题

最近在改一个裹脚布项目,对打包脚本升级的要求是「循序渐进」(工期紧,稳定为主)——Debug 下用新版的 Gradle Plugin,Release 用旧版的。嗯,很自然会想到改动 root project 下的 build.gradle,加一段判断:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// apply isDebug() method from utils.gradle
apply from: project.getRootProject().rootDir.absolutePath + '/scripts/utils.gradle'
def gradlePluginVersion = isDebug() ? '2.3.3' : '2.0.0'
buildscript {
repositories {
jcenter()
mavenLocal()
mavenCentral()
}
dependencies {
// use outer variable
classpath "com.android.tools.build:gradle:$gradlePluginVersion"
classpath ...
}
}
...

Gradle 日常崩(4)AAR 的 tools:replace 冲突

最近做一些 SDK 升级时,有些包引入后会有诸如此类的报错:

AndroidManifest.xml:22:9-40 Error:
Attribute application@theme value=(@style/AppTheme) from AndroidManifest.xml:22:9-40
is also present at [some:libraries:version] AndroidManifest.xml:9:18-62 value=(@style/AnotherTheme).
Suggestion: add ‘tools:replace=”android:theme”‘ to element at AndroidManifest.xml:18:5-65:19 to override.

这是一个很常见的错误了,照着提示做 replace 就 OK 了。但是当我加上 replace 的代码后,发现依旧报错:

Multiple entries with same key: @android:theme=REPLACE and android:theme=REPLACE.

百思不得其解,查看了一下依赖库的 AndroidManifest.xml 源码,发现它也设置了tools:replace="android:theme",而 Manifest Merger 把这个视为冲突抛了出来。

Gradle 日常崩(3)再谈 AAR 与混淆

之前写了篇文章讲到了由于 buildTypes 默认设置的原因导致 library module 无法 debug 的情况。事实上,当时只解决了打 Debug 包的情况,而忽略了打 Release 包时还埋了一个隐藏的问题。

问题还原:我们在做全局的 rebuild 或者 assembleRelease 时,会出现有些类找不到的情况,而 assembleDebug 不会。仔细观察会发现,这些报错的类都是被外部 module 引用的部分,例如 module A 有类 Clazz,被 module B 引用,则 Clazz 报错。

Gradle 日常崩(2)2.3.0 Plugin 再无 exploded-aar

升级 Android Gradle Plugin 至 2.3.0 后,会发现 exploded-aar 这个 build 目录下的文件夹大部分情况下不存在了(但是仍然有时候会出现一两个 aar 的解压包,有些诡异)。这个改动最相关的原因是 2.3.0 默认开启了 Build Cache,具体在这里有说明。

Gradle 日常崩(1)Library Module BuildTypes

最近工作中换了一个工程,重新配了一遍 Gradle 的环境,然后发现所有的 Library Module 都无法 Debug 或者只能取到某些全局变量(局部变量找不到)。百思不得其解时,突然发现我明明打的是 Debug 包 assembleDebug,我的 Library Module 执行的却都是 transformClassesAndResourcesWithProguardForRelease。明明在这些 module 都配置了 debugbuildTypes,但却不生效,反而打了混淆的 release 包。