我們接著上一節的內容繼續: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這個介面,我們下一節介紹“攔截”。