“我报名参与金石方案1期应战——分割10万奖池,这是我的第3篇文章,点击检查活动概况”

前语

了解一下将 Android library 发布到中心库房(比方 Maven Center,jitpack) 的进程中关于一些细节的疑问。比方咱们究竟发布了啥?只要 xxx.aar 文件吗?代码中依靠的三方库又是怎样处理的?

疑问

关于怎么将一个功用完善的 Android Library 发布到 Maven Center 或者类似的中心库房,现已是十分小儿科的东西了,网上有很多材料能够参阅。

但是关于发布的内容,发布进程的一些细节,仍然有很多问题值得咱们考虑。

  1. 经过装备 publish 功用插件,履行发布使命之后,咱们究竟发布了啥?只要 aar 文件吗?
  2. 本地 library 依靠的三方库是怎样处理的?是编译到咱们发布的包里了吗?
  3. 本地 library 依靠的三方库,dependencies 闭包下 libs 目录和直接 implementation 三方库终究会有什么差异吗?
  4. 资源文件是怎样处理的? assets 文件夹下的内容会发生什么?
  5. so 库文件呢,会发生什么?
  6. 代码的混杂装备怎样处理?library 下的 proguard-rules.proconsumer-rules.pro 有啥用?

假如你对上面的这些内容一目了然,那么下面的东西也就不用看了,能够直接叉掉了。

解惑

关于怎么给 library 经过 gradle 进行发布相关的装备,相关的细节就不展开说了,能够参阅的文章实在是太多了。

为了便利演示,本文直接运用本地库房。当然你也能够经过 Nexux 搭建本地私服,有兴趣的同学能够参阅 Android 运用maven publish插件发布产品(aar)全进程实践。

这儿将发布装备统一到一个 publish.gradle 文件当中.

发布装备

apply plugin: 'maven-publish'
def GROUP_ID = "com.engineer.third"
def ARTIFACT_ID = "thirdlib"
def VERSION = "1.0.0"
// 上传源码的 task 
task sourceJar(type: Jar) {
    from android.sourceSets.main.java.srcDirs
    archiveClassifier = "sources"
}
afterEvaluate {
    publishing {
        // 装备maven 库房
        repositories { RepositoryHandler handler ->
            handler.mavenLocal()
            handler.maven {
                url "${rootDir}/local_repo/"
            }
        }
        publications { PublicationContainer publicationContainer ->
            debug(MavenPublication) {
                from components.debug 
                artifact sourceJar // 上传源码
                groupId = GROUP_ID
                artifactId = ARTIFACT_ID
                version = "${VERSION}_DEBUG"
            }
            release(MavenPublication) {
                from components.release
                artifact sourceJar // 上传源码
                groupId = GROUP_ID
                artifactId = ARTIFACT_ID
                version = VERSION
            }
        }
    }
}

为了便利,咱们装备了 ${rootDir}/local_repo/ 作为本地库房,本地库房默认途径是 /Users/username/.m2/repository。其实,咱们能够直接运用这个本地库房,在 build.gradle 中增加 maven 地址就能够了。

allprojects {
    repositories {
        maven { url "${rootDir}/local_repo/" }
        google()
        jcenter()
        mavenCentral()
    }
}

这样咱们就能够在本地进行模拟发布和依靠 library 的功用。毕竟,库房地址仅仅个地址而已。

这样在 library 依靠这个装备文件 apply from: file("publish.gradle") 后,咱们便能够在 gradle task 中看到发布使命了。

发布 Android library 到 Maven 解惑

发布内容

经过双击图中的 publish 使命或者是命令行履行 gradlew :thirdlib:publish 就能够开始履行发布使命,发布使命成功后咱们就能够到 ${rootDir}/local_repo/ 检查一下究竟有哪些产品。

发布 Android library 到 Maven 解惑

有哪些内容

发布产品看着很多,但其实只要这么几类

  • aar 文件
  • module 文件
  • pom 文件
  • source.jar

其他都是这些文件的数字签名。

用 Android Studio 看一下 aar 的内容。

发布 Android library 到 Maven 解惑

能够看到 library 源码中的 xxx.so 和 assets 目录下的文件会按照编译时的装备和内容,原封不动的打包到 aar 中。即便是没有运用到的内容。

aar 的内容就是咱们的代码了,source.jar 是源代码,便利依靠 library 的时分进行检查。那么剩下的 module 文件和 pom 文件有什么作用呢?咱们结合剩下的问题来处理这个疑问。

依靠的规则

