背景
在 Android 应用开发中,我们经常能看到下面的页面:
这些页面有以下几个共同特征:
1.一个 Activity 中包含多个子业务(以下称为子模块)。如上面 App 消息页,包含了消息、简信俩个模块;
2.多个子模块之间不存在依赖关系,相互独立即可运行;
3.子模块的生命周期与 Activity 相关联,且不能超过 Activity 的生命周期。
本文探讨的就是如何将上述类型的 Activity 代码组件化、模块化,使其易于阅读、开发、维护。
探索
实际开发中,如果将多个独立的功能代码全部嵌入同一个 Activity 类中,该 Activity 必定会越来越复杂冗余,维护性、可读性也会越来越差。
于是为了解决这个问题,我们可以将一个个独立的功能抽成一个个子模块,在子模块中完成各自的功能;由于子模块之间有其相同特性:比如都需要 context 上下文,都跟随 Activity 生命周期。于是我们将这些相同特性抽出,创建模块抽象类;一个 Activity 的子模块数量是不能保证的,将多个模块交给 Activity 管理显然不合适,为了项目扩展性和可复用性,我们需要一个模块管理类,来有序的创建、初始化、向子模块分发生命周期等等。
这时我们心中大概有如下的组织结构:
下面就以 App 消息页面为例,编写组件化代码。
编码
在 App 消息页面,我们将会按如下方式重构,其中除实体 module 与业务相关联外,其它都是无关 Activity 可复用的。建议看完后面代码再回顾下面结构。
对于子模块,有至少三个依赖需要外部注入:
1.Activity:上下文对象;
2.ViewGroup:布局对象,决定了子模块在哪里布局;
3.SaveInstanceState:保存状态的对象。
所以首先创建 Bean 类:ModuleContext
来表示一个模块需要的依赖参数合集。
public class ModuleContext {
private Activity context; //上下文对象
private Bundle saveInstance; //保存状态的对象
private SparseArrayCompat viewGroups = new SparseArrayCompat<>();
public Activity getContext() {
return context;
}
public void setContext(Activity context) {
this.context = context;
}
public Bundle getSaveInstance() {
return saveInstance;
}
public void setSaveInstance(Bundle saveInstance) {
this.saveInstance = saveInstance;
}
public SparseArrayCompat getViewGroups() {
return viewGroups;
}
public void setViewGroups(SparseArrayCompat viewGroups) {
this.viewGroups = viewGroups;
}
}
ModuleContext
表示一个子模块需要注入的参数,其中 viewGroups
表示该子模块所拥有的所有布局 ViewGroup(一个模块可以有多处布局)。
有了参数,就可以开始创建模块抽象类:AbsModule
。
AbsModule
作为基类至少要有以下功能:
1.初始化模块功能(上面定义的 ModuleContext
在这里注入)。
2.生命周期触发;
3.保存状态触发;
public abstract class AbsModule {
//初始化将AbsModule的参数传入
public abstract void init(ModuleContext moduleContext);
public abstract void onSaveInstanceState(Bundle outState);
public abstract void onResume();
public abstract void onPause();
public abstract void onStop();
public abstract void onOrientationChanges(boolean isLandscape);
public abstract void onDestroy();
}
模块管理类将会对其下所有的模块 AbsModule
执行初始化、生命周期分发等。这里为了扩展性,模块管理类将派生出以下俩个类:
ModuleManager
:抽象管理类,仅做模块的相关方法分发;
public class ModuleManager {
private List modules = new ArrayList<>(); //模块类全限定名
protected HashMap allModules = new HashMap<>(); //模块实体
public List getModuleNames() {
return modules;
}
public void moduleConfig(List modules) {
this.modules = modules;
}
public AbsModule getModuleByName(String name) {
if (!allModules.isEmpty()) {
return allModules.get(name);
}
return null;
}
public void onResume() {
for (AbsModule module : allModules.values()) {
if (module != null) {
module.onResume();
}
}
}
public void onPause() {
for (AbsModule module : allModules.values()) {
if (module != null) {
module.onPause();
}
}
}
public void onStop() {
for (AbsModule module : allModules.values()) {
if (module != null) {
module.onStop();
}
}
}
public void onDestroy() {
for (AbsModule module : allModules.values()) {
if (module != null) {
module.onDestroy();
}
}
}
public void onConfigurationChanged(Configuration newConfig) {
for (AbsModule module : allModules.values()) {
if (module != null) {
module.onOrientationChanges(newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE);
}
}
}
}
ActivityModuleManager
:Activity 模块管理类,负责模块的初始化,然后将初始化成功的模块交给父类 ModuleManager
进行相关方法分发。
ActivityModuleManager
一共完成了如下功能:
1.创建指定模块;
2.指定模块依赖注入;
3.初始化指定模块;
4.纳入方法分发管理。
简单来说一句话,模块依次初始化。
public class ActivityModuleManager extends ModuleManager {
public void initModules(Bundle saveInstance, Activity activity, HashMap> modules) {
if (activity == null || modules == null) {
return;
}
//配置Activity下的所有module全限定名 后续可以根据名称还原module实体(实体才包含ViewGroup、Activity等参数)
moduleConfig(new ArrayList<>(modules.keySet()));
//依次给所有module初始化:1.创建实体 2.传递参数 3.调用初始化 4.纳入生命周期管理
for (String moduleName : modules.keySet()) {
//创建对应module
AbsModule module = ModuleFactory.newModuleInstance(moduleName);
if (module != null) {
//创建参数
ModuleContext moduleContext = new ModuleContext();
moduleContext.setContext(activity);
moduleContext.setSaveInstance(saveInstance);
SparseArrayCompat viewGroups = new SparseArrayCompat<>();
ArrayList mViewIds = modules.get(moduleName);
if (mViewIds != null && mViewIds.size() > 0) {
for (int i = 0; i < mViewIds.size(); i++) {
viewGroups.put(i, (ViewGroup) activity.findViewById(mViewIds.get(i)));
}
}
moduleContext.setViewGroups(viewGroups);
//调用初始化(参数传递)
module.init(moduleContext);
//纳入管理
allModules.put(moduleName, module);
}
}
}
}
上面类使用了 ModuleFactory 创建实体模块,其实现如下:
public class ModuleFactory {
//反射初始化对应module
public static AbsModule newModuleInstance(String moduleName) {
if (TextUtils.isEmpty(moduleName)) {
return null;
}
try {
Class extends AbsModule> moduleClzz = (Class extends AbsModule>) Class.forName(moduleName);
return moduleClzz.newInstance();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
}
return null;
}
}
使用反射获取实体对象的意义主要在于 ModuleFactory 可以不依赖具体模块,在组件化编程中,ModuleFactory 作为 Base 模块的一员不依赖任何功能模块的类非常必要。
现在,模块管理类和模块已经有了关联关系,下面只需要通过 Activity 分发事件到 ActivityModuleManager
,进而就可以分发到子模块中。这样就可以专注子模块的功能开发,事件分发全权交给模块管理类。
创建 Activity 的抽象类 ModuleManagerActivity
,负责与模块管理类通信:
public abstract class ModuleManagerActivity extends Activity {
private ActivityModuleManager moduleManager;
@Override
protected void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//布局 onLayout 时初始化
ViewTreeObserver viewTreeObserver = getWindow().getDecorView().getRootView().getViewTreeObserver();
if (android.os.Build.VERSION.SDK_INT > Build.VERSION_CODES.JELLY_BEAN_MR2) {
viewTreeObserver.addOnWindowAttachListener(new ViewTreeObserver.OnWindowAttachListener() {
@Override
public void onWindowAttached() {
if (moduleManager == null) {
initModuleManager(savedInstanceState);
}
}
@Override
public void onWindowDetached() {
}
});
} else {
viewTreeObserver.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
if (moduleManager == null) {
initModuleManager(savedInstanceState);
}
}
});
}
}
private void initModuleManager(Bundle saveInstance) {
moduleManager = new ActivityModuleManager();
moduleManager.initModules(saveInstance, this, moduleConfig());
}
public abstract HashMap> moduleConfig();
@Override
protected void onResume() {
super.onResume();
if (moduleManager != null){
moduleManager.onResume();
}
}
@Override
protected void onStop() {
super.onStop();
if (moduleManager != null){
moduleManager.onStop();
}
}
@Override
protected void onDestroy() {
super.onDestroy();
if (moduleManager != null){
moduleManager.onDestroy();
}
}
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
if (moduleManager != null){
moduleManager.onConfigurationChanged(newConfig);
}
}
}
至此组件化分发结构代码开发完毕,下面开始测试实践。
测试
创建一个简单的模块类 BodyAModule
(可以当作模拟上面 App 简信模块,在这里编写业务代码)
public class BodyAModule extends AbsModule {
private Activity activity;
private ViewGroup parentViewGroup;
@Override
public void init(ModuleContext moduleContext) {
activity = moduleContext.getContext();
parentViewGroup = moduleContext.getViewGroups().get(0);
initView();
}
private void initView() {
LayoutInflater.from(activity).inflate(R.layout.content_body_a, parentViewGroup, true);
}
@Override
public void onSaveInstanceState(Bundle outState) {
}
@Override
public void onResume() {
}
@Override
public void onPause() {
}
@Override
public void onStop() {
}
@Override
public void onOrientationChanges(boolean isLandscape) {
}
@Override
public void onDestroy() {
}
}
布局也很简单:
同理创建 BodyBModule
。
MainActivity 继承自 ModuleManagerActivity,并引入 BodyAModule
、BodyBModule
。
public class MainActivity extends ModuleManagerActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
@Override
public HashMap> moduleConfig() {
HashMap> map = new HashMap<>();
map.put(PageConfig.BODY_A, new ArrayList() {{
add(R.id.al_fl_bodyA);
}});
map.put(PageConfig.BODY_B, new ArrayList() {{
add(R.id.al_fl_bodyB);
}});
return map;
}
}
其中 PageConfig 仅为方便管理模块类,代码如下:
public class PageConfig {
public class PageConfig {
public static final String BODY_A = "com.example.activitysend.BodyAModule";
public static final String BODY_B = "com.example.activitysend.BodyBModule";
}
MainActivity 布局如下:
这样就将布局与实体 module 类关联了起来,实际运行效果如下图:
总结
上面示例中的模块划分清晰易懂,Activity、module 简洁明了、功能专一,在阅读、开发、扩展上都大有益处。
组件化分发结构的思想是将功能模块化分割,分割后的多模块负责业务,由专门的管理类维护生命周期。而 Activity 或其他类则负责与管理类进行生命周期的分发交互,完全隔离业务处理。
上面的代码参考自
举个栗子,电商 App,在支付完成之后,刷新购物车页面数据。错误编码可能是,EventBus 触发购物车 Activity 调用该页的 Manager 执行刷新对应模块数据的方法。但是在组件化分发结构上,Activity 只负责模块的生命控制,如初始化、生命周期分发,不负责任何相关模块的业务功能,所以正确的做法是,Activity 什么都不做,EventBus 直接在相关模块中注册,由相关模块直接接收 EventBus 事件并处理。
区别在于,误区 “Activity 通过控制 Manager 执行相关模块的业务方法” 本质只是对功能的封装,不是分割。封装的结果会保留调用入口供其它类使用,分割在于尽可能的完全解耦,将逻辑从 Activity 或其他地方剥离,业务功能上完完全全自我掌控、自我维护,Activity 不调用也不参与。
可以理解为将一个 Activity 分解成了无数小 activity,这些小 activity 只接收生命周期管理,而业务上完全自我维护,不受原本 Activity 的任何管制。
上述组件化分发架构就是分割的良好体现,通过生命周期分发控制,实现业务代码的完全剥离。
以上仅为个人理解。