Retrofit + EventBus的一点总结

说起Android应用开发的网络请求框架,最流行也最优秀的两个,一个是Volley,另一个是Retrofit. 今天来聊聊我在项目中对Retrofit的应用实践.

Retrofit介绍

Retrofit是明星程序员、男神Jake Wharton所在的Square公司开源的一个RESTful网络请求框架, 目前最新版是2.1.0,但是本文基于Retrofit 1.9.x,2.x 版本相对于1.x 有非常大的更新,以后有机会再研究一下2.x版本. 本文不涉及Retrofit的入门介绍,不了解的亲们可以查看文档和Github项目主页, 网络上也有大量的入门介绍文章. 本文主要讲讲我在实际项目中对Retrofit和EventBus结合使用的总结。

EventBus介绍

EventBus是一个Android平台的事件总线框架,使用简单、轻量、低开销,可以用于代码的解耦. 基本介绍和用法见项目主页.

为什么使用EventBus

大家知道Retrofit中声明一个API接口的方式如下:
我们定义一个interface 叫做 MyRetrofitService(我知道MyXxx这样的命名有点土, 但是作为示例, 观众朋友们忍耐一下吧-_- ), 里面声明一个登录方法:

@Headers("Content-Type: application/json;charset=UTF-8")
@POST("/api/appLogin")
void login(@Body LoginReq loginReqBody, Callback cb);

其中Callback是使用Retrofit提供的接口retrofit.Callback,用于接收请求响应. LoginRespBaseResp的子类.

如果我们在UI层代码中调用接口的时候是类似下面的写法:

MyRestService.getInstance().login(new loginReqBody("username","password"), new Callback {
    @Override
    public void failure(RetrofitError err) {
        handleRetrofitError(err);
    }

    @Override
    public void success(final LoginResp arg0, Response arg1) {
        //TODO: 处理响应
    }
});

那么很显然UI层的业务逻辑代码和Retrofit的代码紧紧耦合在了一起,剪不断,理还乱. 如果哪天项目不再爱Retrofit了,想要更换网络请求框架,不再使用Retrofit,那么所有的调用网络接口的UI层代码都要一番改动,这样代码维护成本高,而且极易引入bug.

我们要做的是将业务逻辑和网络请求两层做分离,解耦.

  • 最初的尝试
    定义一个ResponseHandler抽象类,实现Callback接口, 在UI层调用时传入ResponseHandler类的实例,这样UI层代码不再直接依赖Retrofit的代码,改为依赖ResponseHandler类. ResponseHandler类的实现大致如下:
public abstract class ResponseHandler implements Callback {

    @Override
    public void success(BaseResp resp, Response response) {
        //TODO: 如果需要的话,做一些针对resp的处理
        onSuccess(resp);
    }

    @Override
    public void failure(RetrofitError retrofitError) {
        int errCode = getErrCode(retrofitError);
        String errMsg = getErrMsg(retrofitError);
        onFailure0(errCode, errMsg);
    }

    public abstract void onSuccess(BaseResp resp);
    public abstract void onFailure0(int errorCode, String errorMsg);
}

这样已经达到了解耦的目的,好比你跟一个人网聊,每天聊得开心开心极了,但是你不知道那边手机或电脑后边是不是已经换了人了,反正对你来说体验一样. 相对于直接面聊,隔了一层网络,你和TA被网络解耦了。

  • 更进一步
    考虑这样一个需求,如果一个Activity里有两个Fragment,左侧列表FragmentA和右侧详情FragmentB,在FragmentA中点击一个按钮时,调用一个接口,然后根据接口返回结果FragmentB需要相应的刷新界面.

    实现这个需求有很多方式,使用事件总线是比较好的一种。事件总线可以自己实现,也可以使用一些优秀的开源框架,比如EventBus.
    一种方式是在FragmentA中接收接口回调,然后再用EventBus通知FragmentB. 那么既然已经引入了事件总线,何不把网络接口请求的响应用EventBus的事件形式发出来,这样代码看起来更清楚美观些,接下来是这种方式的实践总结.

Retrofit + EventBus


首先要有一个全局的EventBus单例实例,可以放在Application里,也可以如下:

    public class EventBusProvider {
        private static final EventBus mEventBus;
        static {
            mEventBus = EventBus.builder().logNoSubscriberMessages(false).sendNoSubscriberEvent(false).build();
        }

        public static final EventBus getEventBus() {
            return mEventBus;
        }
}


所有需要处理网络请求的Activity都继承自一个BaseActivity,在BaseActivity里加一个onEvent()方法,因为onEvent()方法是EventBus的监听者类必须有的一个方法,这样避免所有的activity都去写onEvent()方法.

BaseActivityonCreate()方法里注册监听EventBusProvider.getEventBus().register(this);
onDestroy()里解注册EventBusProvider.getEventBus().unregister(this);

BaseActivity类:

