基於AOP的MVP框架(三)GoMVP進階註解

不能用真名發表於2018-09-30

上一節我們將了GoMVP的進階使用:基於AOP的MVP框架(二)GoMVP進階註解

如果想在框架處理返回資料之前對資料“動手腳”,我們可以在我們自己的PresenterAdapter上實現InterceptGoBack這個介面,我們拿上面的MarketPresenterAdapter舉個例子:

4、"攔截"返回資料,優先處理

public class MarketPresenterAdapter extends PresenterAdapter implements InterceptGoBack<MarketBean>{
    @Override
    public Observable onCreateObservable(Context context, RetrofitConverter retrofitConverter) {
    
        Retrofit retrofit = retrofitConverter.createRetrofit();
        ApiServer apiServer = retrofit.create(ApiServer.class);
        HashMap<String, Object> map = new HashMap<>();
        map.put("請求引數1",0);
        map.put("請求引數2","123");
        Observable<HttpResult<SecretKeyBean>> observable = apiServer.getSecretKey(map);
        return observable;
    }

    @Override
    public Pair onSuccessCodePair() {
        return new Pair("success","true");
    }

    @Override
    public String onErrorMessageKey() {
        return "message";
    }

    @Override
    public Class targetBeanType() {
        return MarketBean.class;
    }
    
    @Override
    public boolean intercept(MarketBean marketBean) {
        //預先處理marketBean
        marketBean.setMessage("我被處理過了");
        return false;
    }
}
複製程式碼

實現InterceptGoBack介面後,還要實現一下它的intercept方法,方法的回撥引數是你想要預先處理的JavaBean,這裡是MarketBean,它的返回值很關鍵,

如果返回false,說明不會攔截流程繼續交給框架去處理,View層會接收到回撥,如果返回true,證明此處要攔截剩下的流程,不在交由框架去處理和返回到View層。

第二點需要注意的是,intercept處理完的Bean資料後,如果接著交給框架繼續處理(返回false),框架會用處理過的資料繼續執行剩下的邏輯。

5、使用@GoActionBack

有一種場景,如果一個頁面的多個介面呼叫返回的資料型別是一致的,我們想單獨處理每一個請求,我們可以使用@GoActionBack註解來接收回撥:

public class AnnotationDemoActivity extends AppCompatActivity implements ExecuteStatusView {

    private static final String DELETE = "action_delete";
    private static final String ADD = "action_add";
    @BindView(R.id.button2)
    Button button;
    @BindView(R.id.button3)
    Button button3;

    /**
     * 注入Presenter,RepositoryInjection,
     * RepositoryInjection必須為DataSourceInjection的子類
     */
    @DataSource(RepositoryInjection.class)
    private LifecyclePresenter presenter;

    private MessageCountPresenter messagePresenterAdapter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_simple_demo);

        messagePresenterAdapter = new MessageCountPresenter();
        messagePresenterAdapter.setStatus(0);
        presenter.registerExecuteStatus(this);
    }
    /**
     * test @GoBack
     * @param bean
     */
    @GoBack
    public void hahaha(MarketBean bean) {
        GoLog.E("MarketBean is backing:" + bean.getMessage());
    }
    /**
     * 這裡的action要對應Adapter裡的action
     * test @GoActionBack
     * @param bean
     */
    @GoActionBack(action = DELETE)
    public void receiverDeleteData(MarketBean bean) {
        GoLog.E("MarketBean delete is backing:" + bean);
    }
    @GoActionBack(action = ADD)
    public void receiverAddData(MarketBean bean) {
        GoLog.E("MarketBean add is backing:" + bean);
    }
    
    @OnClick({R.id.button2, R.id.button3})
    public void onViewClicked(View view) {
        switch (view.getId()) {
            case R.id.button2:
                new Thread(() -> {
                    presenter.bindPresenterAdapter(new MarketPresenterAdapter(DELETE));
                    presenter.execute();
                }).start();
                break;
            case R.id.button3:
                presenter.execute(new MarketPresenterAdapter(ADD));
                break;
        }
    }
}
複製程式碼

上面的例子中,我們增加來三個回撥方法,這三個回撥的引數都是同一個型別MarketBean,其中兩個使用了@GoActionBack註解,改註解引數是個字串型別。同時在執行adapter時,給adapter傳遞了一個值,這個值就是註解上定義的字串的值,這個值需要在adapter中使用,像這樣:

public class MarketPresenterAdapter extends BasePresenterAdapter implements InterceptGoBack<MarketBean>{
    private String action;
    .
    .
    
    public MarketPresenterAdapter(String action) {
       this.action = action;
    }

    //if action 為null或者"",則被@GoActionBack修飾的方法接收不到回撥。
    @Override
    public String action() {
        return action;
    }
    .
    .
}
複製程式碼

實現action方法,告訴框架這個Adapter和具體的接收事件的方法之間的關係,這樣在框架執行完任務後才能找到正確的回撥方法。比如:

