前段時間做項目時有用到一個比較常用的設計模式-MVP模式,我個人比較喜歡有優(yōu)美的結構的代碼,我始終相信代碼也是一種藝術,現(xiàn)在就特地將這一部分拿出來分享哈,自己也可以再熟悉熟悉。
OK,廢話不多說先上個圖,來看看我的代碼中MVP模式的應用:
mvpimg
- View 代表的是Android中對應的Activity,F(xiàn)ragment或View等與UI相關的。(view通常含有Presenter的引用)
- ViewInterface 用來提取一些可重用的功能,比如Pregress等以及方便的更改View,類之間更加的模塊化。使用時,只需要讓View去實現(xiàn)這個接口.
- Presenter 相當于一個路由,又來自View的轉發(fā)請求,以及Model回調的數(shù)據(jù)實體。(Presenter持有Model層的應用,以及ViewInterface的引用)
- Model 業(yè)務邏輯層。提取數(shù)據(jù)從Database,或云端API,緩存等,然后再對數(shù)據(jù)加以處理,再分發(fā)給Presenter去處理。
OK,來個栗子看看。一個根據(jù)圖書的isbn,從豆瓣的RestFul API中提取圖書的細節(jié)信息,再返回給前臺界面。
1.在View層中
/**
* ViewInterface
* 定義一些通用的view接口
*/
public interface LoadDataView {
/***
* 耗時操作,加載數(shù)據(jù),顯示Progress
*/
void showLoading();
/***
* 隱藏Progress
*/
void hideLoading();
}
/**
* 更細小的,用來顯示圖書細節(jié)的View接口
*/
public interface LoadBookView extends LoadDataView {
void showDetailsView(BookEntity entity);
}
/***
* Fragment,屬于View層,實現(xiàn)了ViewInterface(LoadBookView)
*/
public class BookDetailFragment extends Fragment implements LoadBookView{
/**圖書條形碼ISBN號*/
public static final String ISBN = "9787121060748";
/**持Presenter對象*/
private BookDetailsPresenter presenter;
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
initWidget();
presenter = new BookDetailsPresenter(getActivity(), ISBN); // 實例化一個presenter對象
presenter.setView(this); //讓Presenter持一個ViewInterface實例(LoadBookView)
presenter.loadData(); //告訴Presenter快給我加載Data
}
@Override
public void showDetailsView(BookEntity entity) {
//更新UI等操作
}
@Override
public void showLoading() {
rlProgress.setVisibility(View.VISIBLE);
}
@Override
public void hideLoading() {
rlProgress.setVisibility(View.GONE);
}
}
2.在Presenter層中
public class BookDetailsPresenter {
/**持一個Model層的對象,用來從網(wǎng)頁接口Rest Api中提取數(shù)據(jù)*/
private RestApi restApi = null;
/**一個ViewInterface對象,用來回調Data給View*/
private LoadBookView loadBookView;
private String isbn;
public BookDetailsPresenter(Context context, String isbn) {
restApi = new RestApiImpl(context);
this.isbn =isbn;
}
public void setView(LoadBookView loadBookView) {
this.loadBookView = loadBookView;
}
public void loadData() {
loadBookView.showLoading();
//耗時操作,開個線程異步的加載數(shù)據(jù)
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
restApi.getBookDetailByIsbn(isbn, bookDetailsCallback);
}
});
thread.start();
}
//匿名內部類,接收bookDetailCallback的回調數(shù)據(jù)
private RestApi.BookDetailsCallback bookDetailsCallback = new RestApi.BookDetailsCallback() {
@Override
public void onBookEntityLoaded(BookEntity bookEntity) {
notifyDataLoadedSuccessful(bookEntity);
BookDetailsPresenter.this.loadBookView.hideLoading();
}
@Override
public void onError(Exception e) {
//異常后的相關處理
}
};
/***
* 通知獲取數(shù)據(jù)成功了,趕快通知UI更新吧
*/
private void notifyDataLoadedSuccessful(final BookEntity bookEntity) {
Handler handler = new Handler(Looper.getMainLooper());
handler.post(new Runnable() {
@Override
public void run() {
BookDetailsPresenter.this.loadBookView.showDetailsView(bookEntity);
}
});
}
}
3.在Model層中
/***
* 整個應用程序需要的數(shù)據(jù)實體類
*/
public class BookEntity {
//一些set,get方法
}
/**
* 一個接口,用來從rest api api獲得數(shù)據(jù),它的實現(xiàn)在RestApiImpl中
*/
public interface RestApi {
String API_ISBN_BASE_URL = "https://api.douban.com/v2/book/isbn/";
/***
* 更細小的接口,用來將獲取到的數(shù)據(jù),回調給它的調用者
*/
interface BookDetailsCallback {
void onBookEntityLoaded(BookEntity bookEntity);
void onError(Exception e);
}
/**
* 從網(wǎng)絡獲取數(shù)據(jù),然后通過bookDetailCallback回調給Presenter
* @param isbn
* @param bookDetailsCallback
*/
void getBookDetailByIsbn(final String isbn, final BookDetailsCallback bookDetailsCallback);
}
好了核心的代碼就貼到這兒了,每個類關鍵的地方都偶注釋,整個Demo的代碼在github上,有興趣可以下來看看。這篇文章主要是從我的這個項目graduation中分離出來的, 主要借鑒了architecting-android-the-clean-way, 現(xiàn)在拿出來分享給大家,有不合理的地方也請多多指正,感謝萬分!