上篇我們說到MVP的一些相關概念:Android MVP架構(1)概念介紹
在弄清楚了MVP架構的基本原理後,我們就可以着手去自己實現一個MVP架構項目了。目前市面已有不少成熟的MVP框架,本篇僅作學習以及研究探討使用,不作任何對比。
使用自定義註解實現注入:
由上篇的架構圖可以看出,Presenter和Model之間會進行數據的交互,所以Presenter裏往往會持有Model對象的引用,而在Activity層,我們是需要調用Presenter來觸發View層的回調結果的。簡而言之就是:Activity裏有Presenter,Presenter裏面又有Model。而爲了處理這些複雜的依賴關係,我們可以統一使用注入的方式來解決。註解注入的本質其實就是反射,通過java的反射機制,可以方便我們快速地注入所需的對象。類似Spring的注入機制,對反射還不太瞭解的小夥伴可以移步我的另一篇博客:仿springboot @Autowired自動注入
在本篇中,通過使用@Inject註解來實現Model以及Presenter的自動注入。
小提示,實際開發中爲了提升代碼效率,請使用dagger替換反射來實現自定義注入。
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Inject {}
爲了實現注入,我們還需定義一個注入器,既然是注入器,就會包含創建和銷燬兩個過程,對注入器Injector的定義如下:
public interface Injector {
void inject();
void destroy();
}
由於Model和Presenter的注入並不是全部一樣的,所以我們需要使用兩個注入器來對它們進行注入,分別對應爲ModelInjector以及PresenterInjector,注入器需要實現Injector接口。
/**
* model注入器
*/
public class ModelInjector implements Injector {
// 注入到的presenter
private BasePresenter mPresenter;
// 需要注入的model
private List<BaseModel> mModels;
public ModelInjector(BasePresenter presenter) {
mPresenter = presenter;
}
@Override
public void inject() {
mModels = new ArrayList<>();
// 獲得mPresenter的字段
Field[] fields = mPresenter.getClass().getDeclaredFields();
for (Field field : fields) {
// 獲得字段裏面使用了MvpInject註解的實例 這裏指model
Inject inject = field.getAnnotation(Inject.class);
if(inject != null){
try {
// 獲得該實例的類型
Class<? extends BaseModel> clazz = (Class<? extends BaseModel>) field.getType();
// 創建實例
BaseModel model = clazz.newInstance();
field.setAccessible(true);
field.set(mPresenter, model);
mModels.add(model);
} catch (IllegalAccessException | InstantiationException | ClassCastException e) {
e.printStackTrace();
throw new IllegalStateException("are you ok?");
}
}
}
}
@Override
public void destroy() {
mModels.clear();
mModels = null;
mPresenter = null;
}
}
/**
* Presenter注入器
*/
public class PresenterInjector implements Injector {
// 需要注入的地方(Activity)
private IBaseView mView;
// 需要注入的接口
private List<BasePresenter> mPresenters;
public PresenterInjector(IBaseView view){
mView = view;
}
@SuppressWarnings("unchecked")
@Override
public void inject() {
mPresenters = new ArrayList<>();
Field[] fields = mView.getClass().getDeclaredFields();
for (Field field : fields) {
Inject inject = field.getAnnotation(Inject.class);
if(inject != null){
try {
Class<? extends BasePresenter> clazz = (Class<? extends BasePresenter>) field.getType();
BasePresenter presenter = clazz.newInstance();
field.setAccessible(true);
field.set(mView, presenter);
presenter.viewAttach(mView);
mPresenters.add(presenter);
} catch (IllegalAccessException | InstantiationException | ClassCastException e) {
e.printStackTrace();
throw new IllegalStateException("inject wrong!");
}
}
}
}
@Override
public void destroy() {
for (BasePresenter presenter : mPresenters) {
presenter.viewDetach();
}
mPresenters.clear();
mPresenters = null;
mView = null;
}
}
注入器創建完畢之後,下一步就是如何將Model以及Presenter進行更好的封裝了。這裏受到安卓輪子哥AndroidProject項目的啓發輪子哥:AndroidProject,使用代理的方式巧妙避免getView過程中的大量判空。設計的Presenter封裝基類如下:
public class BasePresenter<V extends IBaseView>
implements InvocationHandler {
/** View 層 */
private V mView;
/** 代理對象 */
protected V view;
// model注入器
private Injector mInjector;
@SuppressWarnings("unchecked")
public void viewAttach(V v) {
mView = v;
// V 層解綁了 P 層,那麼 getView 就爲空,調用 V 層就會發生空指針異常
// 如果在 P 層的每個子類中都進行 getView() != null 防空判斷會導致開發成本非常高,並且容易出現遺漏
view = (V) Proxy.newProxyInstance(v.getClass().getClassLoader(),
v.getClass().getInterfaces(), this);
mInjector = new ModelInjector(this);
mInjector.inject();
}
public void viewDetach() {
mView = null;
mInjector.destroy();
}
private boolean isAttached() {
return view != null && mView != null;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
return isAttached() ? method.invoke(mView, args) : null;
}
}
其中的IBaseView作爲一切View父類的存在,內部沒有任何內容。
至於Model的進一步封裝,主要用以處理網絡請求爲主,可通過自定義的BaseModel封裝get/post方式的實現。
public class BaseModel{
/**
* json post 請求
* @param url
* @param request
* @param listener
*/
protected void postJson(String url, JSONObject request, IResponseListener listener) {
HttpManager.getInstance().postJson(url,request,listener);
}
/**
* post 請求
* @param url
* @param request
* @param listener
*/
protected void doPost(String url, RequestBody request, IResponseListener listener) {
HttpManager.getInstance().post(url,request,listener);
}
/**
* 帶參 get請求
* @param url
* @param paramsMap
* @param listener
*/
protected void doGet(String url, Map<String, String> paramsMap, IResponseListener listener) {
HttpManager.getInstance().get(url, paramsMap, listener);
}
/**
* 無參 get請求
* @param url
* @param listener
*/
protected void doGet(String url, IResponseListener listener) {
doGet(url,null, listener);
}
}
至此,整個MVP框架的封裝已經基本完成,只需要在自定義的Activity基類的基礎上,對創建的註解使用注入器進行注入即可。
示例的MvpActivity如下:
public abstract class MvpActivity
extends BaseActivity implements IBaseView{
private Injector pInjector;
@Override
public void setContentView(int layoutResID) {
super.setContentView(layoutResID);
pInjector = new PresenterInjector(this);
pInjector.inject();
initView();
initData();
}
protected void initData(){};
protected void initView(){};
@Override
protected void onDestroy() {
pInjector.destroy();
super.onDestroy();
}
}
本篇主要講解了簡單的MVP框架的創建,篇幅原因,下一篇中將會完善該框架,使其能夠完成基本的網絡請求。