presenter.execute(new MarketPresenterAdapter(ADD));
複製程式碼

當,執行完成後,該方法會收到回撥:

@GoActionBack(action = ADD)
public void receiverAddData(MarketBean bean) {
    GoLog.E("MarketBean add is backing:" + bean);
}
複製程式碼

PS:注意上面三個回撥方法,其中兩個分別被@GoActionBack(action = ADD)與,@GoActionBack(action = DELETE)修飾,它們相對於使用了不同的action的Adapter,其中還有一個被@GoBack修飾的回撥,這個回撥的型別同樣是MarketBean,所以不管使用那種action的Adapter,這個方法都會收到回撥,因為@GoBack只認型別。而@GoActionBack多了層維度,不只認型別,還認action。

6、使用OnExecuteListener監聽excute狀態

實現ExecuteStatusView介面便可以監聽excute執行狀態

public class AnnoDemoActivity extends AppCompatActivity implements ExecuteStatusView {

    @DataSource(RepositoryInjection.class)
    private LifecyclePresenter presenter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_simple_demo);
        //註冊進度監聽
        presenter.registerExcuteStatus(this);
    }
    
    @Override
    public void onExecuteBegin() {
        //loading View show。。。
    }

    @Override
    public void onExecuteFinish() {
        //loading View close。。。
    }
    //其他程式碼。。。
}
複製程式碼

目前只提供了開始和結束,分別為onExecuteBegin和onExecuteFinish,同時需要注意的是,如果多次執行excute方法,每執行一次excute,ExecuteStatusView的回撥都會被執行,注意。

7、擴充套件Cache

在初始化MarketRepository時,需要實現GoDataSource介面,其中getGoCache的返回值用來指定資料倉儲的具體快取實現:

public class MarketRepository implements GoDataSource {

    @Override
    public <B> GoDataSource loadDataFromRepository(Observable<B> observable, Observer observer) {
        observable.subscribeOn(Schedulers.io())
                .unsubscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(observer);
        return this;
    }

    /**
    * 指定具體快取方案
    **/
    @Override
    public GoCache getGoCache(Context context) {
        return new DefaultGoCache(context);
    }

    @Override
    public RetrofitConverter onCreateRetrofitConverter() {
        return new MainRetrofit();
    }

    @Override
    public <T> void targetClazz(Class<T> clazz) {

    }
}
複製程式碼

在getGoCache方法返回了一個DefaultGoCache物件,這是框架提供的快取實現,它實現了GoDataSource.GoCache介面,比如要實現自己的快取方案,可以這樣:

1、實現GoDataSource.GoCach介面

public class MyGoCache implements GoDataSource.GoCache<String, String> {

    private final Context context;

    public MyGoCache(Context context) {
        this.context = context;
    }

    @Override
    public void onAdd(String key, String value) {
        SharedPUtil.setParam(context, key, value);
        GoLog.D(TAG + "cache :" + "key:" + key + "....value:" + value);
    }

    @Override
    public String onGet(String key) {

        String s = SharedPUtil.getParam(context, key);
        GoLog.D(TAG + "cache out :" + "key:" + key + "....value:" + s);
        return s;
    }
}
複製程式碼

2、這裡需要實現兩個方法,一個onAdd,一個onGet,分別對應新增和獲取,這裡我們用SharedPreferences來作為快取方案。

3、在實現我們自己的Repository時,把自定義的MyGoCache設定到我們的Repository裡:

        /**
        * 指定具體快取方案
        **/
        @Override
        public GoCache getGoCache(Context context) {
            //自定義的GoCache/
            return new MyGoCache(context);
        }
複製程式碼

8、Fragment

在Fragment中一樣可以使用,這裡就不單獨寫例子了,但需要注意的是,如果使用註解初始化presenter,presenter只可以在onCreateView的方法內以及其後的生命週期使用,之前比如onCreate中使用就會被報空指標異常,那是因為presenter沒有初始化的原因,框架會在Fragment的onCeateView方法中初始化presenter。

基於AOP的實現原理

上面介紹了GoMVP的使用方式,我們姐下來介紹它是如何做到只用一行註解就可以完成Presenter的初始化和Repository的初始化與繫結,如何通過一行註解接收資料而不需要通過業務程式碼去實現,這裡就要提到一個AOP的工具AspectJ,關於AspectJ的使用網上有很多的例子比如這一篇 www.jianshu.com/p/f90e04bcb… ,大家可以先了解一下AspectJ的基本使用。

GoMVP原始碼地址:github.com/wuchengithu…

結語:

GoMVP是一個基於AOP的MVP框架,在開發過程中可以減少模版程式碼的書寫,提高開發效率的同時也具備著MVP架構的擴充套件性,同時框架也在探索更多的AOP特性,會不斷的進行優化和迭代。下面幾章將會介紹GoMVP的實現原理。

相關文章