public class BaseActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        EventBusProvider.getEventBus().register(this);
        super.onCreate(savedInstanceState);
    }

    @Override
    protected void onDestroy() {
        EventBusProvider.getEventBus().unregister(this);
        super.onDestroy();
    }

    public final void onEvent(NwEvent event) {
        if (event != null && event.type.mainType == getNwEventMainType()) {
            handleNwEvent(event);
        }
    }

    //这个方法是final的,因为子类不需要覆盖这个方法,这个方法会返回当前子类的类型
    protected final Type getNwEventMainType() {
        return this.getClass();
    }

    /**
     * NOTE: 需要处理网络请求响应的子类,要覆盖这个方法来处理响应
     */
    protected void handleNwEvent(NwEvent event) {
    }
}

**NOTE: ** BaseFragmentBaseActivity类似.

NwEvent是网络事件类:

public class NwEvent {

    public NwEventType type = null;
    public BaseResp content = null;
    public boolean success = false;
    public MyRestService.ErrorType errorType = MyRestService.ErrorType.NOT_SPECIFIED;

    public NwEvent() {
        this.type = new NwEventType();
    }

    public NwEvent(NwEventType type, boolean success, BaseResp content, MyRestService.ErrorType errorType) {
        this.type = type != null ? type : new NwEventType();
        this.content = content;
        this.success = success;
        this.errorType = errorType;
    }
}

NwEventType是事件类型类,mainType表示是哪个类发出的请求的响应事件,subType用于区分一个类发出的多个请求:

public class NwEventType {
    public Type mainType = null;
    public int subType = -1;

    public NwEventType() {
    }

    public NwEventType(Type type) {
        this.mainType = type;
    }

    public NwEventType(Type mainType, int subType) {
        this.mainType = mainType;
        this.subType = subType;
    }

    @Override
    public String toString() {
        return "{ mainType: " + mainType + ", subType: " + subType + " }";
    }
}


另写一个ResponseHandler类,处理网络响应回调,并post事件,简要如下:

class ResponseHandler implements Callback {
    private NwEventType eventType = null;

    public ResponseHandler(NwEventType eventType) {
        this.eventType = eventType;
    }
    
    @Override
    public void success(BaseResp resp, Response response) {
        //TODO: 一些针对resp的判断、处理
        ErrorType errorType = ErrorType.OK;//TOOD: 根据你的逻辑.
        boolean result = resp != null;//或更具体的判断
        
        //然后发出Event, 在具体调用接口的Activity或Fragment就能收到onEvent()回调了
        EventBusProvider.getEventBus().post(new NwEvent(eventType, result, resp, errorType));
    }
    
    @Override
    public void failure(RetrofitError error) {
        //TODO: 一些针对error的判断、处理
        EventBusProvider.getEventBus().post(new NwEvent(eventType, false, null, errorType));
    }
}


接口声明还是一样:

@Headers("Content-Type: application/json;charset=UTF-8")
@POST("/api/appLogin")
void login(@Body LoginReq loginReqBody, Callback cb);

然后在MyRestService里对外的接口改为传入一个事件类型NwEventType即可,以为NwEventType中的mainTypesubType已经能够确定是哪个类发出的哪个请求:

public void login(String username, String password, NwEventType eventType) {
    mApiService.login(new LoginReq(username, password), new ResponseHandler(eventType));
}


UI层的调用. 比如在一个Activity里调用接口:

class MyExampleActivity extends BaseActivity {

    private static final int NW_EVENT_SUB_TYPE_LOGIN = 1;
    private static final int NW_EVENT_SUB_TYPE_GET_USER_INFO = 2;

    ...
    
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        
        ...
        MyRestService.getInstance().login(un, pw, new NwEventType(getNwEventMainType(), NW_EVENT_SUB_TYPE_LOGIN));
        ...
        
        
    }

    ...
    
    @Override
    protected void handleNwEvent(NwEvent event) {
        switch (event.type.subType) {
            case NW_EVENT_SUB_TYPE_LOGIN:
                handleLoginResp(event);
                break;
            case NW_EVENT_SUB_TYPE_GET_USER_INFO:
                handleUserInfoResp(event);
                break;
            
        }
    }
    
    ...
    
    private void handleLoginResp(NwEvent event) {
        //TODO 处理登录响应
        
        //如果登录成功,而且有需要,调用获取用户信息接口
        MyRestService.getInstance().getUserInfo(new NwEventType(getNwEventMainType(), NW_EVENT_SUB_TYPE_GET_USER_INFO));
    }
    
    ...
}

总结

以上方式基本实现了网络层和UI、业务逻辑层的解耦. 但是对于EventBus的使用并不限于如此,比如NwEventType的实现就可以改为用API接口的编号来实现,这样一个类发出的请求就不被拘泥于一定要自己这个类来监听处理,也就可以直接实现我们在"更进一步"小节中提到的需求. 等等.

献花拍砖请随意-_-.

你可能感兴趣的:(Retrofit + EventBus的一点总结)