最近發現再也無法忍受越來越臃腫的Activity代碼,越來越來混亂的Activity層的代碼,投入到了MVP的懷抱。目前來看MVP的架構還是很適合Android的,在這裏記錄一下一點心得,希望都給想用MVP的人一點幫助。
老的MVC架構
剛開始接觸Android的時候會覺得Android的整個代碼架構就是一個MVC。
- M : 業務層和模型層,相當與javabean和我們的業務請求代碼
- V : 視圖層,對應Android的layout.xml佈局文件
- C : 控制層,對應於Activity中對於UI 的各種操作
看起來MVC架構很清晰,但是實際的開發中,請求的業務代碼往往被丟到了Activity裏面,大家都知道layout.xml的佈局文件只能提供默認的UI設置,所以開發中視圖層的變化也被丟到了Activity裏面,再加上Activity本身承擔着控制層的責任。所以Activity達成了MVC集合的成就,最終我們的Activity就變得越來越難看,從幾百行變成了幾千行。維護的成本也越來越高
新的MVP架構
- M : 還是業務層和模型層
- V : 視圖層的責任由Activity來擔當
- P : 新成員Prensenter 用來代理 C(control) 控制層
MVP與MVC最大的不同,其實是Activity職責的變化,由原來的C (控制層) 變成了 V(視圖層),不再管控制層的問題,只管如何去顯示。控制層的角色就由我們的新人 Presenter來擔當,這種架構就解決了Activity過度耦合控制層和視圖層的問題。
一個demo
理念終歸要用代碼來實現,來看一個很典型的例子,打開一個有列表的Activity界面,請求數據然後刷新界面的過程。先來看看工程的架構
我分了MVC 和 MVP2種寫法的目錄,MVP與MVC不同就在於多了2個結構presenter和view。
這裏業務層大家就公用了一個biz,先來看看相同的biz 也就是業務層
RequestBiz
聲明瞭一個接口,帶有請求數據業務的方法
public interface RequestBiz {
//請求數據業務
void requestForData(OnRequestListener listener);
}
- 1
- 2
- 3
- 4
- 1
- 2
- 3
- 4
RequestBiziml
請求的實現類爲了模擬網絡請求,開啓了一個會sleep2秒的線程,然後裝填請求的數據,通過OnRequestListener 接口回調出去,與我們平時開發的方式一致。
public class RequestBiziml implements RequestBiz{
@Override
public void requestForData(final OnRequestListener listener) {
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(2000);
ArrayList<String> data = new ArrayList<String>();
for(int i = 1 ; i< 8 ; i++){
data.add("item"+i);
}
if(null != listener){
listener.onSuccess(data);
}
}catch(Exception e){
e.printStackTrace();
}
}
}).start();
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
OnRequestListener
數據請求的回掉接口,聲明瞭成功和失敗的方法 。
public interface OnRequestListener {
void onSuccess(List<String> data);
void onFailed();
}
- 1
- 2
- 3
- 4
- 5
- 1
- 2
- 3
- 4
- 5
到此業務層完成,開始實現功能了,先來看看傳統寫法
MVC的寫法
public class MVCActivity extends AppCompatActivity{
private ListView mvcListView;
private RequestBiz requestBiz;
private ProgressBar pb;
private Handler handler;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_mvc);
mvcListView = (ListView)findViewById(R.id.mvc_listview);
pb = (ProgressBar) findViewById(R.id.mvc_loading);
pb.setVisibility(View.VISIBLE);
handler = new Handler(Looper.getMainLooper());
requestBiz = new RequestBiziml();
requestForData();
}
public void requestForData(){
requestBiz.requestForData(new OnRequestListener() {
@Override
public void onSuccess(final List<String> data) {
//由於請求開啓了新線程,所以用handler去更新界面
handler.post(new Runnable() {
@Override
public void run() {
pb.setVisibility(View.GONE);
ArrayAdapter adapter = new ArrayAdapter(MVCActivity.this,
android.R.layout.simple_list_item_1,data);
mvcListView.setAdapter(adapter);
mvcListView.setOnItemClickListener(itemClickListener);
}
});
}
@Override
public void onFailed() {
pb.setVisibility(View.GONE);
Toast.makeText(MVCActivity.this,"加載失敗",Toast.LENGTH_SHORT).show();
}
});
}
AdapterView.OnItemClickListener itemClickListener = new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
Toast.makeText(MVCActivity.this,"點擊了item"+(position+1),Toast.LENGTH_SHORT).show();
}
};
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
比較常見的Activity裏面的寫法:
- 界面的初始化
- 發起請求以及請求完成後的界面更新
- 點擊的監聽設置方法。
C: 控制層(點擊事件,網絡請求), V : 視圖層(界面刷新) 。就這樣耦合到了Activity裏面。
MVP的寫法
由於Activity變成了view層不再去控制界面,但是具體的界面的改變api其實還是由Activity來提供的,所以在寫MVP之前需要思考,View層需要哪些方法。
- 顯示loading
- 隱藏loading
- listview的初始化
- 彈出Toast消息
MvpView
public interface MvpView {
//顯示loading progress
void showLoading();
//隱藏loading progress
void hideLoading();
//ListView的初始化
void setListItem();
//Toast 消息
void showMessage();
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
通過上面的MVC的例子,我們可以總結出View層需要的接口。我們的Activty就是View層,所以直接用Activity來實現上面的方法。
public class MVPActivity extends AppCompatActivity implements MvpView{
ListView mvpListView;
MvpPresenter mvpPresenter;
ProgressBar pb;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_mvp);
mvpListView = (ListView)findViewById(R.id.mvp_listview);
pb = (ProgressBar) findViewById(R.id.mvp_loading);
}
@Override
public void showLoading() {
pb.setVisibility(View.VISIBLE);
}
@Override
public void hideLoading() {
pb.setVisibility(View.GONE);
}
@Override
public void setListItem(List<String> data) {
ArrayAdapter adapter = new ArrayAdapter(MVPActivity.this,
android.R.layout.simple_list_item_1,data);
mvpListView.setAdapter(adapter);
}
@Override
public void showMessage(String message) {
Toast.makeText(this,message,Toast.LENGTH_SHORT).show();
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
View視圖層就完成了。接下來開始寫presenter層, 同樣在寫presenter之前想想控制層需要哪些方法
- 網絡請求
- 點擊事件
MvpPresenter
public class MvpPresenter {
private MvpView mvpView;
RequestBiz requestBiz;
private Handler mHandler;
public MvpPresenter(MvpView mvpView) {
this.mvpView = mvpView;
requestBiz = new RequestBiziml();
mHandler = new Handler(Looper.getMainLooper());
}
public void onResume(){
mvpView.showLoading();
requestBiz.requestForData(new OnRequestListener() {
@Override
public void onSuccess(final List<String> data) {
//由於請求開啓了新線程,所以用handler去更新界面
mHandler.post(new Runnable() {
@Override
public void run() {
mvpView.hideLoading();
mvpView.setListItem(data);
}
});
}
@Override
public void onFailed() {
mvpView.showMessage("請求失敗");
}
});
}
public void onItemClick(int position){
mvpView.showMessage("點擊了item"+position);
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
Presenter完成,現在就剩下一件事,Activity中使用Presenter
完整版MVPActivity
public class MVPActivity extends AppCompatActivity implements MvpView ,AdapterView.OnItemClickListener{
ListView mvpListView;
MvpPresenter mvpPresenter;
ProgressBar pb;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_mvp);
mvpListView = (ListView)findViewById(R.id.mvp_listview);
mvpListView.setOnItemClickListener(this);
pb = (ProgressBar) findViewById(R.id.mvp_loading);
mvpPresenter = new MvpPresenter(this);
}
@Override
protected void onResume() {
super.onResume();
mvpPresenter.onResume();
}
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
mvpPresenter.onItemClick(position);
}
@Override
public void showLoading() {
pb.setVisibility(View.VISIBLE);
}
@Override
public void hideLoading() {
pb.setVisibility(View.GONE);
}
@Override
public void setListItem(List<String> data) {
ArrayAdapter adapter = new ArrayAdapter(MVPActivity.this,
android.R.layout.simple_list_item_1,data);
mvpListView.setAdapter(adapter);
}
@Override
public void showMessage(String message) {
Toast.makeText(this,message,Toast.LENGTH_SHORT).show();
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
靜靜的享受MVP的感覺吧,就是這麼清爽,沒有亂七八糟的業務,沒有各種點擊處理邏輯,Activity只需要提供View層的方法就可以了。
一點心得
我們的實際發開中需求往往瞬息萬變,每次還要等美工出界面,這樣往往會浪費大量時間,但是在MVP的世界這些都不是事,視圖層與控制層完全分離,可以讓我們在界面還是很粗糙的情況下,先進行控制層的開發,甚至可以先讓View層先提供方法出來,這樣可以節省很多時間。後面的複雜需求變化,也就是構建接口的時候會變的複雜,但是在有這麼多優勢的情況下,這點複雜度完全可以接受
還等什麼快來加入MVP吧!!!