从基础概念到实战应用,系统梳理 Android 架构演进之路
一、基础概念 1.1 为什么需要架构优化? 随着业务迭代,单体 App 会遇到诸多问题:
问题
表现
影响
代码耦合严重
模块间直接 import,循环依赖
难以维护、编译慢
编译效率低
改一行代码全量编译
开发效率下降
团队协作冲突
多人修改同一工程
Git 冲突频繁
无法独立开发
强依赖主工程
无法并行开发、独立测试
复用困难
业务逻辑与 UI 混在一起
跨项目复用成本高
1.2 三种架构模式辨析 1 2 3 4 5 6 7 8 9 10 11 12 ┌─────────────────────────────────────────────────────────────────┐ │ 架构演进路径 │ ├─────────────────────────────────────────────────────────────────┤ │ │ │ 单体架构 ──► 模块化 ──► 组件化 ──► 插件化 │ │ │ │ │ │ │ │ │ │ │ │ │ │ ▼ ▼ ▼ ▼ │ │ 按功能拆分 Gradle模块 路由解耦 运行时动态加载 │ │ 包结构划分 build独立 ARouter等 热更新/按需下载 │ │ │ └─────────────────────────────────────────────────────────────────┘
模块化(Modularization)
定义 :按业务功能将代码拆分为独立的 Gradle 模块,每个模块有清晰的 build.gradle 和边界
特点 :物理隔离、可独立编译,通常仍在同一 Git 仓库内
粒度 :中等,如「用户模块」「订单模块」「支付模块」
组件化(Componentization)
定义 :在模块化基础上,通过路由/服务发现实现模块间完全解耦,可独立开发、独立运行
特点 :依赖倒置、接口隔离、可单模块调试(Application 切换)
粒度 :较细,如「登录组件」「分享组件」「埋点组件」
插件化(Pluginization)
定义 :插件 APK/Dex 可动态加载、热更新,主 App 与插件解耦到运行时
特点 :运行时动态、按需下载、可热修复、减小包体积
粒度 :独立 APK/模块
二、模块化原理与实践 2.1 模块化的核心原则
单一职责 :每个模块只负责一块业务
接口隔离 :模块间通过接口/路由通信,不暴露实现细节
依赖倒置 :依赖抽象(interface)而非具体实现
2.2 Gradle 多模块目录结构 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 MyApp/ ├── app/ # 主工程壳 │ └── build.gradle ├── module_user/ # 用户模块 │ ├── build.gradle │ └── src/main/ ├── module_order/ # 订单模块 │ └── build.gradle ├── module_payment/ # 支付模块 │ └── build.gradle ├── common_base/ # 公共基础库 │ ├── network/ │ ├── utils/ │ └── base/ ├── common_router/ # 路由层 │ └── build.gradle ├── build.gradle └── settings.gradle
2.3 settings.gradle 与模块声明 1 2 3 4 5 6 7 8 rootProject.name = "MyApp" include ':app' include ':module_user' include ':module_order' include ':module_payment' include ':common_base' include ':common_router'
2.4 模块间依赖配置 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 dependencies { implementation project(':module_user' ) implementation project(':module_order' ) implementation project(':module_payment' ) implementation project(':common_router' ) implementation project(':common_base' ) } dependencies { implementation project(':common_base' ) implementation project(':common_router' ) } dependencies { implementation 'androidx.appcompat:appcompat:1.6.1' implementation 'com.squareup.retrofit2:retrofit:2.9.0' }
2.5 模块间通信:接口 + 实现注入 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 interface IUserService { fun getCurrentUser () : User? fun logout () } class UserServiceImpl : IUserService { override fun getCurrentUser () : User? = UserManager.currentUser override fun logout () { } } object ServiceLocator { var userService: IUserService? = null } class OrderActivity : AppCompatActivity () { private val userService: IUserService? by lazy { ServiceLocator.userService } fun showUserInfo () { userService?.getCurrentUser()?.let { user -> } } }
三、组件化原理与实现 3.1 组件化架构图 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 ┌──────────────────┐ │ app (壳) │ │ 主工程/组装 │ └────────┬─────────┘ │ ┌──────────────┼──────────────┐ │ │ │ ▼ ▼ ▼ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ module_user │ │ module_order│ │module_payment│ └──────┬──────┘ └──────┬──────┘ └──────┬──────┘ │ │ │ └───────────────┼───────────────┘ │ ┌──────▼──────┐ │ ARouter │ │ 路由中间层 │ └──────┬──────┘ │ ┌──────▼──────┐ │ common_base │ │ 基础组件库 │ └─────────────┘
3.2 路由方案对比
方案
原理
优点
缺点
ARouter
注解 + 编译期生成路由表
阿里出品、生态成熟、支持拦截器
需引入注解处理器
WMRouter
美团方案,分组路由
按需加载、包体积优化
学习成本略高
CC (Component Call)
组件调用框架
同步/异步调用、跨进程
概念较多
手写路由表
Map 映射
简单、无依赖
维护成本高
3.3 单模块独立运行(Application 切换) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 android { defaultConfig { if (project.hasProperty('runAsApp' ) && runAsApp) { applicationId "com.example.user" } } sourceSets { main { manifest.srcFile 'src/main/AndroidManifest.xml' if (project.hasProperty('runAsApp' ) && runAsApp) { manifest.srcFile 'src/main/debug/AndroidManifest.xml' } } } }
1 2 3 4 5 6 7 8 9 10 11 12 13 <manifest > <application android:name =".UserDebugApplication" android:label ="User Module Debug" > <activity android:name =".UserActivity" android:exported ="true" > <intent-filter > <action android:name ="android.intent.action.MAIN" /> <category android:name ="android.intent.category.LAUNCHER" /> </intent-filter > </activity > </application > </manifest >
1 2 ./gradlew :module_user:installDebug -PrunAsApp=true
3.4 ARouter 使用与原理 3.4.1 添加依赖 1 2 3 4 5 dependencies { implementation 'com.alibaba:arouter-api:1.5.2' kapt 'com.alibaba:arouter-compiler:1.5.2' }
3.4.2 路由配置与跳转 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 @Route(path = "/order/list" ) class OrderListActivity : AppCompatActivity () { @Autowired lateinit var userId: String override fun onCreate (savedInstanceState: Bundle ?) { super .onCreate(savedInstanceState) ARouter.getInstance().inject(this ) } } ARouter.getInstance() .build("/order/list" ) .withString("userId" , "12345" ) .navigation()
3.4.3 服务发现(跨模块获取实现) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 interface IOrderService { fun getOrderCount (userId: String ) : Int } @Route(path = "/service/order" ) class OrderServiceImpl : IOrderService { override fun getOrderCount (userId: String ) : Int = } val orderService = ARouter.getInstance() .navigation(IOrderService::class .java) orderService?.getOrderCount("12345" )
3.4.5 ARouter 核心源码解析 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 class Warehouse { static Map<String, RouteMeta> routes = ConcurrentHashMap() static Map<String, Class<? extends IRouteGroup>> groupsIndex = ConcurrentHashMap() } fun navigation (path: String ) : Postcard { val meta = Warehouse.routes[path] return Postcard(path, meta) } fun Postcard.navigation () : Any? { return _ARouter.navigation(context, this , requestCode, navigationCallback) } fun _ARouter.navigation (...) { when (meta.type) { RouteType.ACTIVITY -> { val intent = Intent(context, meta.destination) context.startActivity(intent) } RouteType.ISERVICE -> { return meta.destination.newInstance() } } }
3.5 依赖关系设计原则 1 2 3 4 5 6 7 8 9 业务模块 (user/order/payment) │ ├── 只依赖 common_base、common_router (arouter-api) │ └── 业务模块之间 不直接依赖 app 壳工程 │ └── 依赖所有业务模块,负责组装和 Application 初始化
四、插件化原理与实现 4.1 插件化的应用场景
热更新 :修复线上 Bug 无需发版
按需加载 :减小包体积,冷启动只加载核心
动态能力 :运营活动插件、A/B 测试模块
多端复用 :同一套插件可被主 App、Split APK 加载
4.2 Android 插件化技术选型
方案
说明
适用场景
Dynamic Feature Modules
Google 官方,AGP 支持
按需下载、免安装功能模块
VirtualAPK
滴滴开源
完整的插件化框架
RePlugin
360 出品
插件即独立 APK,稳定成熟
Shadow
腾讯开源
支持 Android 10+,零 Hook
Atlas
阿里
大型 App 动态化
4.3 Dynamic Feature Modules(官方方案) 4.3.1 配置 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 include ':app' include ':feature:order' dependencies { implementation project(':common_base' ) dynamicFeatures = [':feature:order' ] } apply plugin: 'com.android.dynamic-feature' android { defaultConfig { minSdk 24 } } dependencies { implementation project(':app' ) }
4.3.2 按需下载与安装 1 2 3 4 5 6 7 8 9 10 11 12 13 14 val splitInstallManager = SplitInstallManagerFactory.create(context)val request = SplitInstallRequest.newBuilder() .addModule("order" ) .build() splitInstallManager.startInstall(request) .addOnSuccessListener { startActivity(Intent(this , OrderActivity::class .java)) } .addOnFailureListener { }
4.4 RePlugin 核心原理简述 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 ┌──────────────────────────────────────────────────────┐ │ Host App │ │ ┌────────────────────────────────────────────────┐ │ │ │ RePluginHostConfig / PluginManager │ │ │ │ - 加载插件 APK │ │ │ │ - 替换 ClassLoader │ │ │ │ - Hook Activity/Service 等组件 │ │ │ └────────────────────────────────────────────────┘ │ └──────────────────────────────────────────────────────┘ │ ┌───────────────┼───────────────┐ ▼ ▼ ▼ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ Plugin1 │ │ Plugin2 │ │ Plugin3 │ │ .apk │ │ .apk │ │ .apk │ └──────────┘ └──────────┘ └──────────┘
核心流程 :
Dex 加载 :将插件 APK 的 classes.dex 加入宿主的 DexPathList
资源加载 :创建 Resources,合并插件的 AssetManager
组件占位 :通过 Hook Instrumentation/IActivityManager,用占位 Activity 代理插件 Activity
4.5 手写简易插件加载(Dex 加载) 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 class PluginLoader (private val context: Context) { private val pluginClassLoaders = mutableMapOf<String, DexClassLoader>() fun loadPlugin (apkPath: String ) : Boolean { val optimizedDir = File(context.filesDir, "plugin_opt" ).apply { mkdirs() } val libDir = File(context.filesDir, "plugin_lib" ).apply { mkdirs() } val classLoader = DexClassLoader( apkPath, optimizedDir.absolutePath, null , context.classLoader ) pluginClassLoaders[apkPath] = classLoader return true } fun loadClass (apkPath: String , className: String ) : Class<*>? { val loader = pluginClassLoaders[apkPath] ?: return null return try { loader.loadClass(className) } catch (e: ClassNotFoundException) { null } } } val loader = PluginLoader(context)loader.loadPlugin("/sdcard/plugin.apk" ) val clazz = loader.loadClass("/sdcard/plugin.apk" , "com.plugin.MainActivity" )val activity = clazz.newInstance()
4.6 插件化资源加载 1 2 3 4 5 6 7 8 9 10 11 12 fun createPluginResources (hostResources: Resources , apkPath: String ) : Resources { val assets = AssetManager::class .java.newInstance() val addAssetPath = AssetManager::class .java.getMethod("addAssetPath" , String::class .java) addAssetPath.invoke(assets, apkPath) return Resources( assets, hostResources.displayMetrics, hostResources.configuration ) }
五、ARouter 框架源码深度解析 5.1 整体架构 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 ┌────────────────────────────────────────────────────────┐ │ ARouter │ │ - 对外 API:build、navigation、inject │ └────────────────────────────────────────────────────────┘ │ ┌─────────────────────────┴─────────────────────────────┐ │ _ARouter (内部实现) │ │ - LogisticsCenter:路由表、分组加载 │ │ - Warehouse:路由/服务/拦截器 存储 │ └────────────────────────────────────────────────────────┘ │ ┌─────────────────┼─────────────────┐ ▼ ▼ ▼ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ 注解处理器 │ │ 拦截器链 │ │ 降级策略 │ │ 编译期生成 │ │ 可扩展 │ │ DegradeService│ └──────────────┘ └──────────────┘ └──────────────┘
5.2 LogisticsCenter 路由表加载 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public static void init (Context context) { try { Set<String> routerMap = ClassUtils.getFileNameByPackageName( context, ROUTE_ROOT_PACKAGE); for (String className : routerMap) { if (className.startsWith(ROUTE_ROOT_PACKAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_ROOT)) { ((IRouteRoot) Class.forName(className).getConstructor().newInstance()) .loadInto(Warehouse.groupsIndex); } } } catch (Exception e) { throw new HandlerException ("ARouter init logistics center exception" ); } }
5.3 拦截器机制 1 2 3 4 5 6 7 8 9 10 11 12 @Interceptor(priority = 8) class LoginInterceptor : IInterceptor { override fun process (postcard: Postcard , callback: InterceptorCallback ) { if (postcard.extra == NEED_LOGIN && !UserManager.isLoggedIn()) { ARouter.getInstance().build("/user/login" ).navigation() callback.onInterrupt(null ) } else { callback.onContinue(postcard) } } }
六、实际项目应用案例 6.1 某电商 App 组件化拆分 1 2 3 4 5 6 7 8 9 10 11 app (壳工程) ├── module_home # 首页 ├── module_product # 商品详情 ├── module_cart # 购物车 ├── module_order # 订单 ├── module_user # 用户中心 ├── module_payment # 支付 ├── module_share # 分享(可复用) ├── module_analytics # 埋点(可复用) ├── common_router # ARouter 封装 └── common_base # 网络/缓存/UI 基础库
收益 :
编译时间:全量 6min → 单模块 1min
4 个业务线可并行开发,Git 冲突减少 60%
ShareModule、AnalyticsModule 复用到多个 App
6.2 路由表设计实战 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 object RouterPath { const val ORDER_LIST = "/order/list" const val ORDER_DETAIL = "/order/detail" const val USER_LOGIN = "/user/login" const val USER_PROFILE = "/user/profile" } object Router { fun toOrderList (userId: String ) { ARouter.getInstance() .build(RouterPath.ORDER_LIST) .withString("userId" , userId) .navigation() } fun toOrderDetail (orderId: String ) { ARouter.getInstance() .build(RouterPath.ORDER_DETAIL) .withString("orderId" , orderId) .navigation() } }
6.3 解耦实践:避免循环依赖 错误示例 :
1 2 module_order -> module_user (获取用户信息) module_user -> module_order (跳转订单列表)
形成循环依赖,Gradle 编译失败。
正确做法 :
1 2 module_order -> common_router module_user -> common_router
需要「用户信息」时,Order 通过 ARouter.getInstance().navigation(IUserService::class.java) 获取;需要「跳转订单」时,User 通过 ARouter.getInstance().build("/order/list").navigation() 跳转。
6.4 模块化 + 插件化组合:Dynamic Feature 实践 某资讯 App 将「小说」「漫画」等非核心功能做成 Dynamic Feature,用户首次进入时提示下载:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 fun isModuleInstalled (moduleName: String ) : Boolean { return SplitInstallManagerFactory.create(context) .installedModules.contains(moduleName) } fun openNovelModule () { if (isModuleInstalled("novel" )) { startActivity(Intent(this , NovelActivity::class .java)) } else { showDownloadDialog { installDynamicModule("novel" ) } } }
七、最佳实践与注意事项 7.1 模块拆分原则
高内聚低耦合 :模块内聚度高,模块间依赖少
按业务边界拆分 :参考 DDD 的 Bounded Context
基础组件下沉 :网络、缓存、日志等抽成 common_base
渐进式演进 :先模块化再组件化,避免一步到位导致成本过高
7.2 常见坑与规避
问题
原因
规避
编译顺序错误
模块间隐式依赖
严格检查 build.gradle,用 ./gradlew :module_x:dependencies 检查
路由表膨胀
所有页面都注册路由
仅对外暴露的页面注册,内部页面用 startActivity
启动变慢
初始化时加载全部路由
使用分组按需加载(ARouter 已支持)
R 文件冲突
多模块资源 ID 冲突
在 build.gradle 中设置 resourcePrefix "module_user_"
插件化兼容性
不同厂商 ROM 限制
优先使用 Dynamic Feature,第三方框架需充分测试
7.3 R 文件与资源隔离 1 2 3 4 android { resourcePrefix "user_" }
7.4 架构演进路线图 1 2 3 4 5 6 7 8 9 10 11 12 13 14 Phase 1: 模块化 ├── 拆分为 Gradle 多模块 ├── 建立 common_base、common_router └── 模块间通过接口通信 Phase 2: 组件化 ├── 引入 ARouter 路由 ├── 服务发现替代直接依赖 └── 支持单模块独立运行调试 Phase 3: 插件化(可选) ├── 非核心功能改为 Dynamic Feature ├── 或引入 RePlugin/Shadow 做完整插件化 └── 按需下载、热更新
八、总结
架构
适用场景
核心手段
模块化
中小型 App、团队 < 10 人
Gradle 多模块、接口、依赖配置
组件化
中大型 App、多业务线并行
ARouter、服务发现、单模块调试
插件化
需要动态化、热更新、包体积优化
Dynamic Feature、RePlugin、Shadow
架构没有银弹,需结合团队规模、业务复杂度、迭代节奏选择合适方案。建议从模块化起步,随着复杂度提升再逐步演进到组件化,插件化则按实际需求谨慎引入。
参考资源