MVP在android中的原理解析 MVP+Retrofit+Rxjava在項目中實戰解析 架構經驗分享html
MVP是模型(Model)、視圖(View)、主持人(Presenter)的縮寫,分別表明項目中3個不一樣的模塊。java
模型(Model):負責處理數據的加載或者存儲,好比從網絡或本地[數據庫]
(lib.csdn.net/base/mysql)…mysql
視圖(View):負責界面數據的展現,與用戶進行交互;android
以下圖所示,sql
這就是MVP模式的整個核心過程。
數據庫
上面已經介紹過MVP的核心思想以及基本架構,固然咱們在實際項目中不只僅要把建構劃分出來,還要加以延伸,這樣纔可以使項目的總體架構具有可擴展行、可複用性、可維護性、靈活性。下面我用我在實際項目中的角度來解析我所理解的MVP。編程
/**
* @Description MVP之V層 是全部VIEW的基類,其餘類能夠繼承該類
* @Author ydc
* @CreateDate 2016/10/10
* @Version 1.0
*/
public interface Iview<T> {
/**
* @description 全局的顯示加載框
* @author ydc
* @createDate
* @version 1.0
*/
void showLoading();
/**
* @description 全局的顯示加載框
* @author ydc
* @createDate
* @version 1.0
*/
...
/**
* @description 當前fragment是否有效
* @author ydc
* @createDate
* @version 1.0
*/
boolean isActive();
}複製代碼
能夠看出Iview 接口是因此activity 和fragment最基本且共有的方法定義。api
b、NewsView接口代碼以下:bash
/**ydc 新聞列表所特有的方法定義
* Created by Administrator on 2017/7/6.
*/
public interface NewsView extends Iview {
void addNews(List<NewsBean> newsList);
void showLoadFailMsg(String msg);
}複製代碼
NewsView接口繼承自Iview接口,定義新聞列表特有的方法。服務器
c、封裝BaseActivity基類
BaseActivity做爲因此activity的基類,你能夠把因此activity共有的方法和屬性提取到該中。複製代碼
d、activity_main.xml佈局文件代碼以下:
佈局裏面僅僅放了一個RecyclerView,用來展現數據列表。
e、NewListActivity代碼以下:
public class NewListActivity extends BaseActivity implements NewsView {
private RecyclerView mRecyclerView;
private LinearLayoutManager mLayoutManager;
private NewsAdapter mAdapter;
private List<NewsBean> mData;
private int pageIndex = 0;
private NewsPresenter mPresenter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mPresenter = new NewsPresenter();
mPresenter.attachView(this);
mPresenter.subscribe();
........
mRecyclerView.setItemAnimator(new DefaultItemAnimator());
mAdapter = new NewsAdapter(getApplicationContext());
mRecyclerView.setAdapter(mAdapter);
}
........
@Override
public void addNews(List<NewsBean> newsList) {
mAdapter.isShowFooter(true);
if(mData == null) {
mData = new ArrayList<NewsBean>();
}
mData.addAll(newsList);
if(pageIndex == 0) {
mAdapter.setmDate(mData);
} else {
//若是沒有更多數據了,則隱藏footer佈局
if(newsList == null || newsList.size() == 0) {
mAdapter.isShowFooter(false);
}
mAdapter.notifyDataSetChanged();
}
}
@Override
public void showLoadFailMsg(String msg) {
}
}複製代碼
在NewListActivity 中咱們能夠看到,NewListActivity 顯示實現了NewsView 接口,實現了NewsView和Iview 未實現的方法,在代碼中能夠看出NewListActivity並無作一些邏輯處理工做,僅僅作了添加數據和展現數據以及一些提示消息等工做,數據處理的工做都是調用 NewsPresenter 完成的。
二、Presenter層
a、Ipresenter 代碼以下:
/**
* @Description MVP的P層
* @Author ydc
* @CreateDate 2016/10/10
* @Version 1.0
*/
public interface Ipresenter<T extends Iview> {
/**
* @description 關聯P與V(綁定,VIEW銷燬適合解綁)
* @author ydc
* @createDate
* @version 1.0
*/
void attachView(T view);
/**
* @description 取消關聯P與V(防止內存泄漏)
* @author ydc
* @createDate
* @version 1.0
*/
void detachView();
/**
* @description RX訂閱
* @author ydc
* @createDate
* @version 1.0
*/
void subscribe();
/**
* @description RX取消訂閱
* @author ydc
* @createDate
* @version 1.0
*/
void unsubscribe();複製代碼
Ipresenter定義了全部presenter最基本且共有的方法。
b、BasePresenter代碼以下:
/**
* @Description 抽象的公用Presenter
* @Author ydc
* @CreateDate 20170707
* @Version 1.0
*/
public abstract class BasePresenter<T extends Iview> implements Ipresenter<T> {
protected T mMvpView;//全部View
protected SubscriptionList mSubscriptions;//rx註冊中心
protected DataRepository mDataCenter;//數據中心
//protected abstract SubscriptionList createSubscriptionList();//引入darger後取締
......
/**
* @description p&v沒有綁定的異常
* @author ydc
* @createDate
* @version 1.0
*/
public static class MvpViewNotAttachedException extends RuntimeException {
public MvpViewNotAttachedException() {
super("Please call Presenter.attachView(MvpView) before requesting data to the Presenter");
}
}
/**
* @description 統一添加訂閱關聯被觀察者和觀察者
* @author ydc
* @createDate
* @version 1.0
*/
public void addSubscription(Observable observable, Subscriber subscriber) {
if( observable!=null && subscriber!=null ){
if (mSubscriptions == null) {
mSubscriptions = new SubscriptionList();
}
mSubscriptions.clear();
mSubscriptions.add(observable
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(subscriber));
}
}
}複製代碼
BasePresenter是一個abstract類,在實現Ipresenter的未實現的方法以外,又擴展了幾個因此presenter共用的方法。
c、Presenter代碼以下:
/**ydc 新聞類的協議也能夠是接口
* Created by Administrator on 2017/7/6.
*/
abstract class Presenter extends BasePresenter<NewsView> {
public abstract void loadNews(int type, int page);
}複製代碼
能夠看出Presenter也是一個abstract類、繼承自BasePresenter抽象類,同時定義了新聞列表所特有的方法。
d、NewsPresenter代碼以下:
/**
* Created by Administrator on 2017/7/6.
*/
public class NewsPresenter extends Presenter {
private Model mModel;
public NewsPresenter(){
mModel=new NewsModel();
}
@Override
public void loadNews(int type, int page) {
addSubscription(mModel.loadNews("nc/article/headline/T1348647909107/0-20.html",0), new ApiCallBack<NewsRequestModel>() {
@Override
public void onStart() {
getMvpView().showLoading();
}
@Override
public void onSuccess(NewsRequestModel modelBean) {
if(modelBean!=null){
getMvpView().addNews(modelBean.getT1348647909107());
}
}
@Override
public void onFailure(String errorMsg) {
getMvpView().showLoadFailMsg(errorMsg);
}
@Override
public void onFinished() {
getMvpView().hideLoading();
}
});
}
@Override
public void subscribe() {
}
}複製代碼
能夠看出NewsPresenter持有view和model的接口或是抽象類,起到中轉的做用。
三、Model層
a、Imodel接口代碼以下:
**
* @Description MVP的M層
* @Author ydc
* @CreateDate 2016/10/10
* @Version 1.0
*/
public interface Imodel {
}複製代碼
我這裏其實並無作什麼,只是留了一個接口而已,你能夠定義因此model的基本方法。
b、BaseModel代碼以下:
/**
* @Description 數據模型基礎類
* @Author ydc
* @CreateDate 2016/11/2
* @Version 1.0
*/
public abstract class BaseModel implements Imodel {
/**
* @description 返回服務接口對象實例
* @author ydc
* @createDate
* @version 1.0
*/
public <T> T createService(final Class<T> clazz) {
validateServiceInterface(clazz);
return (T) RxService.RETROFIT.createRetrofit().create(clazz);
}
/**
* @description 校驗接口合法性
* @author ydc
* @createDate
* @version 1.0
*/
public <T> void validateServiceInterface(Class<T> service) {
if (service == null) {
//AppToast.ShowToast("服務接口不能爲空!");
}
if (!service.isInterface()) {
throw new IllegalArgumentException("API declarations must be interfaces.");
}
if (service.getInterfaces().length > 0) {
throw new IllegalArgumentException("API interfaces must not extend other interfaces.");
}
}
}複製代碼
這個類我是用它來作Retrofit的初始化以及網絡請求的工做,固然我要進一步包裝Retrofit,因此這裏只看到一個方法調用。
c、Model代碼以下:
/**ydc 獲取數據的邏輯模塊協議,也能夠是接口,提供給P調用,在callback中更新V
* Created by Administrator on 2017/7/6.
*/
public abstract class Model extends BaseModel {
public abstract Observable<NewsRequestModel> loadNews(String url, int type);
}複製代碼
既然公共BaseModel的職責任命爲整個網絡調用的工做,那麼我就要在抽象一個Model抽象類來定義新聞數據處理邏輯模塊協議,提供給P調用。
d、NewsModel代碼以下:
/**ydc 新聞數據處理協議
* Created by Administrator on 2017/7/6.
*/
public class NewsModel extends Model {
private INewService service=createService(INewService.class);
@Override
public Observable<NewsRequestModel> loadNews(String url, int type) {
Map<String, String> map = new HashMap<>();
//map.put("type", type+"");
return service.getNewList(url,map);
}
}複製代碼
這個類實現了Model做爲具體的新聞列表數據處理層。
Retrofit 是 Square 的一個著名的網絡請求庫,是okHTTP的升級版,目前公認的最好的網絡請求框架。
響應式編程RxJava就更不用說,它的強大之處只有用過的人才會體會獲得。
Retrofit 除了提供了傳統的 Callback 形式的 API,還有 RxJava 版本的 Observable 形式 API。下面我用對比的方式來介紹 Retrofit 的 RxJava 版 API 和傳統版本的區別。
以獲取一個 User 對象的接口做爲例子。使用Retrofit 的傳統 API,你能夠用這樣的方式來定義請求:
@GET("/user")
public void getUser(@Query("userId") String userId, Callback<User> callback);複製代碼
在程序的構建過程當中, Retrofit 會把自動把方法實現並生成代碼,而後開發者就能夠利用下面的方法來獲取特定用戶並處理響應:
getUser(userId, new Callback<User>() {
@Override
public void success(User user) {
userView.setUser(user);
}
@Override
public void failure(RetrofitError error) {
// Error handling
...
}
};複製代碼
其實 Retrofit傳統的API調用與okHTTP功能和使用上沒有什麼本質的區別,它的強大之處在於與RxJava結合使用。
而使用 RxJava 形式的 API,定義一樣的請求是這樣的:
@GET("/user")
public Observable<User> getUser(@Query("userId") String userId);複製代碼
使用的時候是這樣的:
getUser(userId)
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<User>() {
@Override
public void onNext(User user) {
userView.setUser(user);
}
@Override
public void onCompleted() {
}
@Override
public void onError(Throwable error) {
// Error handling
...
}
});複製代碼
看到區別了嗎?
當 RxJava 形式的時候,Retrofit 把請求封裝進 Observable ,在請求結束後調用 onNext() 或在請求失敗後調用 onError()。
對比來看, Callback 形式和 Observable 形式長得不太同樣,但本質都差很少,並且在細節上 Observable 形式彷佛還比 Callback 形式要差點。那 Retrofit 爲何還要提供 RxJava 的支持呢?
單個請求體現不出它的優點所在,可是情景複雜起來, Callback 形式立刻就會開始讓人頭疼。
假設 /user 接口並不能直接訪問,而須要填入一個在線獲取的 token ,代碼應該怎麼寫?
Callback 方式,可使用嵌套的 Callback:
GET("/token")
public void getToken(Callback<String> callback);
@GET("/user")
public void getUser(@Query("token") String token, @Query("userId") String userId, Callback<User> callback);
...
getToken(new Callback<String>() {
@Override
public void success(String token) {
getUser(token, userId, new Callback<User>() {
@Override
public void success(User user) {
userView.setUser(user);
}
@Override
public void failure(RetrofitError error) {
// Error handling
...
}
};
}
@Override
public void failure(RetrofitError error) {
// Error handling
...
}
});複製代碼
卻是沒有什麼性能問題,但是迷之縮進並且充滿了無窮無盡的回調,這種後果你懂我也懂,作過大項目的人應該更懂。
而使用 RxJava 的話,代碼是這樣的:
@GET("/token")
public Observable<String> getToken();
@GET("/user")
public Observable<User> getUser(@Query("token") String token, @Query("userId") String userId);
...
getToken()
.flatMap(new Func1<String, Observable<User>>() {
@Override
public Observable<User> onNext(String token) {
return getUser(token, userId);
})
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<User>() {
@Override
public void onNext(User user) {
userView.setUser(user);
}
@Override
public void onCompleted() {
}
@Override
public void onError(Throwable error) {
// Error handling
...
}
});複製代碼
用一個 flatMap() 就搞定了邏輯,整個請求都在一條鏈當中。讀者看到這裏應該明白我爲何選擇RxJava 與 Retrofit 的結合來處理網絡請求。其實RxJava有兩個比較核心的功能就是數據轉換和線程調度,固然它還有其它的強大之處,只是咱們用的最多的是這兩個而已。
RxJava 與 Retrofit 的結合在本項目中的應用
一、建立RxService類代:
RxService類主要用來初始化Retrofit以及添加頭部和系統參數,NewsModel初始化時,順帶完成了以上工做。
二、INewService接口代碼以下:
/**網絡接口
* Created by Administrator on 2017/7/6.
*/
public interface INewService {
@GET
Observable<NewsRequestModel> getNewList(@Url String url,
@QueryMap Map<String, String> params);
}複製代碼
這個類是來定義新聞列表網絡接口
三、管理被觀察者和觀察者
統一添加訂閱關聯被觀察者和觀察者
protected SubscriptionList mSubscriptions;//rx註冊中心
/**
* @description 統一添加訂閱關聯被觀察者和觀察者
* @author ydc
* @createDate
* @version 1.0
*/
public void addSubscription(Observable observable, Subscriber subscriber) {
if( observable!=null && subscriber!=null ){
if (mSubscriptions == null) {
mSubscriptions = new SubscriptionList();
}
mSubscriptions.clear();
mSubscriptions.add(observable
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(subscriber));
}
}複製代碼
RX取消訂閱代碼以下:
@Override
public void unsubscribe(){
if(mSubscriptions!=null){
mSubscriptions.clear();
}
}複製代碼
以上兩段代碼是在BasePresenter抽象類中。
四、把請求添加到rx註冊中心SubscriptionLis中
public class NewsRequestModel extends BaseFeed {
public List<NewsBean> getT1348647909107() {
return T1348647909107;
}
public void setT1348647909107(List<NewsBean> t1348647909107) {
T1348647909107 = t1348647909107;
}
private List<NewsBean> T1348647909107;
}複製代碼
該類繼承自BaseFeed,做爲新聞列表接口返回實體映射,這個須要和後臺api接口開發人員協商好再定義。
做爲因此接口返回實體映射基類,這個也須要和後臺api開發人員協商好,至少我是這麼作的。複製代碼
####RxJava 與 Retrofit 的結合小結
Demo如今地址:
[download.csdn.net/download/xi…]
項目地址:
若是你以爲此文對您有所幫助,歡迎入羣 QQ交流羣 :232203809
微信公衆號:終端研發部