关于依靠,一般有两种。

  • 将 xxx.jar 或 xxx.aar 文件放到本地 libs 目录下,然后经过 implementation fileTree(include: ['*.jar','*.aar'], dir: 'libs') 进行依靠。
  • 经过 implementation 'com.google.code.gson:gson:2.9.0' 这种直接声明中心库房 group_id:artifact_id:version 的方法进行依靠。

这儿的状况比较复杂,分隔阐明一下。首先看本地依靠直接依靠 libs 包的内容。

本地依靠

aar

直接从 libs 包依靠三方组件的时分,假如是 aar 包,那么为了编译生成 library 自己的 aar ,则只能用 compileOnly fileTree(include: ['*.aar'], dir: 'libs') 的方法。否则会报错:

Direct local .aar file dependencies are not supported when building
an AAR. The resulting AAR would be broken because the classes and 
Android resources from any local .aar file dependencies would not be
packaged in the resulting AAR.

至于为什么有这种约束官方的报错信息现已阐明原因了。清楚明了,本地依靠的 aar 包是无法打进终究产品的。 那么假如必须要用到这个 aar 该怎样办呢?两种办法,一是将 aar 发布到中心库房,进行长途依靠;二是哪里缺这个 aar ,就在哪里直接进行本地依靠。

jar

jar 类型的文件要分状况

  • compilyOnly 依靠,那么不会输出到终究产品中。
  • implementationapi 依靠,将在终究 aar 文件的 libs 目录下保存相应的依靠。

发布 Android library 到 Maven 解惑

能够看到源码中 libs 目录下的两个 .jar 文件终究都输出到了 aar 文件中。这儿需求留意的是,class.jar 中并没有包含 libs 目录下 jar 文件的 *.class 文件,仅仅一些装备文件。

中心库房的依靠

这儿首先需求明确一点是,只要是经过声明中心库房 group_id:artifact_id:version 的方法进行依靠,那么所依靠的内容必定不会打包到终究的 aar 文件的。这么做是合理的,也是必须得,否则这 Android 的依靠办理将是一场噩梦。试想一下,假如一切依靠的东西,都能够打到一个包里,那假如发生抵触了,岂不是要解到天荒地老。

经过声明中心库房 group_id:artifact_id:version 的方法进行依靠的内容,在编译时必然是需求的,那么依靠这个 library 的运用方,又该怎么获取到这些依靠呢?这就要靠前面提到的 pom 文件了。

这儿咱们先比较一下,dependencies 各种装备一下,pom 文件的内容。

  • api
dependencies {
    compileOnly fileTree(include: ['*.aar'], dir: 'libs')
    implementation fileTree(include: ['*.jar'], dir: 'libs')
    compileOnly 'com.squareup.radiography:radiography:2.4.1'
    implementation 'androidx.appcompat:appcompat:1.5.1'
    api 'com.google.android.material:material:1.6.1'
}
<?xml version="1.0" encoding="UTF-8"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <!-- This module was also published with a richer model, Gradle metadata,  -->
  <!-- which should be used instead. Do not delete the following line which  -->
  <!-- is to indicate to Gradle or any Gradle module metadata file consumer  -->
  <!-- that they should prefer consuming it instead. -->
  <!-- do_not_remove: published-with-gradle-metadata -->
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.engineer.third</groupId>
  <artifactId>thirdlib</artifactId>
  <version>1.0.0</version>
  <packaging>aar</packaging>
  <dependencies>
    <dependency>
      <groupId>com.google.android.material</groupId>
      <artifactId>material</artifactId>
      <version>1.6.1</version>
      <scope>compile</scope>
    </dependency>
    <dependency>
      <groupId>org.jetbrains.kotlin</groupId>
      <artifactId>kotlin-stdlib-jdk8</artifactId>
      <version>1.6.10</version>
      <scope>compile</scope>
    </dependency>
    <dependency>
      <groupId>androidx.appcompat</groupId>
      <artifactId>appcompat</artifactId>
      <version>1.5.1</version>
      <scope>runtime</scope>
    </dependency>
  </dependencies>
</project>

看到 compile 这个在运用 Android Studio 2.x 版别时的常见的装备,是不是有种似曾相识的感觉。能够看到

  • 本地依靠的装备不会出现在 pom 文件中
  • compileOnly 依靠的内容,不会出现在 pom 文件中
  • implementation 在 pom 文件中 scope 变成了 runtime
  • api 在 pom 文件中 scope 变成了 compile

这些都是契合预期的。关于 pom 文件是什么以及 scope 的作用,这儿就不解说了,网上有很多相关的科普,很简略理解的。

