前言
老板讓我搭建一個APP,我該怎么快速上手?
現(xiàn)在常用的Android應(yīng)用總體架構(gòu)是什么樣的?
Android開發(fā)現(xiàn)在有哪些流行的新技術(shù)和工具,可以提高我工作效率?
這里介紹這樣一個工程模板,讓你快速搭建健壯(strong),易擴展(scalable),易測試(testable),易維護(maintainable)的Android工程。
什么是架構(gòu)
本文主要討論Android軟件架構(gòu),那么何謂軟件架構(gòu)?軟件體系結(jié)構(gòu)是構(gòu)建計算機軟件實踐的基礎(chǔ)。與建筑師設(shè)定建筑項目的設(shè)計原則和目標(biāo),作為繪圖員畫圖的基礎(chǔ)一樣,軟件架構(gòu)師或者系統(tǒng)架構(gòu)師陳述軟件架構(gòu)以作為滿足不同客戶需求的實際系統(tǒng)設(shè)計方案的基礎(chǔ)。軟件的架構(gòu)是后續(xù)軟件開發(fā)實施的規(guī)則和骨架,好的架構(gòu)可以極大地降低軟件的開發(fā)成本和維護成本。
Android開發(fā)架構(gòu)的演進
Android應(yīng)用架構(gòu)在一直演進,沒有最好的架構(gòu),只有最適合的架構(gòu)。通過對歷史過程中的架構(gòu)的了解,可以了解每個架構(gòu)的優(yōu)缺點和適用范圍,知道為什么我們經(jīng)歷了這些變化。
MVC
MVC模式最早由Trygve Reenskaug在1978年提出。目的是實現(xiàn)一種動態(tài)的程序設(shè)計,使后續(xù)對程序的修改和擴展簡化,并且使程序某一部分的重復(fù)利用成為可能。它將軟件系統(tǒng)分為三個部分:模型,視圖和控制器。
MVC在Android上的應(yīng)用
- View:對應(yīng)于xml布局文件
- Model:模型數(shù)據(jù)
- Controllor:對應(yīng)于Activity業(yè)務(wù)邏輯,數(shù)據(jù)處理和界面相應(yīng)
MVC的問題
這里引用一段蘋果開發(fā)者網(wǎng)站對MVC的論述
However, there is a theoretical problem with this design. View objects and model objects should be the most reusable objects in an application. View objects represent the "look and feel" of an operating system and the applications that system supports; consistency in appearance and behavior is essential, and that requires highly reusable objects. Model objects by definition encapsulate the data associated with a problem domain and perform operations on that data. Design-wise, it's best to keep model and view objects separate from each other, because that enhances their reusability.
意思大致是:View和Model都是需要重用的模塊,在MVC中View監(jiān)聽Model的通知導(dǎo)致View依賴了Model,降低了View的可重用性。現(xiàn)實中常常在Android上實現(xiàn)的MVC里,View是xml文件,Activity是Controller其中又包含了大量和Model數(shù)據(jù)耦合的代碼又要響應(yīng)用戶事件,導(dǎo)致承擔(dān)功能過多,代碼量過大難以維護和測試。而且如果修改了Model的代碼,也可能會影響Activity中的邏輯。所以大型的工程,往往需要將Activity中一些和界面無關(guān)的職責(zé)拆分出來,提高工程的可維護性和可測試性。
MVP
為了徹底的將Model和View解耦,MVP對MVC進行了一些改進,將View和Model之間的通知和調(diào)用去掉,所有的數(shù)據(jù)流都經(jīng)過Presenter。
MVP的案例
這里可以參考google在github上的示例。這個工程里面實現(xiàn)了很多架構(gòu),每個架構(gòu)都是單獨一個分支,其中分支TODO-MVP就是演示的MVP。下面簡單解讀一下這個工程:
1 工程結(jié)構(gòu)
- 按照功能模塊劃分包
本工程按照不同的功能來分包,如todoList,taskdetail,statistic(統(tǒng)計頁面)等,每個功能都是相對獨立的。通常來講,我們可以用功能分包或者按照層次(如view,presenter,model等)來分包。關(guān)于那種分包模式好,Clean Architecture的作者做了一些有意義的論述,其中旗幟鮮明的支持按照功能分包。 - 功能模塊的結(jié)構(gòu)
在每個功能模塊中,主要包括了該功能的Activity、Fragment、Presenter、Contract四個類文件。Activity用于創(chuàng)建Presenter和View,F(xiàn)ragment實現(xiàn)了View的接口,Presenter實現(xiàn)了各項邏輯,Contract用于把Presenter和View聯(lián)系在一起。Model在各個功能模塊之外,為各模塊提供支持。
2 MVP在工程中的使用
- 使用contract把同一個功能的view和presenter聯(lián)系在一起,如:
public interface TasksContract {
interface View extends BaseView<Presenter>{}
interface Presenter extends BasePresenter{}
}
- Fragment繼承View,來實現(xiàn)界面的具體邏輯
public class TasksFragment extends Fragment implements TasksContract.View {
}
- 在Activity中構(gòu)造Presenter和View
TasksFragment tasksFragment =
(TasksFragment) getSupportFragmentManager().findFragmentById(R.id.contentFrame);
if (tasksFragment == null) {
// Create the fragment
tasksFragment = TasksFragment.newInstance();
ActivityUtils.addFragmentToActivity(
getSupportFragmentManager(), tasksFragment, R.id.contentFrame);
}
// Create the presenter
mTasksPresenter = new TasksPresenter(
Injection.provideTasksRepository(getApplicationContext()), tasksFragment);
- 在Presenter里處理各類邏輯
public class TasksPresenter implements TasksContract.Presenter {
@Override
public void loadTasks(boolean forceUpdate) {}
@Override
public void addNewTask() {}
}
MVP的問題
那MVP會有什么問題呢?試想一下下面的場景:假設(shè)要實現(xiàn)一個類似QQ的最近會話列表功能,那么可以從服務(wù)器拉到一個最近會話的列表如下[{id:1,message:"hello1"},{id:2,message:"hello2"}],列表中每一項有一個id和一個最近消息。好了,這樣就可以顯示這個會話列表了。可是過了一段時間,產(chǎn)品又要求如果最近會話里面有好友,需要顯示好友的備注,而不是id。這時我們不得不又向服務(wù)器請求好友列表,然后把是好友的id替換為備注名顯示在界面上。這里就出現(xiàn)了一個問題,當(dāng)程序中已有一個功能是顯示好友列表,此處實現(xiàn)了一套拉取好友列表的功能了,而現(xiàn)在又需要再重復(fù)實現(xiàn)一次。聰明的讀者這個時候也許已經(jīng)想到了一個解決方案,把兩個功能重復(fù)的地方拿出來實現(xiàn),從而達到重用的目的。
Clean Architecture
為了解決上面的問題,就有人提出了Clean Architecture的架構(gòu)。架構(gòu)圖如下:
- 依賴是由外向內(nèi)的,最外層藍(lán)色的包括UI,Model(本地數(shù)據(jù)庫或網(wǎng)絡(luò)數(shù)據(jù)),綠色層是Presenter,紅色層是Use Case,這里的Use Case指的就是外層可重用的模塊,如上文中提到的好友資料相關(guān)的邏輯,黃色層稱為Entities為更基礎(chǔ)的模塊,如整個APP可共用的邏輯,或者一個公司所有APP都可以共用的模塊(如果程序比較簡單,就沒有這一層)。
- 每一層都有單獨的數(shù)據(jù)結(jié)構(gòu),數(shù)據(jù)從一層到另一層的時候都需要經(jīng)過轉(zhuǎn)換,如果不希望這個程序都使用同樣的數(shù)據(jù)結(jié)構(gòu)的話,這么做是值得的。
- 具體的實現(xiàn)我們還是參考google的demo,如圖所示:
mvp-clean實現(xiàn)
整個工程分三個模塊,分別是Data,Domain,Presentation。Data是原MVP中的Model,在Clean Architecture中處于最外層;Domain處于Use Case層,用于實現(xiàn)各個可以重用的業(yè)務(wù)模塊;Presentation包含了UI和Presenter和自己的Model - 獨立的UI,獨立的數(shù)據(jù)庫,獨立的業(yè)務(wù)邏輯,更易測試和維護
Clean Architecture的案例
這里解讀一下google的工程,如下圖:
可以看到這個工程和前文的MVP工程結(jié)構(gòu)沒有太大變化,只是在功能模塊中加入了domain目錄,里面實現(xiàn)了Use Case。
RxJava和Dagger
在工程整體結(jié)構(gòu)確定了后,這里還可以使用時下比較熱門的兩個庫,幫助我們寫出更優(yōu)美,更易測試的代碼。
RxJava
RxJava是用于壓縮異步操作的庫,可以讓多層callback回調(diào)變成一串連續(xù)的事件,從而減少層次讓代碼更簡潔。更多詳細(xì)介紹
Dagger
Dagger是一個輕量級的依賴注入庫。關(guān)于Dagger2的使用方法可以參考這里。使用Dagger的主要好處包括:
- 依賴是在外部注入和配置的,所以方便了模塊的可重用性
- 可以注入抽象類和接口,從而更好的解耦
- 可以注入mock的依賴,方便測試
工程地址
這里實現(xiàn)了一個Clean Architecture + RxJava + Dagger的框架示例, 該示例使用了必應(yīng)中國的每日圖片API。