目前Android開發幾乎都離不開網絡請求,而很多Android App網絡框架都使用Retrofit來發送網絡請求和響應交互,其優點是一底層依賴了強大靈活的Okhttp,二是其符合標準的RESTFUL和後端交互更爽。
本身Retrofit已經封裝得很好了,其使用也很簡單:
//定以接口
public interface GitHubService {
@GET("users/{user}/repos")
Call<List<Repo>> listRepos(@Path("user") String user);
}
//獲取實例
Retrofit retrofit = new Retrofit.Builder()
//設置OKHttpClient,如果不設置會提供一個默認的
.client(new OkHttpClient())
//設置baseUrl
.baseUrl("https://api.github.com/")
//添加Gson轉換器
.addConverterFactory(GsonConverterFactory.create())
.build();
GitHubService service = retrofit.create(GitHubService.class);
Call<List<Repo>> call = service.listRepos("octocat");
//異步請求
clone.enqueue(new Callback<List<Repo>>() {
@Override
public void onResponse(Call<List<Repo>> call, Response<List<Repo>> response) {
// Get result bean from response.body()
List<Repo> repos = response.body();
...
}
@Override
public void onFailure(Call<List<Repo>> call, Throwable t) {
}
});
所以,即便Retrofit已經做好了大量封裝工作,但是如果在app層使用的時候,如果直接使用將會在代碼中到處充斥了這樣的冗餘代碼,作爲一名資深碼農,肯定不會讓這樣的事情發生,爲了偷懶,先要動腦筋。因此,如何更加方便的使用網絡請求,讓代碼看起來更加優雅,這裏奉上自己的勞動成果,不敢獨享,兩行代碼搞定請求。
先來看看我最後的使用效果,用起來是像下面這樣的:
/**
* GET 請求 示例:
*/
private void clickGetButton(){
String [] strArray = {"Android", "1"};
RetrofitClient.get("newAppVerInfo", strArray, new BaseCallback<NewAppVerInfo>() {
@Override
protected void on200Resp(NewAppVerInfo newAppVerInfo){
textView.setText("收到GET結果: newAppVerInfo = " + new Gson().toJson(newAppVerInfo));
}
});
}
/**
* POST 請求 示例:
*/
private void clickPostButton(){
LoginReq loginReq = new LoginReq("13051892977", FormatUtil.md5("123456"), "phoneID");
RetrofitClient.post("loginInfo", loginReq, new BaseCallback<LoginInfo>() {
@Override
protected void on200Resp(LoginInfo loginInfo){
textView.setText("收到POST結果: loginInfo = " + new Gson().toJson(loginInfo));
}
});
}
說明:
1. 上面 GET 和 POST 請求的第一行都是在組裝發送請求所需要的參數,第二行即發送請求,請求回來的callback在UI線程,可以直接操作view,默認只需要override 200 response時的操作,如果想對Non-200的response override當然也可以。
2. 上例中的newAppVerInfo和loginInfo都是定義在EHService中的接口函數名(見下)。
3. callback中的NewAppVerInfo和LoginInfo是定義在EHService中的接口函數中Call中的類型,也是你自己最終想得到的。
其中EHService如下:
public interface EHService {
@GET("product/getNewVersionInfo.json")
Call<NewAppVerInfo> newAppVerInfo(@Query("OSType") String OSType, @Query("DevType") String DevType);
@Headers({"Content-Type: application/json","Accept: application/json"})//需要添加頭
@POST("login/login.json")
Call<LoginInfo> loginInfo(@Body RequestBody loginReq);
}
有沒有覺得很方便,很爽?!好的,接下來,我們來看看是如何實現的!這裏最核心的代碼都封裝在RetrofitClient中,首先對於Retrofit的獲取採用了單例:
public class RetrofitClient {
private static Retrofit retrofit;
private static EHService service;
private RetrofitClient(){}
private static void initRetrofitClient(){
if(retrofit == null || service == null){
synchronized (RetrofitClient.class){
if(retrofit == null){
retrofit = new Retrofit.Builder() .baseUrl("https://your.base.url/") .addConverterFactory(GsonConverterFactory.create())
.build();
service = retrofit.create(EHService.class);
}
}
}
}
public static EHService requestHttp(){
initRetrofitClient();
return service;
}
public static <E extends BaseModel> Call<E> get(String getFuncName, String[] paramArray, BaseCallback<E> baseCallback){
Call<E> typeCall = null;
try {
typeCall = (Call<E>) invokeMethod(requestHttp(), getFuncName, paramArray);
typeCall.enqueue(baseCallback);
}catch (NoSuchMethodException e){
Log.e("TAG", "NoSuchMethodException e = " + e.getMessage());
}catch (IllegalAccessException e){
Log.e("TAG", "IllegalAccessException e = " + e.getMessage());
}catch (InvocationTargetException e){
Log.e("TAG", "InvocationTargetException e = " + e.getMessage());
}catch (Exception e){
Log.e("TAG", "Exception e = " + e.getMessage());
}
return typeCall;
}
/**
* 反射調用方法
* @param newObj 實例化的一個對象
* @param methodName 對象的方法名
* @param args 參數數組
* @return 返回值
* @throws Exception
*/
public static Object invokeMethod(Object newObj, String methodName, Object[] args)throws Exception {
Class ownerClass = newObj.getClass();
Class[] argsClass = new Class[args.length];
for (int i = 0, j = args.length; i < j; i++) {
argsClass[i] = args[i].getClass();
}
Method method = ownerClass.getMethod(methodName, argsClass);
return method.invoke(newObj, args);
}
public static <T, E extends BaseModel> Call<E> post(String postFuncName, T postBean, BaseCallback<E> baseCallback){
Call<E> typeCall = null;
RequestBody requestBody = getRequestBody(postBean);
try {
Method EHServiceMethod = EHService.class.getDeclaredMethod(postFuncName, RequestBody.class);
typeCall = (Call<E>) EHServiceMethod.invoke(requestHttp(), requestBody);
typeCall.enqueue(baseCallback);
}catch (NoSuchMethodException e){
Log.e("TAG", "NoSuchMethodException e = " + e.getMessage());
}catch (IllegalAccessException e){
Log.e("TAG", "IllegalAccessException e = " + e.getMessage());
}catch (InvocationTargetException e){
Log.e("TAG", "InvocationTargetException e = " + e.getMessage());
}catch (Exception e){
Log.e("TAG", "Exception e = " + e.getMessage());
}
return typeCall;
}
public static <T> RequestBody getRequestBody(T postBean){
//通過Gson將Bean轉化爲Json字符串形式
String reqBeanJson = new Gson().toJson(postBean);
return RequestBody.create(okhttp3.MediaType.parse("application/json; charset=utf-8"), reqBeanJson);
}
}
首先通過單例函數initRetrofitClient(),保證了後面無論執行多少次都只產生一次 retrofit 和 service, 這正是requestHttp()在後期所做的。
下面我們再看get函數,其參數分別爲getFuncName, String[] paramArray, BaseCallback baseCallback。通過 invokeMethod(requestHttp(), getFuncName, paramArray);反射的方式間接執行了原本定義在EHService中的接口函數,從而實現了發送請求。主要藉助了invokeMethod執行了變參函數。
首先通過反射在EHService Class中找到參數中傳過來的對應的Method,然後賦給其paramArray參數作爲接口中對應的參數執行即可。
get請求相對比較容易理解,下面主要看看post相對來說比較複雜一點的是,其發送請求時需要在post請求有一個json的body帶參數過去,而且這些參數的格式都是不確定的。這裏我使用了泛型,開發者自行定義json對應的bean entity即可在內部自動解析爲json格式進行發送。同理這裏也是使用了反射,通過postFuncName找到EHService Class中找到參數中傳過來的對應的Method,然後執行即可。
這裏有一個BaseCallback callback,那麼BaseCallback裏又做了什麼呢?我們來看:
public abstract class BaseCallback<T extends BaseModel> implements Callback<T> {
@Override
public void onResponse(Call<T> call, Response<T> response) {
int networkRespCode = response.raw().code();
Log.i("TAG", "BaseCallback onResponse! networkRespCode = " + networkRespCode);
T bean = response.body();
if (networkRespCode == 200) { // 200是服務器有合理響應, IP層的HTTP迴應
if(bean.getCode() == 200) on200Resp(bean); // Response 裏 json 層 code: 200
else onNon200Resp(bean);
}else
onFailure(call, new RuntimeException("response error,detail = " + response.raw().toString()));
}
@Override
public void onFailure(Call<T> call, Throwable t) {
Log.e("TAG", "BaseCallback onFailure! call = " + call.request().url().toString());
onFailure(t);
}
/**
* on200Resp() onNon200Resp() onFailure() 無論如何都將是在UI線程中執行!即使是把 retrofitClient 放入子線程中仍是如此!
* 當需要處理 onNon200Resp,onFailure 的場景時,則 override 此方法
*
*/
protected abstract void on200Resp(T bean);
void onNon200Resp(T bean){
Log.e("TAG", "receive non-200 response, code is " + bean.getCode());
}
void onFailure(Throwable t){
Log.e("TAG", "on Failure! Throwable message = " + t.getMessage() + ", stack trace = " + t.getStackTrace());
}
}
這裏實際上是對http response在應用層(即返回的json)中做了進一步的封裝處理,把onResponse json中的 return code 分成了 200 OK 和 non-200 response兩種情況,只把200 response作爲abstract留給開發者來override,當然如果想對non-200做處理也可以在callback中override。
好的,基本上基本原理就是如此簡單。如果有什麼問題,可以留言。另外,你可以去github中看看具體的代碼和實例
https://github.com/AndrewXiao/SimpleRetrofit