Android 组件化学习项目(Kotlin + AGP8+)

Android 组件化学习项目(Kotlin + AGP8+)

一个适合学习 Android 组件化基础实战项目(Kotlin + AGP 8+)。

本仓库用两个业务组件对照讲解,而不是只堆模块数量:

仓库地址:点击进入

业务 Gradle 模块 组件化方式
商城 component-mall 普通组件化(一个 Library 模块包打天下)
用户 user-api + user-impl API + Impl(合同与施工队分离)

项目还包含:

  • common 公共模块
  • ServiceManager 服务注册与获取
  • UserRouter 组件路由入口
  • Application 统一组件初始化
  • 组件通信(接口调用,而非直连 Activity)

适合:

  • Android 组件化入门与进阶
  • 理解「普通组件」与「api + impl」的差别
  • 理解 Gradle 依赖与代码调用两条线
  • 为企业多模块架构打基础

快速上手与构建说明见仓库根目录 READ.md


零、先搞懂:什么叫「组件」?

把每个 Library 模块 想成公司里的一个业务部门

  • 对外只承诺一件事:例如「打开用户页」「打开商城页」
  • 内部自己管页面、数据:ActivityRepository 不应被总部随意调用
  • app 是「总部」:负责把各部门编进 APK,启动时统一「点名报到」(init()

组件化要解决的核心问题只有一句:

txt 复制代码
总部需要用人家的能力,但不应该绑死在人家内部的 Activity 类名上。

真正组件化的核心不是 拆了多少个 module,而是:

txt 复制代码
让调用方(高层)不依赖具体实现(低层细节)。

一、项目结构(与仓库一致)

txt 复制代码
ComponentDemo
│
├── app                    主工程(壳、首页、组装依赖)
├── common                 公共模块(ServiceManager、BaseActivity 等)
├── component-mall         商城组件(普通组件化)
├── user-api               用户组件 · 对外契约
├── user-impl              用户组件 · 具体实现
└── component-user         旧版用户组件(settings 中已注释,仅作对照)

settings.gradle.kts 当前包含:

kotlin 复制代码
include(":app")
include(":component-mall")
include(":common")
include(":user-api")
include(":user-impl")
// include(":component-user")  // 旧写法,已停用

二、模块说明与职能一览

模块 类型 一句话职能 是否业务组件
app application 安装包入口;依赖并组装各模块;Application 里初始化组件 否(壳)
common library 公共基类、ServiceManager、工具;不写具体业务
component-mall library 商城业务 + MallService 对外入口 是(Mall)
user-api library 用户对外「合同」:IUserServiceUserRouter 是(User 对外)
user-impl library 用户页面与实现;UserComponent.init() 注册服务 是(User 对内)
component-user library(停用) 旧版单模块用户组件,与 api+impl 对照 历史-普通组件写法

2.1 app ------ 主工程

职能:

  • 生成可安装的 APK
  • MainActivity:演示如何打开用户页、商城页
  • App.kt:调用 UserComponent.init()MallComponent.init()
  • AppRouter:可集中封装商城跳转(MallService.start

Gradle 依赖(实战配置):

kotlin 复制代码
implementation(project(":common"))
implementation(project(":component-mall"))
implementation(project(":user-api"))
implementation(project(":user-impl"))

规范: 打开用户页应走 UserRouter,不要 Intent(this, UserActivity::class.java)


2.2 common ------ 公共模块

职能: 所有业务组件都能依赖的「基础设施」,避免复制粘贴。

类 / 文件 作用(通俗说)
ServiceManager 服务台:登记、领取各组件实现的接口
BaseActivity 业务 Activity 的公共父类
IService 旧式组件服务标记(商城 MallService 使用)
ToastUtilsRouterPath 工具与常量

依赖规则: common 不依赖 任何 component-malluser-apiuser-impl


2.3 component-mall ------ 商城组件(普通组件化)

职能: 商城相关能力全部放在一个 Library 里,对外只暴露 MallService

目录结构(仓库实际):

txt 复制代码
component-mall
 ├── api/MallService.kt       对外入口:start() 打开商城页
 ├── ui/MallActivity.kt        商城界面
 ├── repository/MallRepository.kt
 ├── data/Product.kt
 └── MallComponent.kt          组件初始化

Gradle:

kotlin 复制代码
implementation(project(":component-mall"))

调用:

kotlin 复制代码
MallService.start(this)
// 或经 AppRouter:AppRouter.openMall(context)

2.4 user-api + user-impl ------ 用户组件(API + Impl)

user-api(合同)

文件 职能
IUserService 定义对外能力(如 openUser
UserRouter 统一入口:UserRouter.service()

依赖:common不依赖 user-impl

user-impl(施工队)

文件 职能
UserServiceImpl 实现 IUserService,内部跳转 UserActivity
UserActivity 用户界面
UserRepositoryUser 数据层
UserComponent init() 时向 ServiceManager 注册实现

依赖:commonuser-api

调用(推荐):

kotlin 复制代码
UserRouter.service().openUser(this)

二(补充)、两大业务组件对比

维度 商城 component-mall 用户 user-api + user-impl
模式名 普通 Library 组件化 API + Impl 分离
模块个数 1 个业务模块 2 个(契约 + 实现)
对外入口 MallService 对象 IUserService + UserRouter
思想 偏「面向实现」 偏「面向接口」
app 依赖 implementation(:component-mall) user-api + user-impl
替换实现 较难(整包绑定) 可换 impl 或自研后 register
学习阶段 第一阶段 第二阶段(企业常用)
本仓库角色 对照组:简单直观 主推:企业组件化第一阶段

记忆比喻:

  • Mall :固定电话 MallService,拨号即用,简单。
  • User :只认协议 IUserService;具体谁送货(UserServiceImpl)在启动时登记到服务台(ServiceManager),以后可换实现而不改总部代码。

二(补充)、引用关系(Gradle + 代码)

组件化要同时看清两条线:编译期谁依赖谁运行期谁调用谁

Gradle 依赖(编译期)

txt 复制代码
                         ┌─────────┐
                         │   app   │
                         └────┬────┘
              ┌────────────────┼────────────────┐
              ▼                ▼                ▼
        ┌──────────┐    ┌──────────┐    ┌─────────────────┐
        │  common  │    │ user-api │    │ component-mall  │
        └──────────┘    └────┬─────┘    └────────┬────────┘
                             │                     │
                             ▼                     │
                      ┌────────────┐               │
                      │ user-impl  │───────────────┘
                      └────────────┘
                      (impl、mall 均依赖 common;
                       impl 只依赖 api,不依赖 mall)

依赖倒置(User):

txt 复制代码
user-impl  →  user-api  →  common
app        →  user-api、user-impl、component-mall、common

注意:

txt 复制代码
app 不应该在业务代码里直接调用 impl 内部实现(Activity、Repository)

Gradle 的 implementation 不会 禁止你在 app 里 import UserActivity,靠的是架构规范(下文第二十三、二十四节)。


代码调用(运行期)

用户组件

txt 复制代码
App.onCreate()
  → UserComponent.init()
  → ServiceManager.register(IUserService, UserServiceImpl)

MainActivity 点击
  → UserRouter.service()
  → ServiceManager.get(IUserService)
  → openUser()
  → UserServiceImpl 内部 startActivity(UserActivity)

商城组件

txt 复制代码
MainActivity 点击
  → AppRouter.openMall()
  → MallService.start()
  → MallActivity

商城路径更短,没有 ServiceManager,也更容易写成和内部类强耦合。


三、普通组件化(component-mall)

商城组件模块名是 component-mall(文档里简称 Mall 业务即可)。

属于:

txt 复制代码
普通 Library 组件化

结构:

txt 复制代码
component-mall
 ├── api          MallService:对外唯一推荐入口
 ├── ui           MallActivity
 ├── repository   数据获取
 └── data         Product 等模型

App 直接依赖整个模块:

kotlin 复制代码
implementation(project(":component-mall"))

调用:

kotlin 复制代码
MallService.start(this)

初始化(与 User 一样,在 Application 里):

kotlin 复制代码
MallComponent.init()

四、普通组件化特点

优点:

  • 简单
  • 容易理解
  • 开发速度快
  • 适合中小项目、学习第一阶段

缺点:

  • app 可以 访问组件内部类(如 MallActivity
  • 耦合较高
  • 不适合大型团队协作边界
  • 不适合插件化、动态化

例如,技术上 App 可以直接:

kotlin 复制代码
// 能编译,但不推荐
startActivity(Intent(this, MallActivity::class.java))

这属于:

txt 复制代码
面向实现编程

五、api + impl 组件化(User)

User 组件拆成:

txt 复制代码
user-api      对外合同
user-impl     对内实现

这是:

txt 复制代码
企业组件化里非常核心的思想(本项目第一阶段)

六、目录结构(仓库实际)

txt 复制代码
user-api
 ├── IUserService.kt
 └── UserRouter.kt

user-impl
 ├── component/UserComponent.kt
 ├── service/UserServiceImpl.kt
 ├── ui/UserActivity.kt
 ├── repository/UserRepository.kt
 └── data/User.kt

common
 └── service/ServiceManager.kt

七、依赖关系(重点)

txt 复制代码
app  →  user-api
app  →  user-impl        (静态打包需要,见第十九~二十二节)
app  →  component-mall
app  →  common

user-impl  →  user-api
user-impl  →  common

user-api  →  common

component-mall  →  common

原则:

txt 复制代码
业务代码:app 只通过 user-api(UserRouter / IUserService)使用用户能力
Gradle:app 为打完整 APK,仍需依赖 user-impl

八、为什么要 api + impl

核心思想:

txt 复制代码
面向接口编程

而不是:

txt 复制代码
面向实现编程

九、错误写法

kotlin 复制代码
startActivity(
    Intent(this, UserActivity::class.java)
)

问题:

  • app 知道内部 Activity
  • 高耦合
  • impl 难以替换
  • 后续难扩展、难做组件独立仓库

十、正确写法

kotlin 复制代码
UserRouter
    .service()
    .openUser(this)

这样,App 在设计上只知道:

txt 复制代码
IUserService
UserRouter

不应该依赖:

txt 复制代码
UserActivity
UserRepository
UserServiceImpl

(这些应关在 user-impl 里。)


十一、IUserService

kotlin 复制代码
interface IUserService {

    fun openUser(context: Context)
}

作用:

txt 复制代码
定义组件对外能力(合同上写清楚能干什么)

放在 user-api,任何调用方只需依赖 api 模块即可看到这份合同。


十二、UserServiceImpl

kotlin 复制代码
class UserServiceImpl : IUserService {

    override fun openUser(context: Context) {

        context.startActivity(
            Intent(context, UserActivity::class.java)
        )
    }
}

作用:

txt 复制代码
真正实现组件逻辑(施工队干活)

跳转 UserActivity 的代码只应出现在 impl,不应散落在 app 的其他业务里。


十三、ServiceManager(重点)

kotlin 复制代码
object ServiceManager

作用:

txt 复制代码
管理所有组件服务(登记 + 领取)

类似:

txt 复制代码
简化版 IoC 容器 / 公司前台服务台

位置:common ,各业务组件在 init() 时注册,调用方通过 api 里的 UserRouter 间接获取。


十四、注册服务

UserComponent.init() 中:

kotlin 复制代码
ServiceManager.register(
    IUserService::class.java,
    UserServiceImpl()
)

含义:

txt 复制代码
把「用户能力」的实现登记到服务台,键是 IUserService 类型

十五、获取服务

UserRouter 中:

kotlin 复制代码
ServiceManager.get(
    IUserService::class.java
)

对外暴露为:

kotlin 复制代码
UserRouter.service()  // 返回 IUserService

十六、为什么会出现 NPE

错误现象:

txt 复制代码
IUserService.openUser()
on a null object reference

常见原因:

txt 复制代码
IUserService 还没注册到 ServiceManager

也就是:

kotlin 复制代码
UserComponent.init()

没执行,或 Application 根本没跑起来。


十七、正确初始化流程

App.kt

kotlin 复制代码
class App : Application() {

    override fun onCreate() {
        super.onCreate()

        UserComponent.init()
        MallComponent.init()
    }
}

两个组件都要初始化: User 注册服务;Mall 目前可做日志、预加载等扩展。


十八、Manifest 必须配置 Application

xml 复制代码
<application
    android:name=".App"
    />

否则:

txt 复制代码
Application.onCreate 不会执行
→ UserComponent.init() 不会执行
→ 调用 UserRouter 容易崩溃

十九、为什么 app 还需要依赖 user-impl

很多人会问:

txt 复制代码
既然已经 api + impl
为什么 app 还需要:
implementation(project(":user-impl"))

原因:

txt 复制代码
当前还是静态组件化

不是:

txt 复制代码
动态组件化 / 插件化

二十、为什么 impl 必须参与编译

因为 Android 里:

txt 复制代码
Activity
布局资源
AndroidManifest

都必须:

txt 复制代码
在编译期打进 APK

否则:

txt 复制代码
ActivityNotFoundException

UserActivity 声明在 user-impl 的 Manifest 中,所以主工程组装时必须带上 impl 模块。


二十一、当前属于什么组件化

当前属于:

txt 复制代码
静态组件化

特点:

txt 复制代码
编译期就把 app、common、component-mall、user-api、user-impl 打进同一个 APK

二十二、真正不依赖 impl 的方案

只有走向:

  • 动态 Feature Module
  • 插件化
  • 动态加载 Dex

才有可能在依赖图上做到:

txt 复制代码
app 只依赖 user-api(运行时再去加载 impl)

本学习项目刻意用静态方式,先把 api/impl 思想练熟。


二十三、为什么 app 仍能看到 impl 类

即使:

kotlin 复制代码
implementation(project(":user-impl"))

App 仍然能 import

kotlin 复制代码
import com.example.user_impl.ui.UserActivity

原因:

txt 复制代码
implementation 不是「源码级隔离」

它只是:

txt 复制代码
控制依赖传递,不暴露给「依赖 app 的第三方模块」

同工程内的 app 模块仍能看到 impl 里所有 public 类。


二十四、真正企业如何限制

企业一般通过:

  • lint 自定义规则
  • detekt
  • Code Review
  • ArchUnit
  • Kotlin internal + 模块可见性
  • 禁止 app 模块 import *.user_impl.*

来约束:

txt 复制代码
app 业务代码禁止直接调用 impl

本仓库学习建议: 打开用户页只写 UserRouter.service().openUser(),当作纪律练习。


二十五、当前项目已经具备的企业思想

当前已经具备:

  • api + impl 分层(User)
  • 普通组件对照(Mall)
  • Router(UserRouterAppRouter
  • ServiceManager 注册发现
  • 面向接口编程
  • 模块边界与依赖倒置(impl → api)
  • Application 统一组件初始化

已经属于:

txt 复制代码
企业组件化第一阶段(静态 + 接口化)

二十六、当前还缺少什么

真正企业级通常还需要:

  • 自动注册组件(KSP / ASM)
  • 组件独立调试(单独 Run 某个 Library)
  • component-mall 也升级为 mall-api / mall-impl
  • 动态组件化、插件化
  • 路由表自动生成(ARouter 等)
  • Convention Plugin 统一 Gradle 配置
  • 组件依赖治理与版本对齐

二十七、推荐学习路线(结合本仓库)

第一阶段:普通组件化

对照 component-mall

  • MallServiceMallActivityMallComponent
  • 理解 implementation(project(":component-mall"))

第二阶段:api + impl

对照 user-apiuser-impl

  • IUserServiceUserRouter
  • UserServiceImplUserComponent

第三阶段:ServiceManager + 初始化

  • 搞懂 register / get
  • 搞懂 App + Manifest
  • 搞懂 NPE 与未注册的关系

第四阶段:自动注册组件

(本仓库尚未实现,可作为扩展作业。)


第五阶段:组件独立运行

给单个 Library 加独立 Application 与 Manifest,单独 Run。


第六~七阶段:动态组件化、插件化

理解第二十二节:为何那时 app 才可能「只依赖 api」。


二十八、这个学习项目的真正价值

它不是:

txt 复制代码
简单 multi-module Demo

而是:

在一个 APK 里并排演示两种组件写法,真正学习:

  • Android 组件化
  • 模块职能与边界
  • Gradle 引用 vs 代码调用
  • 依赖倒置
  • 面向接口与面向实现的差别

的起点。


二十九、当前项目适合谁

适合:

  • Android 初学组件化
  • Android 进阶,准备接触企业多模块
  • 想在一套代码里对比 Mall 与 User
  • 想理解 api + impl、Router、ServiceManager

三十、总结

Mall(component-mall):

txt 复制代码
普通组件化

特点:

  • 简单、开发快
  • 一个模块包含 api + ui + data
  • 耦合相对较高,适合入门与中小功能

User(user-api + user-impl):

txt 复制代码
api + impl 组件化

特点:

  • 调用方依赖 api,面向接口
  • 实现可替换、边界更清晰
  • 更适合大型项目、多团队、后续动态化演进

common:

txt 复制代码
公共底座,不是业务组件

app:

txt 复制代码
壳 + 组装 + 初始化,不写重业务

三十一、最后一句话(核心)

真正组件化核心:

不是:

txt 复制代码
拆多少 module

而是:

txt 复制代码
让高层不依赖具体实现

这才是:

txt 复制代码
Android 企业组件化真正核心思想

本项目中:

  • 点「打开用户组件」→ 走 接口 + Router + ServiceManager(推荐范式)
  • 点「打开商城组件」→ 走 MallService(简单范式)

两条路都跑通之后,再决定团队新业务默认哪一套。

相关推荐
sulikey2 小时前
个人Linux操作系统学习笔记7 - 进程理解
linux·笔记·学习·操作系统·进程·pid
程序喵大人2 小时前
C++ 程序员转型 AI Infra 学习路线
c++·人工智能·学习·ai infra
问心无愧05132 小时前
ctf show web入门100
android·ide·笔记·android studio
段一凡-华北理工大学2 小时前
工业领域的Hadoop架构学习~系列文章14:Hadoop集群部署 - 从规划到上线的全流程实践
大数据·数据库·人工智能·hadoop·学习·架构·高炉炼铁
J.Kuchiki2 小时前
【PostgreSQL内核学习:Unique 算子源码深度解读学习】
数据库·学习·postgresql
暗冰ཏོ2 小时前
2026 App 开发完整指南:Android、iOS、跨平台开发与安卓应用上线全流程
android·ios·uni-app·web app·app开发
我是唐青枫2 小时前
Kotlin apply 详解:对象初始化、链式配置与实战示例
kotlin
三少爷的鞋2 小时前
为什么 Clean Architecture 能让 ViewModel 保持轻量?
android
踏着七彩祥云的小丑2 小时前
嵌入式测试学习第 28 天:网络调试助手使用、TCP服务端客户端实操
单片机·嵌入式硬件·学习