您的位置:首页 > 编程语言

两行代码搞定发送 Retrofit GET/POST 请求

2017-05-06 15:43 459 查看
目前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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: