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

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

我們接著上一節的內容繼續:GoMVP(一)基於AOP的MVP框架GoMVP的使用

通過new的方式初始化MarketRepository是不優雅的,不建議這樣做,真正的做法是使用RepositoryInjection:

7、RepositoryInjection

public class RepositoryInjection implements DataSourceInjection {
    @Override
    public GoDataSource provideRepository(@NonNull Context context) {
        checkNotNull(context);
        return new MarketRepository()
    }
}
複製程式碼

GoMVP提供了一個DataSourceInjection,通過實現DataSourceInjection來建立一個RepositoryInjection,上面onCreate方法內應該這樣寫:

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_simple_demo);
    //繫結資料倉儲
    presenter
    .setRepository(new RepositoryInjection().provideRepository(getApplicationContext()));
}
複製程式碼

框架支援註解建立Repository,前提是必須要用DataSourceInjection這個介面,我們下面會講到。

8、execute()

使用execute方法執行操作:

@OnClick({R.id.button2, R.id.button3})
public void onViewClicked(View view) {
    switch (view.getId()) {
        case R.id.button2:
            presenter.execute(new MarketPresenterAdapter());
            break;
        case R.id.button3:
            break;
    }
}
複製程式碼

execute的入參是一個PresenterAdapter。

9、使用@GoBack註解接受資料

@GoBack
public void hahaha(MarketBean bean) {
    GoLog.E("MarketBean is backing:" + bean.getValue().getMarketData().getAmountRtv());
}
複製程式碼

在Activity任意名稱的方法體上加上@GoBack就可以接收資料了,接收什麼資料,引數就要指定成什麼型別,比如這裡接收的是MarketBean,如果你在這裡隨便寫個什麼其他型別這個方法是不會被回撥的,為什麼呢,因為我們在執行presenter的excute方法傳入的是MarketPresenterAdapter物件,它指定了型別,返回頭看看MarketPresenterAdapter這個類的targetBeanType方法指定的型別是什麼?沒錯就是MarketBean,如果你想接收MarketPresenterAdapter的資料,就要與它相呼應。

    @Override
    public <T> void targetClazz(Class<T> clazz) {
        return MarketBean.class;
    }
複製程式碼

10、使用@GoError接收異常錯誤資訊

    @GoError
    public void error(String errorMsg) {
        GoLog.E("error is backing:" + errorMsg);
    }
複製程式碼

@GoError是用來接收異常或者業務錯誤的註解,接收方法名可以任意起名,但是引數一定是一個String型別。

二、進階使用

1、使用@DataSource註解注入Repository

我們改一下presnter變數的註解為這樣:

@DataSource(RepositoryInjection.class)
private LifecyclePresenter presenter;
複製程式碼

@DataSource註解的引數是一個DataSourceInjection型別的class,通過這個註解presenter的建立與Repository的注入一氣呵成,所以在onCreate方法中就不需要寫繫結的程式碼了。

public class AnnoDemoActivity extends AppCompatActivity{

    
    @DataSource(RepositoryInjection.class)
    private LifecyclePresenter presenter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_simple_demo);
        //不需要繫結資料倉儲的程式碼了
        
        messagePresenterAdapter = new MessageCountPresenter();
        messagePresenterAdapter.setStatus(0);
    }
    //其他程式碼。。。
}
複製程式碼

Note:如果同時存在@Presenter和@DataSource這兩個註解的變數,@DataSource註解的變數會生效,如果呼叫了presenter的setRepository,最後set的Repository會生效。

2、一個介面接收多個返回

有的時候,一個介面可能由多個介面資料組成,我們這裡有兩個PresenterAdapter,MarketPresenterAdapter和MessageCountPresenter,分別獲取市場資訊和訊息個數,如何在Activity裡接收呢?

我們在上面已經指定了MarketPresenterAdapter的targetClazz,現在我們指定MessageCountPresenter

public class MessageCountPresenter extends BasePresenterAdapter {

    //略去部分程式碼……

    @Override
    public Class targetBeanType() {
        return NoticeCountBean.class;
    }
}
複製程式碼

那麼接收的時候可以這麼寫了:

public class AnnoDemoActivity extends AppCompatActivity{

    @BindView(R.id.button2)
    Button button;
    @BindView(R.id.button3)
    Button button3;
    
    @Presenter()
    private LifecyclePresenter presenter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_simple_demo);
        //繫結資料倉儲
         presenter
            .setRepository(new RepositoryInjection().provideRepository(getApplicationContext()));
        
        messagePresenterAdapter = new MessageCountPresenter();
        messagePresenterAdapter.setStatus(0);
    }
    //接收市場資料
    @GoBack
    public void hahaha(MarketBean bean) {
        GoLog.E("MarketBean is backing:" + bean.getValue().getMarketData().getAmountRtv());
    }
    //接收訊息個數資料
    @GoBack
    public void receiverData(NoticeCountBean bean) {
        GoLog.E("NoticeCountBean is backing:" + bean);
    }
    //接收所有的異常返回
    @GoError
    public void error(String errorMsg) {
        GoLog.E("error is backing:" + errorMsg);
    }
    
    //接收訊息個數資料異常返回
    @GoError
    public void errorNoticeCount(NoticeCountBean bean,String errorMsg) {
        GoLog.E("error is backing:" + errorMsg);
    }
    
    @OnClick({R.id.button2, R.id.button3})
    public void onViewClicked(View view) {
        switch (view.getId()) {
            case R.id.button2:
                presenter.execute(new MarketPresenterAdapter());
                break;
            case R.id.button3:
                presenter.execute(messagePresenterAdapter);
                break;
        }
    }
}
複製程式碼

方法hahaha和方法receiverData分別接收MarketBean和NoticeCountBean,接收什麼例項和方法名無關,只於方法引數的具體型別有關。

同樣的,不同的execute操作會返回不同的異常錯誤資訊,如果不單獨指定,上面的error方法可以接收它們所有的異常,但有種情況,可能會針對不同的execute處理不同的異常,這種情況下就需要使用型別確定了,和接收一樣,增加一個包含型別的引數方法,比如errorNoticeCount方法,這個方法的第一個引數是NoticeCountBean物件,這樣通過@GoError這個註解,就可以接收到NoticeCount的錯誤異常,而只會接收到NoticeCount造成的業務異常。

PS:需要注意的是,errorNoticeCount接收的只是業務上的錯誤,比如token失效這樣的。如果碰到類似與(IO,超時,null指標,Json解析等)這樣的異常,只會在error裡返回,不會在errorNoticeCount返回。而error會接收所有的錯誤和異常包括errorNoticeCount返回的業務異常error也會接收到。

3、Json預處理

在擴充套件Presenter,也就是PresenterAdapter時,有兩個方法:

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

@Override
public String onErrorMessageKey() {
    return "message";
}
複製程式碼

框架中預設使用Json返回資料的格式,想象一下當一個資料返回時,我們可能需要處理一下再使用,最常見的是業務層面的成功與失敗,在定義的一般格式的json中最外層的屬性裡一定會有一個判斷業務成功與失敗的欄位,比如successCode,responseCode等等,你把這個欄位告訴框架,並且告訴框架這個欄位表示成功的值是什麼。這裡的例子告訴框架,表示請求結果的成功或失敗的欄位名為"success",表示成功的值為"true",這樣框架處理資料時,如果發現沒有這個欄位,或者有“success”這個欄位但值不為"true"時(注意這裡的值是個字串的true,這是因為業務具體而定,也許是個整形的0,或者boolean的true),將會認為是個錯誤的請求,把結果通過@GoError標註的方法在Activity返回。

而onErrorMessageKey方法是用來確定,當返回業務錯誤時,我們需要解析哪個欄位來返回錯誤資訊,比如當我們執行一些敏感操作時,會攜帶token 去客戶端做驗證,如果token失效會返回一個錯誤碼,如果返回欄位中有錯誤資訊欄位,我們可以直接告訴框架這個欄位是什麼,這裡這個欄位叫“message”,這樣當框架發現success欄位返回的不是“true”時,就會解析message欄位,將錯誤提示內容,比如“token失效”通過@GoError修飾的方法返回。當然框架無法判斷多種情況,如果覺的這兩個方法無法滿足使用者的業務需求,我們還可以通過“攔截”返回資料,來處理多變的業務邏輯。

PS:如果不處理上面兩個方法,會發生什麼?

如果不處理上面兩個方法,框架將不會關心業務異常,會把資料生成的JavaBean返回到View層,而處理異常的@GoError修飾的方法將不會收到業務異常,只會收到執行時的異常(比如IO,網路,解析json等這些異常)因為使用者並沒有定義。

如果想在框架處理返回資料之前對資料“動手腳”,我們可以在我們自己的PresenterAdapter上實現InterceptGoBack這個介面,我們下一節介紹“攔截”。

相關文章