组件化、路由与依赖注入深入详解
更新: 5/15/2026 字数: 0 字 时长: 0 分钟
1. 组件化架构设计
1.1 为什么要组件化
单体工程的问题:
- 编译慢:改一行代码全量编译
- 耦合重:模块间直接引用,牵一发动全身
- 协作难:多团队改同一个模块容易冲突
- 无法独立运行:不能单独调试某个业务模块
组件化的目标:
app(壳工程,只做组装)
├── feature-home(首页模块)
├── feature-video(视频模块)
├── feature-user(用户模块)
├── feature-search(搜索模块)
├── lib-network(网络库)
├── lib-image(图片库)
├── lib-common(公共工具)
└── lib-base(基础框架)1.2 模块分层
┌─────────────────────────────────────┐
│ app(壳工程) │ ← 只做模块组装和初始化
├─────────────────────────────────────┤
│ feature-home │ feature-video │ ... │ ← 业务模块(互相不依赖)
├─────────────────────────────────────┤
│ lib-network │ lib-image │ lib-db │ ← 功能模块(可被业务模块依赖)
├─────────────────────────────────────┤
│ lib-common │ ← 公共工具(所有模块可依赖)
├─────────────────────────────────────┤
│ lib-base │ ← 基础框架(接口定义、路由表)
└─────────────────────────────────────┘核心原则:
- 业务模块之间不能直接依赖(通过路由或接口下沉通信)
- 上层可以依赖下层,下层不能依赖上层
- 公共接口下沉到 lib-base
1.3 模块独立运行
groovy
// feature-home/build.gradle.kts
// 通过 gradle.properties 中的标志位切换 application/library
if (isRunAlone) {
apply plugin: 'com.android.application'
} else {
apply plugin: 'com.android.library'
}
android {
defaultConfig {
if (isRunAlone) {
applicationId "com.example.home"
}
}
sourceSets {
main {
if (isRunAlone) {
// 独立运行时使用独立的 AndroidManifest(有 launcher Activity)
manifest.srcFile 'src/main/debug/AndroidManifest.xml'
} else {
manifest.srcFile 'src/main/AndroidManifest.xml'
}
}
}
}1.4 组件间通信方案
方案1:接口下沉 + SPI
kotlin
// lib-base 中定义接口
interface IUserService {
fun getUserInfo(): UserInfo
fun isLoggedIn(): Boolean
}
// feature-user 中实现
class UserServiceImpl : IUserService {
override fun getUserInfo() = UserRepository.getUser()
override fun isLoggedIn() = TokenManager.hasToken()
}
// 注册(通过 SPI 或手动注册)
// META-INF/services/com.example.base.IUserService
// com.example.user.UserServiceImpl
// 使用(在 feature-home 中)
val userService = ServiceLoader.load(IUserService::class.java).first()
if (userService.isLoggedIn()) { ... }方案2:路由框架(ARouter)
kotlin
// feature-user 中暴露服务
@Route(path = "/user/service")
class UserServiceImpl : IProvider, IUserService {
override fun getUserInfo() = UserRepository.getUser()
override fun init(context: Context) {}
}
// feature-home 中使用
val userService = ARouter.getInstance()
.build("/user/service")
.navigation() as IUserService方案3:EventBus / LiveDataBus
kotlin
// 发送事件(feature-user)
LiveDataBus.get<LoginEvent>("login_event").post(LoginEvent(userId))
// 接收事件(feature-home)
LiveDataBus.get<LoginEvent>("login_event").observe(this) { event ->
refreshUserInfo(event.userId)
}2. 路由框架原理(以 ARouter 为例)
2.1 整体架构
编译期(APT) 运行时
┌──────────────┐ ┌──────────────┐
│ @Route 注解 │ │ ARouter.init │
│ RouteProcessor│ ──生成──→ │ 加载路由表 │
│ 生成路由表类 │ │ │
└──────────────┘ │ navigation() │
│ 匹配路由 │
│ 执行拦截器 │
│ 跳转/获取服务 │
└──────────────┘2.2 编译期:APT 生成路由表
java
// 你写的代码
@Route(path = "/home/main")
public class HomeActivity extends AppCompatActivity { ... }
@Route(path = "/user/detail")
public class UserDetailActivity extends AppCompatActivity { ... }
// APT 生成的代码(每个模块一个类)
public class ARouter$$Group$$home implements IRouteGroup {
@Override
public void loadInto(Map<String, RouteMeta> atlas) {
atlas.put("/home/main", RouteMeta.build(
RouteType.ACTIVITY,
HomeActivity.class,
"/home/main",
"home", // group
null, // paramsType
-1 // priority
));
}
}
// 根路由表(索引 group → 对应的 IRouteGroup 类)
public class ARouter$$Root$$app implements IRouteRoot {
@Override
public void loadInto(Map<String, Class<? extends IRouteGroup>> routes) {
routes.put("home", ARouter$$Group$$home.class);
routes.put("user", ARouter$$Group$$user.class);
}
}2.3 运行时:路由查找与跳转
java
// ARouter.init() 加载路由表
public static void init(Application application) {
// 1. 扫描 dex 文件,找到所有 ARouter$$Root$$ 开头的类
Set<String> routerMap = ClassUtils.getFileNameByPackageName(
context, "com.alibaba.android.arouter.routes");
// 2. 反射实例化,加载路由表到内存
for (String className : routerMap) {
if (className.startsWith(ROOT_PREFIX)) {
((IRouteRoot) Class.forName(className).newInstance())
.loadInto(Warehouse.groupsIndex);
}
}
}
// navigation() 跳转
public Object navigation(Context context, Postcard postcard, int requestCode) {
// 1. 根据 path 查找路由信息
LogisticsCenter.completion(postcard);
// 内部:从 Warehouse.routes 中查找 RouteMeta
// 如果 group 还没加载,先加载 group
// 2. 执行拦截器链
interceptorService.doInterceptions(postcard, new InterceptorCallback() {
@Override
public void onContinue(Postcard postcard) {
// 3. 拦截器通过,执行跳转
_navigation(context, postcard, requestCode);
}
@Override
public void onInterrupt(Throwable exception) {
// 被拦截
}
});
}
private Object _navigation(Context context, Postcard postcard, int requestCode) {
switch (postcard.getType()) {
case ACTIVITY:
Intent intent = new Intent(context, postcard.getDestination());
intent.putExtras(postcard.getExtras());
ActivityCompat.startActivityForResult(activity, intent, requestCode);
break;
case PROVIDER:
// 获取服务实例(单例)
return postcard.getProvider();
case FRAGMENT:
// 创建 Fragment 实例
Fragment fragment = postcard.getDestination().newInstance();
fragment.setArguments(postcard.getExtras());
return fragment;
}
}2.4 拦截器
kotlin
// 登录拦截器:未登录时跳转到登录页
@Interceptor(priority = 8, name = "登录拦截器")
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)
}
}
override fun init(context: Context) {}
}
// 使用:标记需要登录的页面
@Route(path = "/order/detail", extras = NEED_LOGIN)
class OrderDetailActivity : AppCompatActivity()2.5 降级策略
kotlin
// 全局降级:路由找不到时的兜底
class GlobalDegradeService : DegradeService {
override fun onLost(context: Context, postcard: Postcard) {
// 跳转到 404 页面或 H5 兜底页
ARouter.getInstance()
.build("/common/webview")
.withString("url", "https://example.com${postcard.path}")
.navigation()
}
override fun init(context: Context) {}
}3. 依赖注入(Hilt)
3.1 Hilt 核心概念
kotlin
// 1. @HiltAndroidApp:触发 Hilt 代码生成,创建应用级组件
@HiltAndroidApp
class MyApplication : Application()
// 2. @AndroidEntryPoint:标记需要注入的 Android 类
@AndroidEntryPoint
class MainActivity : AppCompatActivity() {
// 3. @Inject:标记需要注入的字段
@Inject lateinit var userRepository: UserRepository
// 4. ViewModel 注入
private val viewModel: MainViewModel by viewModels()
}
// 5. @HiltViewModel
@HiltViewModel
class MainViewModel @Inject constructor(
private val userRepository: UserRepository,
private val savedStateHandle: SavedStateHandle
) : ViewModel()3.2 Module 和 Provides
kotlin
// 提供接口的实现
@Module
@InstallIn(SingletonComponent::class) // 作用域:Application 级别
object NetworkModule {
@Provides
@Singleton // 单例
fun provideOkHttpClient(): OkHttpClient {
return OkHttpClient.Builder()
.connectTimeout(30, TimeUnit.SECONDS)
.addInterceptor(LoggingInterceptor())
.build()
}
@Provides
@Singleton
fun provideRetrofit(okHttpClient: OkHttpClient): Retrofit {
return Retrofit.Builder()
.baseUrl("https://api.example.com/")
.client(okHttpClient) // Hilt 自动注入 OkHttpClient
.addConverterFactory(GsonConverterFactory.create())
.build()
}
@Provides
fun provideApiService(retrofit: Retrofit): ApiService {
return retrofit.create(ApiService::class.java)
}
}
// 绑定接口到实现
@Module
@InstallIn(SingletonComponent::class)
abstract class RepositoryModule {
@Binds
@Singleton
abstract fun bindUserRepository(impl: UserRepositoryImpl): UserRepository
}3.3 Scope(作用域)
kotlin
// Hilt 预定义的组件和作用域
@SingletonComponent → @Singleton → Application 生命周期
@ActivityRetainedComponent → @ActivityRetainedScoped → ViewModel 生命周期(配置变更不销毁)
@ViewModelComponent → @ViewModelScoped → ViewModel 生命周期
@ActivityComponent → @ActivityScoped → Activity 生命周期
@FragmentComponent → @FragmentScoped → Fragment 生命周期
@ViewComponent → @ViewScoped → View 生命周期
@ServiceComponent → @ServiceScoped → Service 生命周期kotlin
// 示例:Activity 级别的作用域
@Module
@InstallIn(ActivityComponent::class)
object ActivityModule {
@Provides
@ActivityScoped // 每个 Activity 实例一个,Activity 销毁时释放
fun provideAnalytics(activity: Activity): AnalyticsTracker {
return AnalyticsTracker(activity)
}
}3.4 Qualifier(限定符)
kotlin
// 同一类型有多个实现时,用 Qualifier 区分
@Qualifier
@Retention(AnnotationRetention.BINARY)
annotation class AuthInterceptor
@Qualifier
@Retention(AnnotationRetention.BINARY)
annotation class LoggingInterceptor
@Module
@InstallIn(SingletonComponent::class)
object InterceptorModule {
@Provides
@AuthInterceptor
fun provideAuthInterceptor(): Interceptor = AuthInterceptorImpl()
@Provides
@LoggingInterceptor
fun provideLoggingInterceptor(): Interceptor = HttpLoggingInterceptor()
}
// 使用时指定
@Provides
fun provideOkHttpClient(
@AuthInterceptor authInterceptor: Interceptor,
@LoggingInterceptor loggingInterceptor: Interceptor
): OkHttpClient {
return OkHttpClient.Builder()
.addInterceptor(authInterceptor)
.addInterceptor(loggingInterceptor)
.build()
}3.5 Hilt 的编译期原理
Hilt 在编译期做了什么:
1. @HiltAndroidApp 处理
→ 生成 Hilt_MyApplication(继承 MyApplication)
→ 在 AndroidManifest 中替换 Application 类名
→ 创建 SingletonComponent(Dagger 组件)
2. @AndroidEntryPoint 处理
→ 生成 Hilt_MainActivity(继承 MainActivity)
→ 在 onCreate 中调用 inject()
→ 从对应的 Component 中获取依赖并注入
3. @Module + @InstallIn 处理
→ 将 Module 安装到指定的 Component
→ 生成 Dagger 的依赖图(DAG)
→ 编译期验证依赖是否完整(缺少依赖会编译报错)与手动依赖管理的对比:
kotlin
// 手动创建(没有 DI)
val okHttpClient = OkHttpClient.Builder().build()
val retrofit = Retrofit.Builder().client(okHttpClient).build()
val apiService = retrofit.create(ApiService::class.java)
val repository = UserRepositoryImpl(apiService)
val viewModel = MainViewModel(repository)
// 问题:创建顺序、生命周期、单例管理全靠手动
// Hilt(自动管理)
@HiltViewModel
class MainViewModel @Inject constructor(
private val repository: UserRepository // Hilt 自动创建并注入整条依赖链
) : ViewModel()