這期來(lái)看下 MVP ,如果不是很了解 MVP 是啥担扑?請(qǐng)自行補(bǔ)一補(bǔ)基礎(chǔ)知識(shí)洒嗤,我其實(shí)也不太會(huì),如果 RxJava 魁亦、OkHttp 渔隶、Retrofit 的運(yùn)用和源碼不是特別熟悉,也請(qǐng)去我之前的一些文章補(bǔ)一補(bǔ)洁奈,我其實(shí)也不太會(huì)间唉。我們先來(lái)寫一個(gè)簡(jiǎn)單版代碼,然后分析其問(wèn)題利术,運(yùn)用之前的一些設(shè)計(jì)模式基礎(chǔ)來(lái)一個(gè)一個(gè)解決呈野。拿獲取個(gè)人用戶資料信息為例:
1.簡(jiǎn)單基礎(chǔ)版本
UserInfoContract 類
/**
* description: User Contract
* author: Darren on 2017/12/16 11:15
* email: 240336124@qq.com
* version: 1.0
*/
public interface UserInfoContract {
// user View 層
interface UserInfoView {
void onLoading();
void onError();
void onSucceed(UserInfo userInfo);
}
// user presenter 層
interface UserInfoPresenter {
void getUsers(String token);
}
//Model層定義接口,外部只需關(guān)心Model返回的數(shù)據(jù),無(wú)需關(guān)心內(nèi)部細(xì)節(jié),如是否使用緩存
interface UserInfoModel {
UserInfo getUsers(String token) throws Exception;
}
}
UserInfoModel 類:
/**
* description: user model
* author: Darren on 2017/12/16 11:18
* email: 240336124@qq.com
* version: 1.0
*/
public class UserInfoModel implements UserInfoContract.UserInfoModel {
@Override
public UserInfo getUsers(String token) throws Exception {
// 模擬數(shù)據(jù)
Thread.sleep(2000);
return new UserInfo("Darren", "14726932514");
}
}
UserInfoPresenter 類:
/**
* description: User Presenter
* author: Darren on 2017/12/16 11:26
* email: 240336124@qq.com
* version: 1.0
*/
public class UserInfoPresenter implements UserInfoContract.UserInfoPresenter {
private UserInfoContract.UserInfoView userInfoView;
private UserInfoContract.UserInfoModel userInfoModel;
public UserInfoPresenter(UserInfoContract.UserInfoView userInfoView) {
this.userInfoView = userInfoView;
userInfoModel = new UserInfoModel();
}
@Override
public void getUsers(String token) {
// 應(yīng)該是 RXJava + OkHttp + Retrofit + Dagger 結(jié)合
userInfoView.onLoading();
// 這里只用 RXJava 模擬一下
Observable.just(token)
.map(new Function<String, UserInfo>() {
@Override
public UserInfo apply(@NonNull String token) throws Exception {
return userInfoModel.getUsers(token);
}
}).subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Consumer<UserInfo>() {
@Override
public void accept(UserInfo userInfo) throws Exception {
userInfoView.onSucceed(userInfo);
}
});
}
}
UserInfoActivity 類:
/**
* description: 用戶信息 InfoActivity
* author: Darren on 2017/12/16 11:15
* email: 240336124@qq.com
* version: 1.0
*/
public class UserInfoActivity extends AppCompatActivity implements UserInfoContract.UserInfoView {
// user presenter
private UserInfoContract.UserInfoPresenter presenter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
presenter = new UserInfoPresenter(this);
presenter.getUsers("user token");
}
@Override
public void onLoading() {
// 正在聯(lián)網(wǎng)獲取加載中...
Log.e("TAG", "加載中...");
}
@Override
public void onError() {
// 獲取用戶信息出錯(cuò)了
Log.e("TAG", "出錯(cuò)了 ...");
}
@Override
public void onSucceed(UserInfo userInfo) {
// 顯示用戶信息或者做其他操作
Log.e("TAG", "完成 :"+userInfo.toString());
}
}
很多人說(shuō) MVP 好,我第一感覺(jué)是信了你的邪這么麻煩印叁,做開(kāi)發(fā)豈不是很蛋疼被冒?其實(shí)有好處肯定有壞處军掂,通過(guò)上面的事例我們就能看出不好的地方,這個(gè)類太多了昨悼,但是分層解耦了蝗锥,不覺(jué)得對(duì)于后期維護(hù)和團(tuán)隊(duì)協(xié)作大有用處嗎?但一下子從 MVC -> MVP 總會(huì)怪怪的率触,那我告訴你更蛋疼的事情
2.泛型構(gòu)建基類
上面的這些代碼會(huì)有很多問(wèn)題终议,會(huì)有什么問(wèn)題?會(huì)讓你奔潰葱蝗,這里我們來(lái)一個(gè)一個(gè)分析穴张,一個(gè)一個(gè)解決,我們 new UserInfoPresenter() 的時(shí)候傳遞的是 this 也就是 activity两曼,當(dāng)我們?nèi)ヂ?lián)網(wǎng)獲取個(gè)人信息的時(shí)候皂甘,如果這個(gè)時(shí)候用戶按了返回鍵退出了 activity,當(dāng)數(shù)據(jù)返回的時(shí)候悼凑,如果恰好碰到 GC 回收了一些數(shù)據(jù)偿枕,那么會(huì)導(dǎo)致應(yīng)用崩潰,所以我們必須解綁判斷佛析,看下修改后的 UserInfoPresenter 類:
/**
* description: 解綁修改后 User Presenter
* author: Darren on 2017/12/16 11:26
* email: 240336124@qq.com
* version: 1.0
*/
public class UserInfoPresenter implements UserInfoContract.UserInfoPresenter {
private UserInfoContract.UserInfoView userInfoView;
private UserInfoContract.UserInfoModel userInfoModel;
public UserInfoPresenter() {
userInfoModel = new UserInfoModel();
}
/**
* 綁定和解綁
*
* @param userInfoView
*/
public void attachView(UserInfoContract.UserInfoView userInfoView) {
this.userInfoView = userInfoView;
}
public void dettachView() {
this.userInfoView = null;
}
@Override
public void getUsers(String token) {
// 應(yīng)該是 RXJava + OkHttp + Retrofit + Dagger 結(jié)合
userInfoView.onLoading();
// 這里只用 RXJava 模擬一下
Observable.just(token)
.map(new Function<String, UserInfo>() {
@Override
public UserInfo apply(@NonNull String token) throws Exception {
return userInfoModel.getUsers(token);
}
}).subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Consumer<UserInfo>() {
@Override
public void accept(UserInfo userInfo) throws Exception {
// 判斷 userInfoView 是不是等于空
if (userInfoView != null)
userInfoView.onSucceed(userInfo);
}
});
}
}
在 Activity 的 onDestory() 中解綁益老,這樣能解決調(diào)奔潰的問(wèn)題,但是新的問(wèn)題又來(lái)了寸莫,每次都這么寫豈不是很蛋疼捺萌?所以肯定要采用泛型和基類統(tǒng)一管理擅耽,修改后的 UserInfoPresenter 類:
/**
* description: 解綁 + 泛型 修改后 User Presenter
* author: Darren on 2017/12/16 11:26
* email: 240336124@qq.com
* version: 1.0
*/
public class UserInfoPresenter extends BasePresenter<UserInfoContract.UserInfoView, UserInfoContract.UserInfoModel>
implements UserInfoContract.UserInfoPresenter {
@Override
public void getUsers(String token) {
// 應(yīng)該是 RXJava + OkHttp + Retrofit + Dagger 結(jié)合
if (view != null)
view.onLoading();
// 這里只用 RXJava 模擬一下
Observable.just(token)
.map(new Function<String, UserInfo>() {
@Override
public UserInfo apply(@NonNull String token) throws Exception {
return model.getUsers(token);
}
}).subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Consumer<UserInfo>() {
@Override
public void accept(UserInfo userInfo) throws Exception {
// 判斷 userInfoView 是不是等于空
if (view != null)
view.onSucceed(userInfo);
}
});
}
}
這樣看上去似乎簡(jiǎn)單了一些樱报,但是我們發(fā)現(xiàn)每次調(diào)用 View 層的方法的時(shí)候焚志,我們不得不判斷一下 view 是不是等于空慷垮,這個(gè)也真是悲劇,動(dòng)態(tài)代理版本:
/**
* description: 解綁 + 泛型 + 動(dòng)態(tài)代理 修改后 Base Presenter
* author: Darren on 2017/12/16 13:01
* email: 240336124@qq.com
* version: 1.0
*/
public class BasePresenter<V extends IView, M extends IModel> implements IPresenter<V> {
// 一個(gè)是原始的 View 谓娃,一個(gè)是代理的 View
private WeakReference<V> mView = null;
private V mProxyView = null;
protected M model = null;
@Override
public void attachView(V view) {
this.mView = new WeakReference<>(view);
// 動(dòng)態(tài)代理
mProxyView = (V) Proxy.newProxyInstance(view.getClass().getClassLoader(),
view.getClass().getInterfaces(), new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (mView != null && mView.get() != null) {
return method.invoke(mView, args);
}
return null;
}
});
// 注入 Model廉邑,怎么注入埂淮,獲取泛型的類型棒拂,也就是 M 的 class伞梯,利用反射new 一個(gè)對(duì)象
try {
ParameterizedType parameterizedType = (ParameterizedType) this.getClass().getGenericSuperclass();
Class<M> modelClazz = (Class<M>) (parameterizedType.getActualTypeArguments()[1]);
model = modelClazz.newInstance();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
@Override
public void detachView() {
this.mView = null;
}
public V getView() {
return mProxyView;
}
}
UserInfoActivity 類:
/**
* description: 解綁 + 泛型 + 動(dòng)態(tài)代理 修改后 用戶信息 InfoActivity
* author: Darren on 2017/12/16 11:15
* email: 240336124@qq.com
* version: 1.0
*/
public class UserInfoActivity extends BaseActivity<UserInfoPresenter, UserInfoContract.UserInfoView>
implements UserInfoContract.UserInfoView {
@Override
protected void setContentView() {
setContentView(R.layout.activity_main);
}
@Override
protected void initData(Bundle savedInstanceState) {
getPresenter().getUsers("user token");
}
@Override
public void onLoading() {
// 正在聯(lián)網(wǎng)獲取加載中...
Log.e("TAG", "加載中...");
}
@Override
public void onError() {
// 獲取用戶信息出錯(cuò)了
Log.e("TAG", "出錯(cuò)了 ...");
}
@Override
public void onSucceed(UserInfo userInfo) {
// 顯示用戶信息或者做其他操作
Log.e("TAG", "完成 :" + userInfo.toString());
}
}
當(dāng)然可能還需要涉及到一些數(shù)據(jù)恢復(fù),如內(nèi)存不足的情況下可能導(dǎo)致 Activity 數(shù)據(jù)保存恢復(fù)帚屉;可能還需要涉及到一些靜態(tài)代理谜诫,因?yàn)椴还庖?BaseActivity 肯定還要有 BaseFragment 等等一些其他 Base ,肯定也需統(tǒng)一處理攻旦;還可以結(jié)合 Dagger 注入等等喻旷。
思考一下:一個(gè) View 多 Presenter 我們應(yīng)該怎么處理?一個(gè) Presenter 多個(gè) Model 又該怎么處理牢屋?