在andorid開發(fā)中越來越多的見到MVP的身影拳话,做過web的開發(fā)的都知道web三層架構(gòu)珍剑,而我們的MVP也有異曲同工之處钓辆,下面我們看下MVP的總體架構(gòu)缕溉,將Model通過Prensenter完全與View隔離開來考传,大大減輕了View的負(fù)擔(dān)
下面以簡單的登錄模塊,來展示最基本的MVP
根據(jù)基本的OOP原則证鸥,V僚楞、P、M層都使用了接口來實(shí)現(xiàn)枉层,當(dāng)然也可以不用這么麻煩泉褐。
首先看View接口內(nèi),展示我們登錄的結(jié)果,而LoginActivity則實(shí)現(xiàn)了LoginView接口鸟蜡,實(shí)現(xiàn)showLoginResult(String msg)方法膜赃,同時在onCreate方法中也對Presenter進(jìn)行了初始化,在登錄按鈕點(diǎn)擊時揉忘,調(diào)用Presenter的Login方法
public interface LoginView {
void showLoginResult(String msg);
}
@Override
public void showLoginResult(String msg) {
ToastUtil.shortToast(getApplicationContext(),msg);
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
ButterKnife.inject(this);
loginPresenter = new LoginPresenterImpl(this,LoginActivity.this);
initData();
}
@OnClick(R.id.tv_login)
public void tvLogin(Button tv){
String mobile=edtAccount.getText().toString().trim();
if (!RegexTool.isMobile(mobile)){
ToastUtil.shortToast(getApplicationContext(),"請輸入正確的11位手機(jī)號~~");
return;
}
String password=edtPassword.getText().toString().trim();
loginPresenter.login(mobile,password);
}
再來看我們的Presenter層接口跳座,最簡單的登錄方法接口,然后看到LoginPresenterImpl接口的實(shí)現(xiàn)類癌淮,所以我們可以在接口實(shí)現(xiàn)類LoginPresenterImpl的構(gòu)造方法中躺坟,進(jìn)行View和Model的初始化,在這里實(shí)現(xiàn)了Presenter的Login方法乳蓄,然后再調(diào)用Model的Login的方法咪橙,進(jìn)行網(wǎng)絡(luò)層的請求。
在Presenter層就同時持有View和Model虚倒,這樣我們就可以在P層處理Model返回的數(shù)據(jù)及View根據(jù)M所做的UI改變
public interface LoginPresenter {
void login(String phone,String passWord);
}
public LoginPresenterImpl(LoginView loginView,Context context) {
this.loginView = loginView;
loginModel = new LoginModelImpl();
}
在Login方法里美侦,我們用到了RxJava、RxAndroid編程
@Override
public void login(final String phone, final String passWord) {
subscription = loginModel.login(phone,passWord)
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io())
.subscribe(new Subscriber<LoginBean>() {
@Override
public void onCompleted() {
}
@Override
public void onError(Throwable e) {
if (e!=null)
loginView.showLoginResult(e.getMessage());
}
@Override
public void onNext(LoginBean loginBean) {
String message = loginBean.getMessage();
loginView.showLoginResult(message);
String status = loginBean.getStatus();
if (status!=null&&status.equals("1")){
// ToDo
}
}
});
}
對于這一段代碼我們先不深究魂奥,首先看到是調(diào)用了Model的Login方法菠剩,我們看一下LoginModel及其實(shí)現(xiàn)類
public interface LoginModel {
Observable<LoginBean> login(String phone, String passWord);
}
public class LoginModelImpl implements LoginModel {
@Override
public Observable<LoginBean> login(final String phone, final String passWord) {
return Observable.create(new Observable.OnSubscribe<LoginBean>() {
@Override
public void call(Subscriber<? super LoginBean> subscriber) {
try {
Map<String ,String> params=new HashMap<>();
params.put(MemberController.Login.PARAMS.MOBILE,phone);
params.put(MemberController.Login.PARAMS.PASSWORD,passWord);
params.put(MemberController.Login.PARAMS.LOGINTYPE,"0");
Response response = OkHttpUtils
.post()
.url(MemberController.Login.URL)
.params(params)
.build().execute();
String result=response.body().string();
LoginBean loginBean = new Gson().fromJson(result, LoginBean.class);
subscriber.onNext(loginBean);
subscriber.onCompleted();
} catch (IOException e) {
e.printStackTrace();
subscriber.onError(new Throwable("網(wǎng)絡(luò)連接超時"));
subscriber.onCompleted();
}
}
});
}
}
這樣一次基本的Login,通過MVP的調(diào)用就完成了請求到耻煤,展示的過程具壮。
下面我們再來看一下RxJava的簡單解析准颓。
整體來說,RxJava是一個基于觀察者模式的處理異步編程的庫棺妓,它讓代碼的異步操作變得非常簡單攘已,(當(dāng)然還有很多其他方面,先不深究)怜跑。
在LoginModelImpl中样勃,我們看到創(chuàng)建了一個被觀察者Observable,在里面我們調(diào)用了訂閱者subscriber的onNext性芬、onComplete峡眶、onError方法。在這里以登錄為例植锉,當(dāng)正常返回LoginBean的時候辫樱,我們就調(diào)用subscriber的onNext方法,將數(shù)據(jù)傳給訂閱者汽煮;當(dāng)數(shù)據(jù)連接異常的時候搏熄,調(diào)用subscriber的onError方法,將異常傳遞給訂閱者暇赤。
下面我們看一下Observable的觀察訂閱者心例,即LoginPresenterImpl的login方法
loginModel.login(phone,passWord)
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io())
.subscribe(new Subscriber<LoginBean>() {
@Override
public void onCompleted() {
}
@Override
public void onError(Throwable e) {
if (e!=null)
loginView.showLoginResult(e.getMessage());
}
@Override
public void onNext(LoginBean loginBean) {
}
可以看到在LoginModelImpl中,我們并沒有開啟網(wǎng)絡(luò)請求線程鞋囊, .subscribeOn(Schedulers.io())代表了Observable在子線程止后,而
observeOn(AndroidSchedulers.mainThread())表示在主線程訂閱,可以看到通過非常簡單的兩句話溜腐,就完成了線程的切換译株。subscribe(new Subscriber<LoginBean>() {},這里就是我們的訂閱者挺益,onNext歉糜、onComplete、onError的三個方法望众,就會回調(diào)之前LoginModelImpl中的被訂閱者Observeable匪补。這樣就基本完成了Rxjava的簡單流程。
當(dāng)然烂翰,訂閱記得調(diào)用subscription的解除訂閱夯缺,subscription.unsubscribe();
最后,細(xì)心的觀察會發(fā)現(xiàn)這上面的MVP有可能會產(chǎn)生內(nèi)存泄漏甘耿,我們在訂閱的時候使用了匿名內(nèi)部類踊兜,而匿名內(nèi)部類默認(rèn)持有外部類的引用,而外部類LoginPresenterImpl又持有了LoginActivity.this佳恬,這樣當(dāng)我們的請求很慢捏境,還沒返回?cái)?shù)據(jù)時于游,我們就退出Actiivty,則就會產(chǎn)生資源無法釋放典蝌,導(dǎo)致內(nèi)存泄漏曙砂。這個問題后面再解決了。