Android开发中如何解耦主App与业务模块以支持外包协作
1. Class.forName() 方案可行性评估
你们想到的这个方案可以临时解决直接类依赖的问题,但存在明显缺陷,不适合长期维护:
- 类名完全靠硬编码字符串,拼写错误不会在编译期报错,只有运行时才会触发
ClassNotFoundException,排查成本极高 - 仅解决了Activity跳转的依赖问题,BaseActivity里如果还有其他全局依赖(比如全局用户信息单例、公共埋点逻辑、通用工具类调用等)还是没法处理
- 后续如果核心类的包名、类名变更,所有外包模块的相关代码都要同步修改,兼容性极差
这个方案仅适合非常小的临时需求场景,不是最优解。
2. 标准落地方法:抽象接口层 + 基础SDK剥离
这是业内做模块化外包协作的通用成熟方案,完全可以满足你方不泄露核心源码、统一基础逻辑的需求,具体实现步骤如下:
2.1 剥离无核心实现的基础接口SDK
单独新建一个空的Library模块,把BaseActivity里所有依赖核心业务的逻辑,全部抽成无实现的接口/抽象类放到这个模块里,整个模块只有接口、抽象类、常量定义,没有任何核心业务实现代码,编译成aar后可以直接提供给外包方,不会泄露任何核心源码。
举个具体的改造示例:
原BaseActivity里的会话过期跳转登录逻辑,原代码是直接调用
startActivity(Intent(this, LoginActivity::class.java))
抽成接口后,先在基础SDK模块定义全局路由接口:
// 基础SDK模块里的接口定义 interface IGlobalRouter { fun goToLogin(context: Context) fun goToHome(context: Context) // 其他所有公共跳转逻辑都在这里定义 } // 基础SDK模块里的服务提供者,用来桥接接口和实现 object GlobalServiceProvider { var router: IGlobalRouter? = null var userSession: IUserSession? = null // 其他全局服务的接口引用都可以放在这里 }
然后改造你的BaseActivity,所有依赖核心类的地方,全部调用GlobalServiceProvider里的接口方法,比如跳转登录就调用GlobalServiceProvider.router?.goToLogin(this)。改造后的BaseActivity因为只依赖抽象接口,没有任何核心业务类的直接引用,可以直接放到这个基础SDK模块里,一起打包给外包方。
2.2 主App侧实现接口
你们自己的主工程里实现所有基础SDK定义的接口,在App启动阶段(比如Application的onCreate生命周期)把实现类赋值给GlobalServiceProvider对应的变量:
// 主工程里的实现 class App: Application() { override fun onCreate() { super.onCreate() GlobalServiceProvider.router = object: IGlobalRouter { override fun goToLogin(context: Context) { context.startActivity(Intent(context, LoginActivity::class.java)) } override fun goToHome(context: Context) { context.startActivity(Intent(context, HomeActivity::class.java)) } } // 其他接口的实现也在这里完成初始化 } }
2.3 外包方开发流程
外包方拿到你们提供的基础SDK aar后,直接继承SDK里的BaseActivity开发对应功能模块即可,不需要关心BaseActivity里的跳转、会话处理的具体实现,也看不到你们的核心业务类源码,开发完成后把自己的模块编译成aar交付给你们集成即可。
3. 可选优化方向
如果后续你们的模块拆分复杂度提升,可以基于这套逻辑接入组件化路由框架,路由的注册、发现可以自动化处理,不需要手动编写大量接口,核心逻辑和上述的抽象思路完全一致。
内容的提问来源于stack exchange,提问作者Mycotina

