基于AOP的MVP框架(二)GoMVP进阶注解

684 阅读6分钟

我们接着上一节的内容继续: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这个接口,我们下一节介绍“拦截”。