当咱们在 gradle 中装备一个三方库依靠的时分,在 Android Studio 底部的信息栏你应该大概率看到过 download xxx.pom 文件的信息。gradle 就是经过这个 pom 文件的信息,明确当时依靠的 library 自身还依靠了那些内容,这就是所谓的依靠传递。

抵触

关于依靠,其实还有点其他内容。假如你是一个有追求的开发者,有代码洁癖。必定希望自己输出的东西越干净越好,运用者用起来越简略、问题越少越好。比方你声明的依靠和运用者声明的依靠抵触了怎样办?人家用的 glide 就是版别就是 3.x ,okhttp 版别就是 3.10.x ,并且还不能随意变化,你比人家高该怎样办? 这时分对外输出的时分就要做好选择,究竟是用 compileOnly 仍是 api 。也能够运用 exclude 扫除一些自身没有用到的内容,既能够扫除可能造成抵触的依靠,又能够稍微加快必定编译速度,何乐而不为呢。

比方像下面这样

    implementation("com.squareup.retrofit2:retrofit:2.9.0") {
        exclude group: "com.squareup.okhttp3", module: "okhttp"
        exclude module: "okio"
    }

自身仅仅运用 retrofit 进行了一些简略的封装,完全能够将 okhttp 相关的内容扫除去。尽可能防止给运用方带来抵触的可能性。

    <dependency>
      <groupId>com.squareup.retrofit2</groupId>
      <artifactId>retrofit</artifactId>
      <version>2.9.0</version>
      <scope>runtime</scope>
      <exclusions>
        <exclusion>
          <artifactId>okio</artifactId>
          <groupId>*</groupId>
        </exclusion>
        <exclusion>
          <artifactId>okhttp</artifactId>
          <groupId>com.squareup.okhttp3</groupId>
        </exclusion>
      </exclusions>
    </dependency>
  </dependencies>

能够看到,pom 文件会很天然的记录这种优化,运用方在依靠的时分,也会扫除这些依靠,天然不会不可思议的引入一些意料之外又用不到的东西。

当然,这种抵触也不是丧命的,运用者也能够自己装备 exclude 移除抵触的依靠项,或者是主动声明关闭依靠传递。

module 文件的作用暂时没理解出来,有知道的同学能够科普一下

混杂装备

最后再说一下混杂装备。一般状况下,当咱们新建一个 library 类型的 Android Module 时,会主动创建 proguard-rules.proconsumer-rules.pro 这两个文件。为什么需求两个,各自有什么用呢?

其实也很简略,浅显来说

  • proguard-rules.pro 是 library 自己用的
  • consumer-rules.pro 是给依靠这个 library 的运用者用的。

proguard-rules.pro 中装备当时 library 中那些内容需求混杂,那些内容需求保存。比方以 thridlib library 为例。

分别对这两个文件进行装备,这儿简略起见,随便装备一下,主要是阐明问题。

proguard-rules.pro

-keep class com.engineer.third.internal.NativeMethodsFactory {*;}

consumer-rules.pro

-keep class com.engineer.third.** {*;}

咱们看一下打包输出的 aar

发布 Android library 到 Maven 解惑

能够看到 proguard-rules.pro 装备的内容对当时 library 生效了(当然这个装备完全能够不用写,Android 自带的 proguard 规则会 keep native 方法及类,这儿仅仅举例)

发布 Android library 到 Maven 解惑

这个就比较有意思了 consumer-rules.pro 的内容变成了 proguard.txt 。那么这个文件,又有什么用呢?这儿就牵扯到另一个知识点了,Android 在输出 release apk 进行混杂的进程中,会搜集每个依靠库中的混杂装备,合并到一起。生成一个终究的混杂装备文件,终究会基于这个文件进行混杂处理。这个总的混杂装备文件一般在 app/build/outputs/mapping/falvor/configuration.txt 中,其中就能够看到各个依靠库的混杂阐明。

这就是 library 中混杂装备的用法。

小结

本文经过总结发布 library 和平时运用三方 library 时的一些疑问和困惑,就一般状况进行了相关的梳理。这样下次遇到相关问题时就有理可循了,不会再抓虾。当然,Gradle 自身的内容十分庞杂,疏忽和过错在所难免,欢迎各位同学评论。

参阅文档

  • 发布Android Lib到Maven Central
  • Gradle筑基篇(六)-运用Maven实现组件化类库发布
  • Android 运用maven publish插件发布产品(aar)全进程实践
  • Android:发布aar包到maven库房以及 maven插件 和 maven-publish 插件的区别
  • 封装第三方库,怎么优雅的依靠三方aar文件
  • 文中触及源码