說起Android應用開發(fā)的網(wǎng)絡請求框架,最流行也最優(yōu)秀的兩個状蜗,一個是Volley,另一個是Retrofit. 今天來聊聊我在項目中對Retrofit的應用實踐.
Retrofit介紹
Retrofit是明星程序員、男神Jake Wharton所在的Square公司開源的一個RESTful網(wǎng)絡請求框架幢炸, 目前最新版是2.1.0充岛,但是本文基于Retrofit 1.9.x保檐,2.x 版本相對于1.x 有非常大的更新,以后有機會再研究一下2.x版本. 本文不涉及Retrofit的入門介紹崔梗,不了解的親們可以查看文檔和Github項目主頁夜只, 網(wǎng)絡上也有大量的入門介紹文章. 本文主要講講我在實際項目中對Retrofit和EventBus結(jié)合使用的總結(jié)。
EventBus介紹
EventBus是一個Android平臺的事件總線框架炒俱,使用簡單盐肃、輕量、低開銷权悟,可以用于代碼的解耦. 基本介紹和用法見項目主頁.
為什么使用EventBus
大家知道Retrofit中聲明一個API接口的方式如下:
我們定義一個interface 叫做 MyRetrofitService
(我知道MyXxx
這樣的命名有點土, 但是作為示例, 觀眾朋友們?nèi)棠鸵幌掳?_- ), 里面聲明一個登錄方法:
@Headers("Content-Type: application/json;charset=UTF-8")
@POST("/api/appLogin")
void login(@Body LoginReq loginReqBody, Callback<LoginResp> cb);
其中Callback<LoginResp>
是使用Retrofit提供的接口retrofit.Callback<T>
砸王,用于接收請求響應. LoginResp
是BaseResp
的子類.
如果我們在UI層代碼中調(diào)用接口的時候是類似下面的寫法:
MyRestService.getInstance().login(new loginReqBody("username","password"), new Callback<LoginResp> {
@Override
public void failure(RetrofitError err) {
handleRetrofitError(err);
}
@Override
public void success(final LoginResp arg0, Response arg1) {
//TODO: 處理響應
}
});
那么很顯然UI層的業(yè)務邏輯代碼和Retrofit的代碼緊緊耦合在了一起,剪不斷峦阁,理還亂. 如果哪天項目不再愛Retrofit了谦铃,想要更換網(wǎng)絡請求框架,不再使用Retrofit榔昔,那么所有的調(diào)用網(wǎng)絡接口的UI層代碼都要一番改動驹闰,這樣代碼維護成本高,而且極易引入bug.
我們要做的是將業(yè)務邏輯和網(wǎng)絡請求兩層做分離撒会,解耦.
-
最初的嘗試:
定義一個ResponseHandler
抽象類嘹朗,實現(xiàn)Callback<T>
接口, 在UI層調(diào)用時傳入ResponseHandler
類的實例,這樣UI層代碼不再直接依賴Retrofit的代碼诵肛,改為依賴ResponseHandler
類.ResponseHandler
類的實現(xiàn)大致如下:
public abstract class ResponseHandler implements Callback<BaseResp > {
@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);
}
這樣已經(jīng)達到了解耦的目的,好比你跟一個人網(wǎng)聊,每天聊得開心開心極了褪秀,但是你不知道那邊手機或電腦后邊是不是已經(jīng)換了人了蓄诽,反正對你來說體驗一樣. 相對于直接面聊,隔了一層網(wǎng)絡媒吗,你和TA被網(wǎng)絡解耦了仑氛。
-
更進一步
考慮這樣一個需求,如果一個Activity里有兩個Fragment闸英,左側(cè)列表FragmentA和右側(cè)詳情FragmentB锯岖,在FragmentA中點擊一個按鈕時,調(diào)用一個接口甫何,然后根據(jù)接口返回結(jié)果FragmentB需要相應的刷新界面.實現(xiàn)這個需求有很多方式嚎莉,使用事件總線是比較好的一種。事件總線可以自己實現(xiàn)沛豌,也可以使用一些優(yōu)秀的開源框架趋箩,比如EventBus.
一種方式是在FragmentA中接收接口回調(diào),然后再用EventBus通知FragmentB. 那么既然已經(jīng)引入了事件總線加派,何不把網(wǎng)絡接口請求的響應用EventBus的事件形式發(fā)出來叫确,這樣代碼看起來更清楚美觀些,接下來是這種方式的實踐總結(jié).
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;
}
}
二娄琉,
所有需要處理網(wǎng)絡請求的Activity都繼承自一個BaseActivity
次乓,在BaseActivity
里加一個onEvent()
方法,因為onEvent()
方法是EventBus的監(jiān)聽者類必須有的一個方法孽水,這樣避免所有的activity都去寫onEvent()
方法.
在BaseActivity
的onCreate()
方法里注冊監(jiān)聽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: 需要處理網(wǎng)絡請求響應的子類女气,要覆蓋這個方法來處理響應
*/
protected void handleNwEvent(NwEvent event) {
}
}
**NOTE: ** BaseFragment
與BaseActivity
類似.
NwEvent
是網(wǎng)絡事件類:
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
表示是哪個類發(fā)出的請求的響應事件,subType
用于區(qū)分一個類發(fā)出的多個請求:
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類缘滥,處理網(wǎng)絡響應回調(diào),并post事件谒主,簡要如下:
class ResponseHandler implements Callback<BaseResp> {
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: 根據(jù)你的邏輯.
boolean result = resp != null;//或更具體的判斷
//然后發(fā)出Event, 在具體調(diào)用接口的Activity或Fragment就能收到onEvent()回調(diào)了
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<LoginResp> cb);
然后在MyRestService
里對外的接口改為傳入一個事件類型NwEventType
即可擎颖,以為NwEventType
中的mainType
和subType
已經(jīng)能夠確定是哪個類發(fā)出的哪個請求:
public void login(String username, String password, NwEventType eventType) {
mApiService.login(new LoginReq(username, password), new ResponseHandler(eventType));
}
五凹耙,
UI層的調(diào)用. 比如在一個Activity
里調(diào)用接口:
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 處理登錄響應
//如果登錄成功,而且有需要肠仪,調(diào)用獲取用戶信息接口
MyRestService.getInstance().getUserInfo(new NwEventType(getNwEventMainType(), NW_EVENT_SUB_TYPE_GET_USER_INFO));
}
...
}
總結(jié)
以上方式基本實現(xiàn)了網(wǎng)絡層和UI、業(yè)務邏輯層的解耦. 但是對于EventBus的使用并不限于如此备典,比如NwEventType的實現(xiàn)就可以改為用API接口的編號來實現(xiàn)异旧,這樣一個類發(fā)出的請求就不被拘泥于一定要自己這個類來監(jiān)聽處理,也就可以直接實現(xiàn)我們在"更進一步"小節(jié)中提到的需求. 等等.
獻花拍磚請隨意-_-.