背景
在 Android 應(yīng)用開發(fā)中驶拱,我們經(jīng)常能看到下面的頁面:
這些頁面有以下幾個(gè)共同特征:
1.一個(gè) Activity 中包含多個(gè)子業(yè)務(wù)(以下稱為子模塊)霜浴。如上面簡(jiǎn)書 App 消息頁,包含了消息蓝纲、簡(jiǎn)信倆個(gè)模塊阴孟;
2.多個(gè)子模塊之間不存在依賴關(guān)系,相互獨(dú)立即可運(yùn)行税迷;
3.子模塊的生命周期與 Activity 相關(guān)聯(lián)永丝,且不能超過 Activity 的生命周期。
本文探討的就是如何將上述類型的 Activity 代碼組件化箭养、模塊化慕嚷,使其易于閱讀、開發(fā)毕泌、維護(hù)喝检。
探索
實(shí)際開發(fā)中,如果將多個(gè)獨(dú)立的功能代碼全部嵌入同一個(gè) Activity 類中撼泛,該 Activity 必定會(huì)越來越復(fù)雜冗余蛇耀,維護(hù)性、可讀性也會(huì)越來越差坎弯。
于是為了解決這個(gè)問題,我們可以將一個(gè)個(gè)獨(dú)立的功能抽成一個(gè)個(gè)子模塊,在子模塊中完成各自的功能抠忘;由于子模塊之間有其相同特性:比如都需要 context 上下文撩炊,都跟隨 Activity 生命周期。于是我們將這些相同特性抽出崎脉,創(chuàng)建模塊抽象類拧咳;一個(gè) Activity 的子模塊數(shù)量是不能保證的,將多個(gè)模塊交給 Activity 管理顯然不合適囚灼,為了項(xiàng)目擴(kuò)展性和可復(fù)用性骆膝,我們需要一個(gè)模塊管理類,來有序的創(chuàng)建灶体、初始化阅签、向子模塊分發(fā)生命周期等等。
這時(shí)我們心中大概有如下的組織結(jié)構(gòu):
下面就以簡(jiǎn)書 App 消息頁面為例蝎抽,編寫組件化代碼政钟。
編碼
在簡(jiǎn)書 App 消息頁面,我們將會(huì)按如下方式重構(gòu)樟结,其中除實(shí)體 module 與業(yè)務(wù)相關(guān)聯(lián)外养交,其它都是無關(guān) Activity 可復(fù)用的。建議看完后面代碼再回顧下面結(jié)構(gòu)瓢宦。
對(duì)于子模塊碎连,有至少三個(gè)依賴需要外部注入:
1.Activity:上下文對(duì)象;
2.ViewGroup:布局對(duì)象驮履,決定了子模塊在哪里布局鱼辙;
3.SaveInstanceState:保存狀態(tài)的對(duì)象。
所以首先創(chuàng)建 Bean 類:ModuleContext
來表示一個(gè)模塊需要的依賴參數(shù)合集疲吸。
public class ModuleContext {
private Activity context; //上下文對(duì)象
private Bundle saveInstance; //保存狀態(tài)的對(duì)象
private SparseArrayCompat<ViewGroup> 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<ViewGroup> getViewGroups() {
return viewGroups;
}
public void setViewGroups(SparseArrayCompat<ViewGroup> viewGroups) {
this.viewGroups = viewGroups;
}
}
ModuleContext
表示一個(gè)子模塊需要注入的參數(shù)座每,其中 viewGroups
表示該子模塊所擁有的所有布局 ViewGroup(一個(gè)模塊可以有多處布局)。
有了參數(shù)摘悴,就可以開始創(chuàng)建模塊抽象類:AbsModule
峭梳。
AbsModule
作為基類至少要有以下功能:
1.初始化模塊功能(上面定義的 ModuleContext
在這里注入)。
2.生命周期觸發(fā)蹂喻;
3.保存狀態(tài)觸發(fā)葱椭;
public abstract class AbsModule {
//初始化將AbsModule的參數(shù)傳入
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();
}
模塊管理類將會(huì)對(duì)其下所有的模塊 AbsModule
執(zhí)行初始化、生命周期分發(fā)等口四。這里為了擴(kuò)展性孵运,模塊管理類將派生出以下倆個(gè)類:
ModuleManager
:抽象管理類,僅做模塊的相關(guān)方法分發(fā)蔓彩;
public class ModuleManager {
private List<String> modules = new ArrayList<>(); //模塊類全限定名
protected HashMap<String, AbsModule> allModules = new HashMap<>(); //模塊實(shí)體
public List<String> getModuleNames() {
return modules;
}
public void moduleConfig(List<String> 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 模塊管理類治笨,負(fù)責(zé)模塊的初始化驳概,然后將初始化成功的模塊交給父類 ModuleManager
進(jìn)行相關(guān)方法分發(fā)。
ActivityModuleManager
一共完成了如下功能:
1.創(chuàng)建指定模塊旷赖;
2.指定模塊依賴注入顺又;
3.初始化指定模塊;
4.納入方法分發(fā)管理等孵。
簡(jiǎn)單來說一句話稚照,模塊依次初始化。
public class ActivityModuleManager extends ModuleManager {
public void initModules(Bundle saveInstance, Activity activity, HashMap<String, ArrayList<Integer>> modules) {
if (activity == null || modules == null) {
return;
}
//配置Activity下的所有module全限定名 后續(xù)可以根據(jù)名稱還原module實(shí)體(實(shí)體才包含ViewGroup俯萌、Activity等參數(shù))
moduleConfig(new ArrayList<>(modules.keySet()));
//依次給所有module初始化:1.創(chuàng)建實(shí)體 2.傳遞參數(shù) 3.調(diào)用初始化 4.納入生命周期管理
for (String moduleName : modules.keySet()) {
//創(chuàng)建對(duì)應(yīng)module
AbsModule module = ModuleFactory.newModuleInstance(moduleName);
if (module != null) {
//創(chuàng)建參數(shù)
ModuleContext moduleContext = new ModuleContext();
moduleContext.setContext(activity);
moduleContext.setSaveInstance(saveInstance);
SparseArrayCompat<ViewGroup> viewGroups = new SparseArrayCompat<>();
ArrayList<Integer> 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);
//調(diào)用初始化(參數(shù)傳遞)
module.init(moduleContext);
//納入管理
allModules.put(moduleName, module);
}
}
}
}
上面類使用了 ModuleFactory 創(chuàng)建實(shí)體模塊果录,其實(shí)現(xiàn)如下:
public class ModuleFactory {
//反射初始化對(duì)應(yīng)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;
}
}
使用反射獲取實(shí)體對(duì)象的意義主要在于 ModuleFactory 可以不依賴具體模塊,在組件化編程中咐熙,ModuleFactory 作為 Base 模塊的一員不依賴任何功能模塊的類非常必要弱恒。
現(xiàn)在,模塊管理類和模塊已經(jīng)有了關(guān)聯(lián)關(guān)系糖声,下面只需要通過 Activity 分發(fā)事件到 ActivityModuleManager
斤彼,進(jìn)而就可以分發(fā)到子模塊中。這樣就可以專注子模塊的功能開發(fā)蘸泻,事件分發(fā)全權(quán)交給模塊管理類琉苇。
創(chuàng)建 Activity 的抽象類 ModuleManagerActivity
,負(fù)責(zé)與模塊管理類通信:
public abstract class ModuleManagerActivity extends Activity {
private ActivityModuleManager moduleManager;
@Override
protected void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//布局 onLayout 時(shí)初始化
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<String, ArrayList<Integer>> 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);
}
}
}
至此組件化分發(fā)結(jié)構(gòu)代碼開發(fā)完畢悦施,下面開始測(cè)試實(shí)踐并扇。
測(cè)試
創(chuàng)建一個(gè)簡(jiǎn)單的模塊類 BodyAModule
(可以當(dāng)作模擬上面簡(jiǎn)書 App 簡(jiǎn)信模塊,在這里編寫業(yè)務(wù)代碼)
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() {
}
}
布局也很簡(jiǎn)單:
<merge xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:text="bodyA" />
</merge>
同理創(chuàng)建 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<String, ArrayList<Integer>> moduleConfig() {
HashMap<String, ArrayList<Integer>> map = new HashMap<>();
map.put(PageConfig.BODY_A, new ArrayList<Integer>() {{
add(R.id.al_fl_bodyA);
}});
map.put(PageConfig.BODY_B, new ArrayList<Integer>() {{
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 布局如下:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity">
<FrameLayout
android:id="@+id/al_fl_bodyA"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"/>
<FrameLayout
android:id="@+id/al_fl_bodyB"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"/>
</LinearLayout>
這樣就將布局與實(shí)體 module 類關(guān)聯(lián)了起來,實(shí)際運(yùn)行效果如下圖:
總結(jié)
上面示例中的模塊劃分清晰易懂顷窒,Activity蛙吏、module 簡(jiǎn)潔明了、功能專一鞋吉,在閱讀鸦做、開發(fā)、擴(kuò)展上都大有益處谓着。
組件化分發(fā)結(jié)構(gòu)的思想是將功能模塊化分割泼诱,分割后的多模塊負(fù)責(zé)業(yè)務(wù),由專門的管理類維護(hù)生命周期赊锚。而 Activity 或其他類則負(fù)責(zé)與管理類進(jìn)行生命周期的分發(fā)交互治筒,完全隔離業(yè)務(wù)處理屉栓。
上面的代碼參考自 <Android 組件化架構(gòu)> 一書,我做了修正和部分修改耸袜。實(shí)際開發(fā)中系瓢,我確實(shí)也有將功能模塊化、然后創(chuàng)建 Manager 進(jìn)行管理句灌、Activity 與 Manager 交互的編碼習(xí)慣,但是我有個(gè)嚴(yán)重的編碼誤區(qū):Activity 直接通過控制 Manager 執(zhí)行相關(guān)模塊的一些業(yè)務(wù)方法欠拾,而不是通過生命周期控制胰锌、讓模塊自身獨(dú)立完成業(yè)務(wù)功能,這樣是不對(duì)的藐窄,需要反思资昧。
舉個(gè)栗子,電商 App荆忍,在支付完成之后格带,刷新購物車頁面數(shù)據(jù)。錯(cuò)誤編碼可能是刹枉,EventBus 觸發(fā)購物車 Activity 調(diào)用該頁的 Manager 執(zhí)行刷新對(duì)應(yīng)模塊數(shù)據(jù)的方法叽唱。但是在組件化分發(fā)結(jié)構(gòu)上,Activity 只負(fù)責(zé)模塊的生命控制微宝,如初始化棺亭、生命周期分發(fā),不負(fù)責(zé)任何相關(guān)模塊的業(yè)務(wù)功能蟋软,所以正確的做法是镶摘,Activity 什么都不做,EventBus 直接在相關(guān)模塊中注冊(cè)岳守,由相關(guān)模塊直接接收 EventBus 事件并處理凄敢。
區(qū)別在于,誤區(qū) “Activity 通過控制 Manager 執(zhí)行相關(guān)模塊的業(yè)務(wù)方法” 本質(zhì)只是對(duì)功能的封裝湿痢,不是分割涝缝。封裝的結(jié)果會(huì)保留調(diào)用入口供其它類使用,分割在于盡可能的完全解耦蒙袍,將邏輯從 Activity 或其他地方剝離俊卤,業(yè)務(wù)功能上完完全全自我掌控、自我維護(hù)害幅,Activity 不調(diào)用也不參與删窒。
可以理解為將一個(gè) Activity 分解成了無數(shù)小 activity咽弦,這些小 activity 只接收生命周期管理,而業(yè)務(wù)上完全自我維護(hù)蝉仇,不受原本 Activity 的任何管制。
上述組件化分發(fā)架構(gòu)就是分割的良好體現(xiàn)饵逐,通過生命周期分發(fā)控制,實(shí)現(xiàn)業(yè)務(wù)代碼的完全剝離。
以上僅為個(gè)人理解恰矩。