前言

关于组件化的介绍,今日就完结了。

后续等组件化更新完,更新

  • Shadow插件化
  • ASM其他
  • Systrace运用与解析
  • 字节码相关

重视大众号:Android苦做舟 解锁 《Android十三大板块文档》,让学习更靠近未来实战。已构成PDF版

内容如下

1.2022最新Android11位大厂面试专题,128道附答案
2.音视频大合集,从初中高到面试包罗万象
3.Android车载运用大合集,从零开端一同学
4.功能优化大合集,离别优化烦恼
5.Framework大合集,从里到外剖析的明明白白
6.Flutter大合集,进阶Flutter高档工程师
7.compose大合集,拥抱新技能
8.Jetpack大合集,全家桶一次吃个够
9.架构大合集,轻松应对作业需求
10.Android根底篇大合集,根基安定楼房平地起
11.Flutter番外篇:Flutter面试+Flutter项目实战+Flutter电子书
12.高档Android组件化强化实战
13.十二模块之弥补部分:其他Android十一大常识系统

收拾不易,重视一下吧。开端进入正题,ღ( ・ᴗ・` )

从有赞微商城看组件化架构实践

组件化改造概述

  • 提出了 5 个调整方向:笼统根底模块、公共服务去中心化、事务模块服务化、笼统根底组件、单/多模块打包
  • 介绍了依据 3 个根底组件依靠和 1 个 Gradle 插件的落地计划

从大厂APP看Android组件化实践(八)最终篇

一丶有赞移动运用怎么给页面安上“恣意门”

“恣意门”:一行装备完结页面跳转重定向。

1.1.布景 & 痛点 & 价值

动态路由组件,处理的是 App 中最最常见的一种行为的问题,那便是:跳转。

跟着 App 技能栈的扩展,从本来最最简略的原生到原生的跳转,扩展到现在同一个 App 中包括原生页面、H5 页面、Weex 页面、Flutter 页面之间的跳转。

从大厂APP看Android组件化实践(八)最终篇
随之而来的问题便是:跟着 App 的版别迭代,许多本来原生完结的页面,需求经过新的 H5 或许 Weex 页面进行晋级/降级。而这些本来都是硬编码的跳转逻辑,或许需求跟着版别不停改动。总结下来,现有的,各个技能栈阻隔的页面跳转逻辑面对的直接问题有:

  • 跳转逻辑跟着版别走,无法一致进行改动
  • 跨技能栈跳转的完结本钱比较高,有必要在桥接模块中进行特别适配
  • 在一些 H5 需求运用专门 WebView 页面翻开的场景下,很难去适配,也有必要经过各个 Web 跳转的阻拦做特别处理

为了处理以上硬编码以及灵活性差的问题,咱们决议收拾现有的各技能栈跳转逻辑,将这些跳转整合,能够满足动态性、可装备的需求。

得益于项目中原有的路由跳转组件,各种页面之间的页面都能够经过 URL 的办法进行路由,所以咱们依据 URL 跳转,开发了一套动态路由组件,它完结的作业有 :

  • 承担 App 内一切跳转逻辑
  • 经过装备中心组件,支撑获取/装备路由替换规矩
  • 匹配一切的路由跳转规矩,射中规矩的,替换成新的方针路由地址
  • 将实践跳转方针地址传递给路由组件履行实践的跳转行为

从大厂APP看Android组件化实践(八)最终篇

1.2.完结计划

路由阻拦+替换 微商城客户端现在现已有一套安定的组件化完结计划,组件之间的页面跳转经过路由的办法进行解耦,这是一种比较常见的办法。

在微商城项目中,担任完结的路由组件为 ZanURLRouter ,它的职责很简略:

  • 发动时注册路由和页面
  • 找寻正确的页面进行跳转

从大厂APP看Android组件化实践(八)最终篇
在不影响外部接口的前提下,咱们在方针路由解析这一步,引进了动态路由

从大厂APP看Android组件化实践(八)最终篇
关于移动端的路由重定向,实践上便是将一个路由转化为另一个路由,如:

youzan://orderlist?type=1&status=2

转化为:

wsc://orderlist/v2?type=1&status=2

跳转规矩装备 路由的阻拦和替换中的一个要害节点便是“装备”,咱们需求一个路由规矩列表来记录和下发匹配规矩。为了便利下发路由规矩表,咱们将这份装备表寄存在有赞移动装备中心,依据客户端的版别进行区分,动态地下发给不同版别的客户端。

一条路由规矩,分为一个 Key 和对应的 Value,Key 为匹配办法,运用正则表达式进行匹配,Value 为替换办法,运用 JSON 格式界说。

实践代码完结中,咱们将“路由规矩”和“路由替换行为”别离笼统成实体类和接口办法。

笼统实体类

关于替换路由跳转的规矩,咱们能够这样装备:

Key: ^youzan://orderlist\?type=(\d+)&status=(\d+)$
Value: {
        "template": "wsc://orderlist/v2?type=$1&status=$2"
}

即:一条匹配规矩 + 一条替换模板。咱们将之笼统为一个实体类, Rule :

class Rule {
    // url 匹配规矩(正则表达式)
    String pattern;
    // url 匹配规矩(正则表达式)
    String template;
}

笼统接口

有了规矩装备之后,就需求对动态路由的行为进行笼统,中心便是初始化规矩、匹配规矩和替换路由三个办法:

// 注册替换规矩
fun initWithPattern(Rule rule)
// 校验是否射中现已注册的路由装备的 pattern 正则
fun testWithRoute(String routeUrl): Boolean
// 获取替换后的跳转地址
fun appliedWithRoute(String routeUrl): String

动态路由器会在运用发动阶段拉取正确的规矩表,解析并记录下来:

从大厂APP看Android组件化实践(八)最终篇

ZanURLRouter 解析方针路由的时分,对每一个规矩进行匹配测验,射中则运用匹配的规矩,返回替换后的路 由,再持续接下来的作业。

路由替换

实体类、接口类都笼统完结之后,便是动态路由的中心完结了,这儿依靠到一个的中心东西便是:正则表达式。这儿用到正则的场景有两个: – 正则验证是否射中规矩 – 正则替换url文本

在 Android 和 iOS 开发中,字符串正则相关的 API 都是自带的,开箱即用:

/* ------------ Android ------------ */
// 正则匹配校验办法
Pattern.matcher(String text)
// 正则匹配校验办法
Regex.replace(String input, String replacement)
/* ------------ iOS ------------ */
(NSString *)stringByReplacingMatchesInString:(NSString *)string options:
(NSMatchingOptions)options range:(NSRange)range withTemplate:(NSString *)templ;

疑难问题:参数处理

大部分状况下,跳转自身都是带参数的,那么动态替换跳转的 URL 之后,参数的获取就成了一个问题,尤其是原生和其他页面页面的跳转。

咱们首要以 Android 为例,Android 原生跳转都是经过一个要害类:Intent 来完结参数的存取。这儿需求留意的是,由于 Intent 传值存在多种杂乱的数据接口,包括 Parcelable 这种杂乱参数的场景,由于降级之后都是以 URL 的形式传值,所以咱们现在约定动态路由的参数只支撑根本数据类型,杂乱参数类型的需求接入方来做兼容。

参数处理咱们分两个典型的场景来讨论: – 原生跳转 H5 参数传递 – H5 跳转原生的参数传递.

原生跳转H5

这儿的办法首要是将 Intent 中的根本数值类型参数取出来,拼接成带参数的 URL 来完结将 Intent 里边的参数传递给H5,首要完结代码如下:

fun appendBundleParams(strBuilder: StringBuilder, bundle: Bundle) {
    val ketSet = bundle.keySet()
    for (key in ketSet) {
        bundle[key]?.let { value ->
           when (value) {
               is Bundle -> appendBundleParams(strBuilder, value)
               is String, is Int, is Long, is Double, is Float, is Char, is Boolean, is Short -> {
                   if (strBuilder.isNotEmpty()) {
                       strBuilder.append("&")
                   }
                   strBuilder.append("$key=$value")
               }
               else -> {
                   // do nothing
               }
            }
        }
    }
}

H5跳转原生

同理的,H5 跳转原生做的便是将 URL 中带着的参数塞到 Intent 中来进行。

这儿比较要害的一个问题是:Intent 的取值都是带类型的,而 URL 的参数都是字符串。咱们现在处理计划也很简略,便是封装 Intent 的取值办法,由于现在有赞 Android 首要运用 Kotlin 来开发,能够运用 Kotlin 的扩展函数特性来完结(Java 能够运用东西类的办法):

fun Intent.getIntFromRouter(key: String, defaultValue: Int): Int {
    val extras = this.extras;
    if (extras == null || !this.hasExtra(key)) {
        return defaultValue
    }
    return extras.get(key) as? Int ?: (this.getStringExtra(key)?.toInt() ?: defaultValue)
}

碰到的坑:UrlEncode

在匹配和替换 URL 规矩的场景中,咱们经常会碰到这么一种状况,URL 是被 UrlEncode 过的。由于字符串的正则匹配和正则替换是不会判断字符串是否被 UrlEncode 过,所以这儿的逻辑需求由路由组件来完结。

UrlEncode 字符串的正则匹配逻辑完结比较简略,即直接将字符串 Decode 之后进行匹配。

比较杂乱的是 UrlEncode 字符串的正则替换,有些状况下,路由中的url是有必要进行 UrlEncode 的,假如直接Decode 进行替换,那么或许会导致实践跳转的方针 URL 被错误地切断,导致无法跳转,所以这儿的替换有必要保留UrlEncode 的字符。

咱们的处理思路是:记录 URLEncode 前后被 encode 字符的下标,然后再手动完结 replace 办法去挨个替换字符串中的字符,中心代码如下:

private fun getEncodeCharMap(url: String, encodeUrl: String): Map<Int, IntRange> {
    if (Uri.decode(encodeUrl) != url) {
        return mapOf()
    }
    val urlChars = url.toCharArray()
    val urlEncodeChars = encodeUrl.toCharArray()
    var i = 0
    var j = 0
    val encodeMap = mutableMapOf<Int, IntRange>()
    while (i < urlChars.size && j < urlEncodeChars.size) {
       // text: [www:] => [www%23]
       // length: [0123] => [012345]
       if (urlChars[i] != urlEncodeChars[j]) {
           val s = Uri.encode(urlChars[i].toString())
           val range = IntRange(j, j + s.length - 1)
           encodeMap[i] = range
           j += s.length
       } else {
           j++
       }
       i++
    }
    return encodeMap
}

1.3.实践运用事例

运用中心

微商城App运用中心,应该是运用动态路由的最佳场景,运用中心存在大量跳转的场景。

从大厂APP看Android组件化实践(八)最终篇
先来说下运用动态路由的布景,运用中心中运用列表都是由服务端一致下发的,后端为每个运用装备的跳转地址是一致的,而 Android 和 iOS 本地路由装备的 URL 是不一致的,假如直接下发装备的话,会存在有一端无法跳转的问题。以店肆办理运用跳转为例:

  • iOS中店肆办理的路由 URL:wsc://shop/management
  • Android 中的路由URL:wsc://team/management
  • 服务端下发的URL:wsc://team/management

那么处理同一套装备跳转不同 URL 的这个问题,就交给动态路由来完结了,我只需求在iOS的动态路由增加一个规矩,将 wsc://shop/management 动态替换成 wsc://team/management 就能够搞定!

订单项目

在微商城客户端的订单模块重构项目中,考虑到订单是运用频次很高的中心场景之一,且代码前史较久,所以新的模块上线后与旧订单列表模块共存,直到灰度完全结束。

由于微商城现已是组件化拆分,事务组件之间的跳转运用路由完结,咱们在规划灰度计划时,利用动态路由来实时进行方针路由的映射:

从大厂APP看Android组件化实践(八)最终篇

1.4.总结

“上线只是开端”,跟着事务迭代,前史事务也越来越多,为了确保不同渠道版别的用户能够平滑过渡到新的功用上去,动态路由组件扮演了一个客户端的 URL 重定向服务的角色,避免因服务下线、功用更新、渠道差异、项目重构等原因导致的功用不可用。

动态路由组件,中心便是十分简略的正则匹配和正则替换,而这个十分简略和中心代码逻辑,完结了事务场景下十分重要的路由重定向。这整套处理计划,也是有赞移动端在运用组件化、动态化的一个重要组成部分,咱们也希望这个技能计划能够抛砖引玉,启发更多优秀的移动端动态化处理思路。

二丶有赞微商城-Android组件化计划

2.1.概述

现在有赞移动端的首要作业内容是在“有赞微商城”和“有赞零售”两条公司首要的事务线,跟着有赞 Saas 事务的增长,客户端也不断迭代,支撑越来越多的功用。 在这个事务快速增长的状况下,移动端技能的全体架构也是一直在不断调整,来确保开发功率和事务的快速迭代。

这篇文章,首要是介绍有赞微商城 Android组件化的一些思路和完结。

现状 客户端的架构,从一开端的“All IN ONE” 模式(即一切代码都在 App 中),逐步演变到现在的一个单 Project 多Module 结构:

从大厂APP看Android组件化实践(八)最终篇
痛点

新的项目架构,也带了了新的问题: – 日益杂乱的 Common 模块,逻辑杂乱,依靠不明晰,不敢随便改动Common 代码,构成大量冗余代码和无法保护的事务逻辑 – 跟着事务模块的增多,打包速度一发不可收拾;从倒杯水的时刻到下楼吃个饭的时刻,大大减慢了开发节奏 – 由于事务模块跟项目中的上层(App 壳)和下层(Common模块)耦合 – 事务模块增多,由于事务模块没有自己的生命周期,无法完结模块之间的阻隔,全体模块控制比较紊乱

需求处理的问题

  • Common模块轻量化,需求将 Common 层的事务向上抽离,通用的底层和根底组件、共用 UI 组件抽成单独的依靠
  • 移动端事务服务化,解耦现有事务,笼统出事务接口,事务模块只向外露出自己的接口,并完结跨模块之间的调用
  • 能够装备单模块或许多模块打包,不必每次调试都全量打包,费时费力,又影响开发的节奏
  • 事务模块的依靠和发布办理

2.2.架构调整

咱们之前虽然有在做整个工程模块化的开发,但是现在的模块化结构能够说是不行彻底的:

  • 模块只是项目结构的概念(一个模块一个 Module),在逻辑层并没有模块这个概念
  • 模块自身并没有生命周期控制
  • 共用服务中心化,共用逻辑部分悉数都在 Common 模块中
  • 模块对外露出的服务不可知,都是直接依靠模块内部的代码逻辑
  • 模块无法单独打包,针对模块的代码改动,只能全量打包之后才能看到作用
  • 为了处理以上的问题,咱们需求对现有的架构进行调整。

模块化的笼统

将模块的功用笼统出一些根底类,组成了模块化支撑组件,它供给的功用有:

  • 笼统出模块自身作为某一类事务的容器,即一切事务模块需求完结自己的模块类,承继自咱们的BaseModule,并在 App 壳工程中进行注册
  • 模块目标跟 Activity 相同,拥有生命周期的概念,需求在生命周期的不同阶段处理自己相应的逻辑(注册服务、初始化数据等)
  • 模块能够注册的对外露出的服务的完结,在注册模块的时分,模块带着的服务也会被注册到 App 的服务中心

公共事务去中心化

跟许多客户端的同学聊过,许多 APP 发展到必定阶段之后,必然会诞生一个所谓的 Common 模块。它就像一个大储物柜,每个人都把一些其他人或许用到的东西一股脑儿塞进去。 这么个塞法,会有两个问题:

  1. 冗余:比方一些东西类,许多时分,当你找不到需求的东西类的时分,你或许会塞一个新的进去
  2. 保护本钱高:一切共用的事务逻辑的完结都在 Common 中,对一些共用事务逻辑的影响面无法掌控

Common 里边都有什么?

  • 东西类
  • 共用的 UI 组件
  • 多个事务模块都共用的事务类
  • 根底组件的封装类(图片库、网络库、Webview)
  • 封装的一些基类(BaseActivity,BaseFragment 什么的)

处理的思路

  • 将共用的事务模块向上抽离到事务模块中(所谓事务模块的服务化)
  • 将根底组件笼统到一个独立的组件中
  • 将一些根底类下沉到不包括事务逻辑的底层中心库中

事务模块服务化 服务化”这个词,在服务端的开发中经常被提到,简略来说,便是依据事务划分为多个模块,模块之间的交互以互相供给服务的办法来完结。 而客户端跟着事务模块的增多,也必然存在事务模块之间存在事务依靠的状况,而Android 端依靠的办法有:

  1. A 模块直接依靠 B 模块,直接调用 B 模块的代码逻辑
  2. 将 A 和 B 模块中的共用部分放到 Common 模块中,经过调用 Common 模块的代码完结依靠

事务模块服务依靠的完结

  • 后端的服务化是借助于 Dubbo 来构建的 RPC 服务,依靠某个服务,只需求依靠其对外露出的 API 模块(只包括接口和数据结构的 Maven 依靠),不需求依靠其详细完结,详细服务调用的完结由结构来完结
  • 客服端的依靠也能够参考这样的办法来完结模块之间的依靠,例如商品模块,能够供给一个 API 层,用来对外露出数据结构和服务

从大厂APP看Android组件化实践(八)最终篇

API 层完结办法

对外露出服务的办法有许多种:

  • 协议的办法:例如”app://order/detail/get?id=100″,数据能够用 JSON 来进行传递,恳求本地服务就像调用一个 Http 服务相同,依据恳求协议来获取数据,然后解析数据进行操作
  • 接口的办法:像后端运用 Dubbo 服务那样,订单模块对外供给一个独立的 Maven 依靠,里边包括了数据接口和对外供给的服务接口,适用方依靠之后直接调用

接口的办法完结 API

协议的办法的问题:假如服务供给的当地更改了之后,需求手动去查询一切调用到的当地,进行更改,并且没有版别办理,并且数据解析都需求手动进行转化,改动的本钱比较高,也有必定稳定性风险。 接口的办法的问题:需求额定供给一个依靠(单独把 API 层打包成一个 aar 包),运用方需求增加 Mave 依靠,所以引进依靠和发布的本钱比较高。

咱们最终选择了接口的办法,这种办法的稳定性和版别控制做的更好,关于改动来说,编译过程主动会帮你校验改动的影响面,而引进依靠和发布本钱高的问题,完全能够交给构建东西(Gradle Plugin)来处理。

事务完结层

事务完结层需求做的,便是完结自己模块自身的事务逻辑,并完结自己供给的 API 接口,露出对外的服务。

根底组件笼统

现有的根底组件完结

项目中现在有许多的根底组件都是一致在 Common 里边进行封装的,例如:账号库、网络库、图片加载、Web 容器库等等,这也带来了一些问题:

  1. Common 太重
  2. 事务模块跟根底组件强耦合,在开发一些跨团队的组件过程中,假如碰到运用的根底库不同的时分,需求比较多的时刻来做封装
  3. 晋级根底组件或替换依靠的本钱比较高,一些 API 的更改需求改动每个调用的当地

完结思路

  • 将常用的根底组件收拾,笼统成单独的一个笼统层,里边界说了一系列根底组件接口(图片加载、Web 容器、JsBridge 调用、账号等等)
  • 把一致完结的组件放到另一个依靠里边,能够在 App 中进行详细完结的注册,而事务模块自身,能够只依靠笼统

依靠结构

从大厂APP看Android组件化实践(八)最终篇

单/多模块打包 跟着事务量和事务杂乱度的增长,还有多个三方组件的引进,客户端工程代码量也变得越来越巨大,直接构成的一个问题是:打包慢!一个简略的场景:当你开发了一个商品模块内部的功用之后,你需求打整个 App 的包才能进行测验,而打一个包的时刻或许是 5~10 分钟,假如一天打包 10 次,也是比较酸爽。咱们的组件也需求支撑单模块或许选定的某些进行打包,其中的思路也是经过自界说 Gradle Plugin 在编译阶段,动态去更改 Module 实践依靠的Android Gradle 插件来完结的。 经测验,同一台电脑,完好打包(clean之后再装置)耗时 4 分钟,而单模块打包 (同样也是 clean 之后装置)耗时 1 分钟,全体打包时刻降低了 70% 以上。

架构图

上面的一些改善点,总结成一张图,便是这样的:

从大厂APP看Android组件化实践(八)最终篇

2.3.完结计划

现在咱们的计划供给 3 个根底组件依靠和 1 个 Gradle 插件:

  • modular-core: 供给组件模块化生命周期和模块服务注册相关的模块化根底组件
  • modular-support: 对项目中二方、三方包接口的笼统
  • modular-support-impl:对项目中二方、三方包接口的默许笼统
  • modular-plugin: 支撑模块生成 API 层目录,生成 APP 运转环境,以及办理模块发布的 Gradle 插件

Modular-core

完结模块类

事务模块类需求承继 BaseModule:

public class ModuleA extends BaseModule {
    @Override
    public void onInstalled() {
        registerBusinessService(ModuleAService.class, new CachedServiceFetcher() {
           @Override
           public ModuleAService createService(@NotNull ModularManage manager) {
               if (service == null) {
                   service = new ModuleAServiceImpl();
               }
               return service;
           }
    }
}

模块生命周期

模块有以下几个生命周期:

  • onInstalled() -> 模块被注册的时分调用:Module 在 App 中被注册的时分
  • onCreate() -> 模块第一次发动的时分调用:Module 所属的某个 Activity 第一次发动的时分
  • onStart() -> 模块发动的时分调用:模块第一次发动之的时分
  • onStop() -> 模块中止的时分调用:Activity 栈里边没有模块所属 Activity 的时分

模块生命周期的完结

其实组件内关于生命周期捕获和监听,都是借助于 Google 的 Android Architecture Components 中的 Lifecycle 库来完结的。

  • 模块生命周期的捕获:首要需求将 Activity 的类注册到 Module 中,然后全局监听 Activity 的 Lifecycle.Event事情,就能够获取到模块内 Activity 的运转状况
  • 模块生命周期的监听:BaseModule 自身承继了LifecycleOwner 接口,能够对其增加观察者,来完结对模块生命周期的监听

Modular-plugin

这儿需求依靠关于 Android 的构建东西 Gralde 的扩展,它支撑的高度可扩展特性,帮助咱们在组件化开发中愈加高效,不需求关系一些额定的作业,只需求重视开发的内容即可,对现有的代码逻辑根本没有侵入。

Gralde 的生命周期

这儿有必要要提一些的便是 Gradle 的生命周期,由于咱们的许多扩展功用,都是在对 Gradle 履行的生命周期的各个阶段做一些改动来完结的,大概的生命周期如图:

从大厂APP看Android组件化实践(八)最终篇

单模块打包

Android 打包成 Apk 并运转的条件有:

  • AndroidManifest.xml 的装备支撑(application 标签的装备)
  • 主 Activity 的装备

完结原理

  • 主动生成模块自己的 Application 类
  • 主动读取 Module 的 AndroidManifest 文件并修改成能够打包成 App 的装备
  • 在打包的时分动态更改 SourceSet,使打包的时分运用生成的文件进行打包
  • 在打包的时分动态更改支撑的 Plugin 类(’com.android.application’或是’com.android.library’)

修改模块 build.gradle 的装备

将以下装备增加到模块目录下的 build.gradle 文件中

modular {
        // 模块包名
        packageName = "com.youzan.ebizcore.plugin.demoa"
        app {
            // 单模块打包开关
                asApp = true
                // 运转的 App 的名称
                appName = "Module A"
                // 进口 Activity
                launchActivity = "com.youzan.ebizcore.plugin.demoa.ModuleAActivity"
                // 装备只在单模块打包时需求引进的依靠
                requires {
                    require "com.squareup.picasso:picasso:2.3.2"
                }
            }
        }

生成单模块运转需求的环境

运转 modular 的 createApp Task,就会主动生成需求的类(以 module_a 为例)

主动生成的文件目录结构:
./module_a
    --src
    ----main
    ------app # 主动生成 app 目录
    --------java # 主动生成 Application 类
    --------res # 主动生成资源
    --------AndroidManifest.xml # 主动生成 Manifest 文件

履行单模块打包并装置的 Task

运转 modular 的 runAsApp Task,模块就会被单独达成一个 apk 包,并装置到你的手机上,假如模块有上下文依靠(比方登录)的话能够额定供给依靠,加到模块的 app 的 requires 中。 这儿的打包履行是在 build 目录下生成了一个打包脚本,并调用 Gradle 的 API 履行脚本来完结打包装置的。

模块 API 办理

模块 API 层供给的接口和数据结构代码是能够直接在模块内部被引用到的,便利开发,但是在露出给外部的模块时分的时分是需求打包成 aar 上传到 Maven 来供给的,Modular-Plugin 别离针对这两个步骤供给了两个 Task,便利开发者快速进行开发和发布。

在 build.gralde 中增加相关装备

modular {
        packageName = "com.youzan.ebizcore.plugin.demoa"
        // 模块 API 支撑相关参数
        api {
              // 是否需求供给 API 支撑的开关(会影响到是否能够运转主动生成代码的 Task)
              hasApi = true
              // 对外供给的 API Service 类名
              apiService = "ModuleAService"
              // API 层的依靠
              requires {
                  require "com.google.code.gson:gson:2.8.2"
              }
         }
}

生成 API 打包需求的文件

运转 modular 的 createApi Task,就会主动生成需求的类(以 module_b 为例)

./module_b
      --src
      ----main
      ------service # 主动生成 service 目录,用来寄存对外接口和数据目标
      --------java # 主动生成 Application 类
      --------AndroidManifest.xml # 主动生成 Manifest 文件,为了单独打成 aar 包

模块发布

发布功用内部运用了 ‘maven-publish’ 插件来进行依靠的上传,开发者只关怀上报的装备就好

在 build.gralde 中增加发布装备

  modular{
         // 模块发布需求的参数
         publish {
             // 是否翻开模块发布
             active = true
             // 上报地址,支撑本地路径和远程 Mave 库房地址
             repo = "../release"
             groupId = "com.youzan.ebizmobile.demo"
             artifactId = "modular-a"
             // 上报的事务模块 aar 包的版别号
             moduleVersion = "0.1.4"
             // 上报的 API 层 aar 包的版别号
             apiVersion = "0.1.5"
             // Maven 登录名和暗码,能够从 local.properties 中取
             userName = ""
             password = ""
       }
  }

履行发布的 Task

运转 modular 的 uploadModule Task,Module-Plugin 会履行打包上传的任务,履行顺序是这样的:

  1. 首要打包并上传 Module 的 API 模块(SourceSet 只包括 API 的类)
  2. 将 Module API 的代码从模块的 SourceSet 中去除,并增加刚才上报的 API 模块的 Maven 依靠到 Module 的 dependencies 中

2.4.Modular-support

根底组件笼统

以图片组件为例,一般事务模块中运用到的图片相关的功用有:图片加载、图片选择等,能够把这些功用笼统成接口

  interface IImageLoadSupport {
      fun <IMAGE : ImageView> loadImage(imageView: IMAGE?, imgUrl: String)
      fun <IMAGE : ImageView> loadImage(imageView: IMAGE?, @DrawableRes drawableId: Int)
      fun <IMAGE : ImageView> loadImage(imageView: IMAGE?, imgUrl: String, callback:ImageLoadCallback<IMAGE>)
      fun imagePicker(activity: Activity?, selectedImgUris: List<Uri>)
      fun onImagePickerResult(requestCode: Int, resultCode: Int, intent: Intent?):List<String>?
  }

根底组件的完结

根底组件的完结能够在 App 中进行注册,假如需求单模块组件中运用 Support 相关功用,能够供给一套默许完结,在但模块运转时引进,在全局有一个 Support 注册中心,以 Map 的形式保护运转中的 Support 目标:

fun <SUPPORT : Any, SUPPORTIMPL : SUPPORT> registerProvider(supportCls: Class<SUPPORT>,provider: SupportProvider<SUPPORTIMPL>) {
    synchronized(Lock) {
        supportsProviderMap[supportCls] = provider
        if (supportsMap.containsKey(supportCls)) {
            supportsMap.remove(supportCls)
        }
    }
}

规划

开发到现在,这边的三个组件现已能够根本完结咱们关于组件化中心需求,但是,也是有一些方向能够进一步优化整套计划的运用:

  • Modular-Support 组件引进依靠注入的办法完结 API 的调用,运用方能够不再需求关怀实例目标的获取
  • Modular-Support 组件能够供给给 Weex、RN、H5、Flutter 事务一些原生的功用
  • Modular-Plugin 能够进一步紧缩打包时刻,并且让开发中的依靠装备愈加灵活
  • Modular-Plugin 持续优化办理和依靠打包的功用,提高功率

重视大众号:Android苦做舟
解锁 《Android十三大板块文档》,让学习更靠近未来实战。已构成PDF版

内容如下

1.2022最新Android11位大厂面试专题,128道附答案
2.音视频大合集,从初中高到面试包罗万象
3.Android车载运用大合集,从零开端一同学
4.功能优化大合集,离别优化烦恼
5.Framework大合集,从里到外剖析的明明白白
6.Flutter大合集,进阶Flutter高档工程师
7.compose大合集,拥抱新技能
8.Jetpack大合集,全家桶一次吃个够
9.架构大合集,轻松应对作业需求
10.Android根底篇大合集,根基安定楼房平地起
11.Flutter番外篇:Flutter面试+Flutter项目实战+Flutter电子书
12.高档Android组件化强化实战
13.十二模块之弥补部分:其他Android十一大常识系统

收拾不易,重视一下吧。ღ( ・ᴗ・` )