如何js使用ajax异步请求Retrofit请求非Restful API

2425人阅读
Android开发(1048)
何为非Restful Api?
本文转自简书-键盘男kkmike999 :
Restful Api
User数据,有uid、name,Restful Api返回数据:
"name": "kkmike999",
在数据库没找到User,直接返回错误的http code。但弊端是当在浏览器调试api,后端查询出错时,很难查看错误码&错误信息。(当然用chrome的开发者工具可以看,但麻烦)
Not Restful Api
但不少后端工程师,并不一定喜欢用Restful Api,他们会自己在json中加入ret、msg这种数据。当User正确返回:
"msg": "成功",
"name": "kkmike999"
错误返回:
"ret": -1,
"msg": "失败"
这样的好处,就是调试api方便,在任意浏览器都可以直观地看到错误码&错误信息。
文章虽然是转载的,但是我对下面的东西有点犹豫不决,迟疑了好几天要不要继续这个博客。
Retrofit一般用法
本来Retrofit对restful的支持,可以让我们写少很多冤枉代码。但后端这么搞一套,前端怎么玩呀?既然木已成舟,我们做APP的总不能老对后端指手画脚,友谊小船说翻就翻。
先说说retrofit普通用法
public class User {
public interface UserService {
@GET("not_restful/user/{name}.json")
Call&User& loadUser(@Path("name") String name);
Bean和Service准备好,接下来就是调用Retrofit了:
OkHttpClient client = new OkHttpClient.Builder().build()
Retrofit retrofit = new Retrofit.Builder().baseUrl("http://***./")
.addConverterFactory(GsonConverterFactory.create())
.client(client)
UserService userService = retrofit.create(UserService.class)
User user = userService.loadUser("kkmike999")
.execute()
此处加入了GsonConverterFactory,没有使用RxJavaCallAdapter。如果是restful api,直接返回User的json,那调用execute().body()就能获得正确的User了。然而,not restful api,返回一个不正确的User ,也不抛错,挺难堪的。
ResponseConverter
我们留意到GsonConverterFactory,看看源码:
package retrofit2.converter.
import com.google.gson.G
import com.google.gson.TypeA
import com.google.gson.reflect.TypeT
import java.lang.annotation.A
import java.lang.reflect.T
import okhttp3.RequestB
import okhttp3.ResponseB
import retrofit2.C
import retrofit2.R
public final class GsonConverterFactory extends Converter.Factory {
public static GsonConverterFactory create() {
return create(new Gson());
public static GsonConverterFactory create(Gson gson) {
return new GsonConverterFactory(gson);
private final G
private GsonConverterFactory(Gson gson) {
if (gson == null) throw new NullPointerException("gson == null");
this.gson =
public Converter&ResponseBody, ?& responseBodyConverter(Type type, Annotation[] annotations, Retrofit retrofit) {
TypeAdapter&?& adapter = gson.getAdapter(TypeToken.get(type));
return new GsonResponseBodyConverter&&(gson, adapter);
public Converter&?, RequestBody& requestBodyConverter(Type type,
Annotation[] parameterAnnotations, Annotation[] methodAnnotations, Retrofit retrofit) {
TypeAdapter&?& adapter = gson.getAdapter(TypeToken.get(type));
return new GsonRequestBodyConverter&&(gson, adapter);
responseBodyConverter方法返回GsonResponseBodyConverter,我们再看看GsonResponseBodyConverter源码:
package retrofit2.converter.
final class GsonResponseBodyConverter&T& implements Converter&ResponseBody, T& {
private final G
private final TypeAdapter&T&
GsonResponseBodyConverter(Gson gson, TypeAdapter&T& adapter) {
this.gson =
this.adapter =
public T convert(ResponseBody value) throws IOException {
JsonReader jsonReader = gson.newJsonReader(value.charStream());
return adapter.read(jsonReader);
} finally {
value.close();
先给大家科普下,TypeAdapter
重写GsonResponseConverter
由源码看出,是GsonResponseBodyConverter对json进行解析的,只要重写GsonResponseBodyConverter,自定义解析,就能达到我们目的了。
但GsonResponseBodyConverter和GsonConverterFactory都是final class,并不能重写。靠~ 不让重写,我就copy代码!
新建retrofit2.converter.gson目录,新建CustomConverterFactory,把GsonConverterFactory源码拷贝过去,同时新建CustomResponseConverter。 把CustomConverterFactory的GsonResponseBodyConverter替换成CustomResponseConverter:
public final class CustomConverterFactory extends Converter.Factory {
public Converter&ResponseBody, ?& responseBodyConverter(Type type, Annotation[] annotations, Retrofit retrofit) {
TypeAdapter&?& adapter = gson.getAdapter(TypeToken.get(type));
return new CustomResponseConverter&&(gson, adapter);
写CustomResponseConverter:
public class CustomResponseConverter&T& implements Converter&ResponseBody, T& {
private final G
private final TypeAdapter&T&
public CustomResponseConverter(Gson gson, TypeAdapter&T& adapter) {
this.gson =
this.adapter =
public T convert(ResponseBody value) throws IOException {
String body = value.string();
JSONObject json = new JSONObject(body);
ret = json.optInt("ret");
String msg = json.optString("msg", "");
if (ret == 0) {
if (json.has("data")) {
Object data = json.get("data");
body = data.toString();
return adapter.fromJson(body);
return (T)
throw new RuntimeException(msg);
} catch (Exception e) {
throw new RuntimeException(e.getMessage());
} finally {
value.close();
为什么我们要新建retrofit2.converter.gson目录?因为GsonRequestBodyConverter不是public class,所以CustomConverterFactory要import GsonRequestBodyConverter就得在同一目录下。当然你喜欢放在自己目录下,可以拷贝源码如法炮制。
接下来,只要 new Retrofit.Builder().addConverterFactory(CustomConverterFactory.create())就大功告成了!
注:以上写的Converter如果不理解没有关系,上面的用法基本用不到,毕竟我们可以通过别的方法解决。下面的Service注解很有意思。
玩转Service注解
既然retrofit能“理解”service方法中的注解,我们为何不试试?GsonConverterFactory的方法responseBodyConverter(Type type, Annotation[] annotations, Retrofit retrofit),这里有Annotation[],没错,这就是service方法中的注解。
我们写一个@Data注解类:
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Data {
String value() default "data";
在loadUser(…)添加@Data:
@Data("user")
@GET("not_restful/user/{name}.json")
Call&User& loadUser(@Path("name") String name);
修改CustomResponseConverter
public class CustomResponseConverter&T& implements Converter&ResponseBody, T& {
private final G
private final TypeAdapter&T&
private final S
public CustomResponseConverter(Gson gson, TypeAdapter&T& adapter, String name) {
this.gson =
this.adapter =
this.name =
public T convert(ResponseBody value) throws IOException {
if (ret == 0) {
if (json.has(name)) {
Object data = json.get(name);
body = data.toString();
return adapter.fromJson(body);
给CustomConverterFactory的responseBodyConverter(…)加上
public Converter&ResponseBody, ?& responseBodyConverter(Type type, Annotation[] annotations, Retrofit retrofit)
String name = "data";// 默认"data"
for (Annotation annotation : annotations) {
if (annotation instanceof Data) {
name = ((Data) annotation).value();
return new CustomResponseConverter&&(gson, adapter, name);
这么写后,后端改什么名称都不怕!
更灵活的Converter
有个需求:APP显示某班级信息&学生信息。后台拍拍脑袋:
"msg": "",
"users": [
"name": "鸣人",
"name": "佐助",
"name": "第七班"
哭了吧,灭了后端工程师恐怕也难解心头之恨!
我们意识到,CustomResponseConverter责任太重,又是判断ret、msg,又是解析json数据并返回bean,如果遇到奇葩json,CustomResponseConverter远远不够强大,而且不灵活。
怎么办,干嘛不自定义converter呢?
问题来了,这个converter应该如何传给CustomConverterFactory?因为在new Retrofit.Builder().addConvertFactory(…)时就要添加ConverterFactory,那时并不知道返回json是怎样,哪个service要用哪个adapter。反正通过构造方法给CustomConverterFactory传Converter肯定行不通。
我们上面不是用过Annotaion吗?同样手段再玩一把如何。写一个@Converter注解:
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Converter {
Class&? extends AbstractResponseConverter& converter();
并且写一个Converter抽象类:
public abstract class AbstractResponseConverter&T& implements Converter&ResponseBody, T&{
protected G
public AbstractResponseConverter(Gson gson) {
this.gson =
为什么要写一个继承Converter抽象类?让我们自定义的Converter直接继承Converter不行吗?
注意了,@Adapter只能携带Class&?&和int``String等基本类型,并不能带converter对象。而我们需要CustomConverterFactory在responseBodyConverter()方法中,通过反射,new一个converter对象,而CustomConverterFactory并不知道调用Converter哪个构造函数,传什么参数。所以,干脆就写一个AbstractResponseConverter,让子类继承它,实现固定的构造方法。这样CustomConverterFactory就可以获取固定的构造方法,生成Converter对象并传入如gson``typeAdapter参数了。
public class ClazzInfo{
List&Student&
public class ClassConverter implements AbstractResponseConverter&ClazzInfo&{
public ClassConverter(Gson gson){
super(gson);
public ClazzInfo convert(ResponseBody value) throws IOException {
ClazzInfo clazz = ...
public Converter ?& responseBodyConverter(Type type, Annotation[] annotations, Retrofit retrofit) {
for (Annotation annotation : annotations) {
if (annotation instanceof Converter) {
Class&? extends AbstractResponseConverter& converterClazz = ((Converter) annotation). converter();
// 获取有 以gson参数的 构造函数
Constructor&? extends AbstractResponseConverter& constructor = converterClazz .getConstructor(Gson.class);
AbstractResponseConverter
converter = constructor.newInstance(gson);
return converter;
} catch (Exception e) {
e.printStackTrace();
return new CustomResponseConverter&&(gson, adapter, name);
Service方法注解:
@Converter(converter = ClassConverter.class)
@GET("not_restful/class/{cid}.json")
Call&ClazzInfo& loadClass(@Path("cid") String cid);
写到这里,已经快吐血了。怎么会有这么奇葩的后端…. 正常情况下,应该把”users”和”class”封装在”data”里,这样我们就可以直接把返回结果写成Call& ClassInfo&就可以了。
Converter,让你的入参和返回类型丰富起来
RequestBodyConverter
1.5.1章节。
ResponseBodyConverter
Retrofit 也支持自定义 ResponseBodyConverter。
我们再来看下我们定义的接口:
public interface GitHubService {
@GET("users/{user}/repos")
Call&List&Repo&& listRepos(@Path("user") String user);
返回值的类型为 List& Repo&,而我们直接拿到的原始返回肯定就是字符串(或者字节流),那么这个返回值类型是怎么来的呢?首先说明的一点是,GitHub 的这个 api 返回的是 Json 字符串,也就是说,我们需要使用 Json 反序列化得到 List& Repo&,这其中用到的其实是 GsonResponseBodyConverter。
问题来了,如果请求得到的 Json 字符串与返回值类型不对应,比如:
接口返回的 Json 字符串:
{"err":0, "content":"This is a content.", "message":"OK"}
返回值类型
class Result{
哇,这时候肯定有人想说,你是不是脑残,偏偏跟服务端对着干?哈哈,我只是示例嘛,而且在生产环境中,你敢保证这种情况不会发生??
这种情况下, Gson 就是再牛逼,也只能默默无语俩眼泪了,它哪儿知道字段的映射关系怎么这么任性啊。好,现在让我们自定义一个 Converter 来解决这个问题吧!
static class ArbitraryResponseBodyConverterFactory extends Converter.Factory{
public Converter&ResponseBody, ?& responseBodyConverter(Type type, Annotation[] annotations, Retrofit retrofit) {
return super.responseBodyConverter(type, annotations, retrofit);
static class ArbitraryResponseBodyConverter implements Converter&ResponseBody, Result&{
public Result convert(ResponseBody value) throws IOException {
RawResult rawResult = new Gson().fromJson(value.string(), RawResult.class);
Result result = new Result();
result.body = rawResult.
result.code = rawResult.
result.msg = rawResult.
static class RawResult{
当然,别忘了在构造 Retrofit 的时候添加这个 Converter,这样我们就能够愉快的让接口返回 Result 对象了。
注意!!Retrofit 在选择合适的 Converter 时,主要依赖于需要转换的对象类型,在添加 Converter 时,注意 Converter 支持的类型的包含关系以及其顺序。
了解此功能还是有必要的,但实践中基本用不上,尽量不要这么做!
&&相关文章推荐
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:9560269次
积分:70783
积分:70783
排名:第23名
原创:505篇
转载:916篇
评论:2684条
如果您认为本博客不错,读后觉得有收获,不妨打赏赞助我一下,让我有动力继续写出高质量的博客。
赠人玫瑰,手有余香。分享技术,传递快乐。
QQ交流群:
有心课堂会员,请加入VIP QQ交流群:
文章:28篇
阅读:97633
文章:69篇
阅读:4626842933人阅读
Android精华(41)
&是一个Square开发的类型安全的REST安卓客户端请求库。这个库为网络认证、API请求以及用发送网络请求提供了强大的框架 。理解OkHttp 的工作流程见&&&。
注意本文是基于Retrofit2.0讲解的 - 译者注。
Retrofit&库让从web api下载JSON 或者xml数据变的非常简单直接。一旦数据下载完成即将其解析成普通java类(POJO)。
首先确保在AndroidManifest.xml中请求了网络权限 :
&manifest&xmlns:android=&&
&&&&&uses-permission&android:name=&android.permission.INTERNET&&/&
&/manifest&
在app/build.gradle文件中添加如下代码:
dependencies&{
&&&&compile&'com.google.code.gson:gson:2.3'
&&&&compile&'com.squareup.retrofit:retrofit:2.0.0-beta1'
&&&&compile&'com.squareup.retrofit:converter-gson:2.0.0-beta2'&&
&&&&compile&'com.squareup.okhttp:okhttp:2.4.0'
过去,Retrofit 依赖于&库来解析JSON数据。 Retrofit 2 现在支持许多种解析方式来解析响应数据,包括&,一个由Square 创建的高效JSON解析库。
但是,它也存在一些&,,因此如果你不确定该使用哪种,就暂时使用Gson converter 吧。
有如下的解析库可以选择:
要能把json解析成数据对象,需要响应的Java对象(准确的讲是类),因此我们需要先自己构造Java对象。
从资源中创建Java对象
本文讨论了两种方法。第一种是手动创建,这要求你学会如何使用&&库。第二种是自动生成,使用。我们建议你使用手动的方式,因为这样你能更好的理解自动生成代码是如何工作的。
手动方式创建Java对象
&&讲述了关于如何利用Gson库创建自己的数据对象来供Retrofit使用。它演示了如何使用Gson 接收来自Rotten Tomatoes API的数据,但是对于其他RESTful web service方法是相同的。
自动生成Java对象
假设你已经有了JSON的响应结果,访问。
你可以选择注解方式为Gson,但这并不是完全必须的,因为java没有它们也能工作。目前,我们暂且选择None作为注解方式。(Retrofit1.9默认使用的是Gson解析,如果要和Retrofit一起使用,注解方式请选择Gson。不然生成的驼峰命名方式会导致解析失败)
接下来,把JSON 输出粘贴到文字输入框中:
点击Preview 按钮。你可以看到前面部分和下图类似:
把生成的类粘贴到项目的诸如models一类的子包之下,把类名Example重命名为反映数据模型的名称。以这里为例,我们命名为User。
注意:生成的代码中存在@Generated注解。安卓默认并没有javax.annotation library里面的许多东西。如果你希望保留@Generated注解,你需要添加如下的依赖。更多信息参见。或者,你可以直接删除这个注解,完全没有问题。
dependencies&{
&&provided&'org.glassfish:javax.annotation:10.0-b28'
创建Retrofit 实例
要向一个api发送我们的网络请求 ,我们需要使用&&类并指定service的base URL (通常情况下就是域名)。 & &
public&static&final&String&BASE_URL&=&&&;
Retrofit&retrofit&=&new&Retrofit.Builder()
&&&&.baseUrl(BASE_URL)
&&&&.addConverterFactory(GsonConverterFactory.create())
&&&&.build();
还需注意我们要指定一个factory 来对响应进行反序列化,使用的是 Gson library。就如这个中所说的,converters 被添加的顺序将是它们被Retrofit尝试的顺序。如果我们希望传入一个自定义的Gson 解析实例,也是可以指定的:
Gson&gson&=&new&GsonBuilder()
&&&&&&&&.setDateFormat(&yyyy-MM-dd'T'HH:mm:ssZ&)
&&&&&&&&.create();
Retrofit&retrofit&=&new&Retrofit.Builder()
&&&&.baseUrl(BASE_URL)
&&&&.addConverterFactory(GsonConverterFactory.create(gson))
&&&&.build();
添加多个converters - 译者注。
定义 Endpoints
在Retrofit 2中,endpoints是定义在一个interface里面的(其实1.9版本也是,只是用法略有不同),使用特殊的retrofit 注解来映射参数以及请求方法这些细节。另外,返回值始终是一个参数化了的Call&T&对象,比如Call&User&。如果你不需要任何类型安全的响应,你可以把返回值指定为Call&Response&。
比如,这个interface 用下面的方式定义了每个endpoint :
public&interface&MyApiEndpointInterface&{
&&&&@GET(&/users/{username}&)
&&&&Call&User&&getUser(@Path(&username&)&String&username);
&&&&@GET(&/group/{id}/users&)
&&&&Call&List&User&&&groupList(@Path(&id&)&int&groupId,&@Query(&sort&)&String&sort);
&&&&@POST(&/users/new&)
&&&&Call&User&&createUser(@Body&User&user);
注意到每个endpoint 都指定了一个关于HTTP(GET, POST, 等等。) &方法的注解以及用于分发网络调用的方法。而且这些方法的参数也可以有特殊的注解。
修改 base URL
一般地,base URL是在实例化&的时候定义的。Retrofit 2 允许你在注解里面重写base URL 。
&@POST(&&)
就如&(中文地址:&)所讨论的,还有一些允许你使用相对路径(不是完整的URL)修改
base URL的方法。
Multipart forms
如果你要提交多参数表单数据(multi-part form data),可以使用@Multipart与@Part注解:
@Multipart
@POST(&/some/endpoint&)
Call&SomeResponse&&someEndpoint(@Part(&name1&)&String&name1,&@Part(&name2&)&String&name2)
Form URL encoding
如果我们希望提交 form-encoded name/value ,我们可以使用@FormUrlEncoded 与 @FieldMap注解:
@FormUrlEncoded
@POST(&/some/endpoint&)
Call&SomeResponse&&someEndpoint(@FieldMap&Map&String,&String&&names);
Upgrading from Retrofit 1
如果你是想从 Retrofit 1升级过来,你应该记得在1.x版本中,如果你想定义一个异步的API请求而非同步请求,最后一个参数必须是Callback类型:
public&interface&MyApiEndpointInterface&{
&&&&@GET(&/users/{username}&)
&&&&void&getUser(@Path(&username&)&String&username,&Callback&User&&cb);
&&&&@GET(&/group/{id}/users&)
&&&&void&groupList(@Path(&id&)&int&groupId,&@Query(&sort&)&String&sort,&Callback&List&User&&&cb);
&&&&@POST(&/users/new&)
&&&&void&createUser(@Body&User&user,&Callback&User&&cb);
Retrofit 1 依赖于这个Callback类型作为最后一个参数,决定api请求是异步的。为了避免出现两种不同的调用模式,这个接口在Retrofit 2被统一了。现在你可以直接定义返回值为一个参数化了的Call&T&,如前面小节所讲。
我们现在可以把这些东西放在一起:
MyApiEndpointInterface&apiService&=
&&&&retrofit.create(MyApiEndpointInterface.class);
如果我们想异步请求这个API,我们如下调用这个service (注意取名为service 是Retrofit 的惯例,并不是语法的一部分):
String&username&=&&sarahjean&;
Call&User&&call&=&apiService.getUser(username);
call.enqueue(new&Callback&User&()&{
&&&&@Override
&&&&public&void&onResponse(Response&User&&response)&{
&&&&&&&&int&statusCode&=&response.code();
&&&&&&&&User&user&=&response.body();&&
&&&&@Override
&&&&public&void&onFailure(Throwable&t)&{
如上所示,Retrofit 将在后台线程下载与解析API数据,然后通过onResponse或者&onFailure方法把结果发送回UI线程。
同步请求呢?更简单,是这样的:
String&username&=&&sarahjean&;&
Call&User&&call&=&apiService.getUser(username);
User&user&=&call.execute();
Retrofit与Authentication
使用Authentication Headers
可以使用一个Interceptor来为请求添加Headers。要发送请求到一个authenticated API,如下:
Interceptor&interceptor&=&new&Interceptor()&{
&&@Override
&&public&Response&intercept(Chain&chain)&throws&IOException&{
&&&&Request&newRequest&=&chain.request().newBuilder().addHeader(&User-Agent&,&&Retrofit-Sample-App&).build();
&&&&return&chain.proceed(newRequest);
OkHttpClient&client&=&new&OkHttpClient();
client.interceptors().add(interceptor);
Retrofit&retrofit&=&new&Retrofit.Builder()
&&.baseUrl(&&)
&&.addConverterFactory(GsonConverterFactory.create())
&&.client(client)
&&.build();
注意在Retrofit 2中,interceptor 必须添加到一个自定义的OkHttpClient。而在Retrofit 1,它可以直接被builder 类设置。
使用 OAuth
为了用OAuth来认证,我们需要为每个网络请求注册一个特殊的header ,这个header 嵌入了在认证过程中为用户获取的access token。实际的认证过程需要使用第三方的library(比如&)完成,然后使用一个把&access token添加到header中。Retrofit与authentication的相关链接列举如下:
使用signpost认证的资料:
其它替代signpost的Android OAuth 库:
&&相关文章推荐
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:676194次
积分:7625
积分:7625
排名:第2772名
原创:89篇
转载:311篇
评论:117条
(1)(1)(1)(4)(13)(18)(16)(8)(2)(3)(4)(6)(1)(3)(8)(2)(5)(3)(5)(36)(34)(48)(27)(22)(27)(7)(12)(10)(10)(2)(12)(4)(17)(17)(9)(1)(2)同事拒绝Retrofit,怎么办? - 简书
同事拒绝Retrofit,怎么办?
photo-9-05c7fd30f416_副本.jpg
为什么使用Retrofit?
我在 前言 提到过HttpClient、OkHttp、Retrofit历史。Retrofit无缝衔接RxJava,请求结果解析都不需要手动写,用Gson处理json-&Object。
总的来说,就是
更清晰的结构
更简洁的代码
拒绝Retrofit的心态
旧代码不好改
担心Retrofit不能满足需求
自己写的代码比Retrofit屌
我猜大部分人都是第一种情况。项目做到一定程度,http层逻辑跟别的类耦合了,换Retrofit要大动干戈,老板不管你代码重构有的没的,项目催得紧......
第二种情况,也是很头疼的问题。很多公司不一定遵循Restful Api准则写接口,新手对如何修改Retrofit代码无从入手。叹息一下,还是用回旧代码吧。
对于这种情况,希望 对你有帮助!
不用犹豫,拿砖头拍死那个同事......
Retrofit官网例子:
Retrofit turns your HTTP API into a Java interface.
public interface GitHubService {
@GET("users/{user}/repos")
Call&List&Repo&& listRepos(@Path("user") String user);
The Retrofit class generates an implementation of the GitHubService interface.
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("/")
GitHubService service = retrofit.create(GitHubService.class);
Each Call from the created GitHubService can make a synchronous or asynchronous HTTP request to the remote webserver.
Call&List&Repo&& repos = service.listRepos("octocat");
即使从来未接触Retrofit的人,一看GitHubService listRepos方法,就了解这个api大概意思,url是什么,传什么参数。
思路:代理旧http代码
我们目的是写到好像Retrofit那样。Retrofit本身就运用了代理模式隐藏了请求的细节,因此,我们就模仿它写一个自己的代理层。
旧http使用方式
public loadUser(String value, Callback callback) {
new Thread(new Runnerable(){
public void run(){
MyHttpClient httpClient = new MyHttpClient();
Map&String, Object& params = new HashMap&&();
params.put("key", value);
String json = httpClient.get("user/kkmike999.json", params);
User user = new Gson().fromJson(json, User.class);
if (callback != null) {
callback.onLoaded(user);
}).start();
MyHttpClient
public class MyHttpClient {
public String get(String path, Map&String, Object& params) throws IOException {
OkHttpClient client = new OkHttpClient();
// "http://kkmike999-file./" + path
HttpUrl.Builder urlBuilder = new HttpUrl.Builder().scheme("http")
.host("kkmike999-file.")
.addPathSegment(path);
for (String key : params.keySet()) {
urlBuilder.addQueryParameter(key, params.get(key)
.toString());
HttpUrl httpUrl = urlBuilder.build();
Request request = new Request.Builder().url(httpUrl)
Response response = client.newCall(request)
.execute();
return response.body()
.string();
大概就是这几个类
Image 2.png
MyRetrofit
public class MyRetrofit {
public &T& T create(Class&T& clazz) {
if (!clazz.isInterface()) {
throw new RuntimeException("Service not interface");
return (T) Proxy.newProxyInstance(clazz.getClassLoader(), new Class[]{clazz}, new ServiceProxy());
ServiceProxy
public class ServiceProxy implements InvocationHandler {
public Object invoke(Object proxy, Method method, Object... args) throws Throwable {
// If the method is a method from Object then defer to normal invocation.
if (method.getDeclaringClass() == Object.class) {
return method.invoke(this, args);
ServiceMethod serviceMethod = new ServiceMethod(method);
= new Converter(new Gson(), serviceMethod.returnType);
HttpCall call = new HttpCall(serviceMethod, converter, args);
return call.request();
public class HttpCall&T& {
ServiceMethod serviceM
public HttpCall(ServiceMethod serviceMethod, Converter converter, Object[] args) {
this.client = new MyHttpClient();
this.converter =
this.serviceMethod = serviceM
this.args =
public T request() {
// 参数count与注释count不一致, 抛错
int argumentCount = args != null ? args.length : 0;
if (argumentCount != serviceMethod.argumentTypes.length) {
throw new IllegalArgumentException("Argument count (" + argumentCount + ") doesn't match expected count (" + serviceMethod.argumentTypes.length + ")");
Map&String, Object& params = new HashMap&&();
for (int p = 0; p & argumentC p++) {
params.put(serviceMethod.getQueryKey(p), args[p].toString());
String url = serviceMethod.url();
String json = client.get(url, params);
return (T) converter.convert(json);
} catch (IOException e) {
e.printStackTrace();
throw new RuntimeException(e.getMessage());// 请求失败
ServiceMethod:
protected M
protected Annotation[]
protected Annotation[][] argumentA
protected Class[]
protected Type
public ServiceMethod(Method method) {
this.method =
methodAnnotations = method.getDeclaredAnnotations();
argumentAnnotations = method.getParameterAnnotations();
argumentTypes = method.getParameterTypes();
returnType = method.getGenericReturnType();
* 从@Query注释,获取请求参数的key
public String getQueryKey(int index) {
for (Annotation annotation : argumentAnnotations[index]) {
if (annotation instanceof Query) {
return ((Query) annotation).value();
return "";
* 从@Get注释中,获取url
public String url() {
for (Annotation annotation : methodAnnotations) {
if (annotation instanceof Get) {
return ((Get) annotation).value();
throw new RuntimeException("no GET or POST annotation");
public class Converter&T& {
public Converter(Gson gson, Type type) {
adapter = gson.getAdapter(TypeToken.get(type));
T convert(String json) throws IOException {
return (T) adapter.fromJson(json);
最好Converter就抽象成一个接口,你不喜欢用Gson,用fastjson,可以随时切换。
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Get {
String value() default "";
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
public @interface Query {
String value() default "";
新Api使用方式
json数据:
"name":"kkmike999"
public interface UserApi {
@Get("user/kkmike999.json")
User loadUser(@Query("key") String value);
public void loadUser(String value, Callback callback) {
new Thread(new Runnerable(){
public void run(){
UserApi userApi = new MyRetrofit().create(UserApi.class);
User user = userApi.loadUser("test");
if (callback != null) {
callback.onLoaded(user);
}).start();
调用方式,99%像Retrofit,实际底层http,还是旧代码。
配合RxJava
现在很多开发者都离不开RxJava了。“Retrofit+RxJava简直无敌” 这种想法恐怕不仅仅是我有吧?
你的UserApi应该是这样的:
public interface UserApi {
@Get("user/kkmike999.json")
Observable&User& loadUserO(@Query("key") String value);
然后,只需要:
新增接口CallAdapter
public interface CallAdapter&T& {
T adapt(HttpCall call);
新增RxCallAdapter
public class RxCallAdapter&T& implements CallAdapter&Observable&T&&{
public Observable&T& adapt(final HttpCall call) {
return Observable
.create(new Observable.OnSubscribe&T&() {
public void call(Subscriber&? super T& subscriber) {
subscriber.onNext((T) call.request());
subscriber.onCompleted();
} catch (Exception e) {
subscriber.onError(e);
.subscribeOn(Schedulers.io());
修改ServiceProxy
public class ServiceProxy implements InvocationHandler {
public Object invoke(Object proxy, Method method, Object... args) throws Throwable {
// If the method is a method from Object then defer to normal invocation.
if (method.getDeclaringClass() == Object.class) {
return method.invoke(this, args);
ServiceMethod serviceMethod = new ServiceMethod(method);
= new Converter(new Gson(), serviceMethod.returnType);
CallAdapter
= new RxCallAdapter();
HttpCall call = new HttpCall(serviceMethod, converter, args);
return adapter.adapt(call);
修改ServiceMethod
public class ServiceMethod{
public ServiceMethod(Method method) {
returnType = method.getGenericReturnType();
if (returnType instanceof ParameterizedType) {
// Obserable&?& 里面的type
ParameterizedType parameterizedType = (ParameterizedType) returnT
returnType
= parameterizedType.getActualTypeArguments()[0];
RxJava Api调用方式
public void loadUser(String value, Callback callback){
userApi.loadUserO("test")
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Action1&User&() {
public void call(User user) {
if (callback != null) {
callback.onLoaded(user);
跟Retrofit使用方式一模一样了!
依赖关系:
依赖关系.png
使用Retrofit最终目的,就是让我们更好地单元测试!
上面的代码,都没提到UserPresenter,这是担心代码量太多。懂MVP的同学应该一看就明白。
public class UserPresenter {
UserView userV
public void loadUser(String value){...}
public class UserPresenterTest {
ArgumentCaptor&User&
UserPresenter userP
public void setUp() throws Exception {
RxJavaPlugins.getInstance()
.registerSchedulersHook(new RxJavaSchedulersHook() {
public Scheduler getIOScheduler() {
// io scheduler会新建线程,把io scheduler-&immediate scheduler, 异步-&同步
return Schedulers.immediate();
MockitoAnnotations.initMocks(this);
userPresenter = new UserPresenter(userApi, userView);
public void loadUser() throws InterruptedException {
User user = new User();
user.uid = 1;
user.name = "kkmike999";
when(userApi.loadUser("test")).thenReturn(Observable.just(user));
userPresenter.loadUser("test");
verify(userView).onUserLoaded(captor.capture());
User result = captor.getValue();
Assert.assertEquals(result.uid, 1);
Assert.assertEquals(result.name, "kkmike999");
其实ServiceProxy可以写得更解耦,例如Converter、CallAdapter都从外面传进来。
public class MyRetrofit {
public Converter converter(Type returnType) {
return new GsonConverter(new Gson(), returnType);
public CallAdapter callAdapter() {
return new RxCallAdapter();
可以参考Retrofit,MyRetrofit用Builder模式构建会更好,此处就不累赘了。
public class ServiceProxy implements InvocationHandler {
public ServiceProxy(MyRetrofit retrofit) {
this.retrofit =
public Object invoke(Object proxy, Method method, Object... args) throws Throwable {
converter = retrofit.converter(serviceMethod.actualType);
CallAdapter adapter
= retrofit.callAdapter();
HttpCall call = new HttpCall(client, serviceMethod, converter, args);
return adapter.adapt(call);
Java Demo:
无论你的同事出于什么理由,不想使用Retrofit,迁就一下也无妨,除非原来的代码真的一团糟。也许他只是未研究Retrofit,不想冒无谓的风险。
放下砖头,立地成佛
当你自己写一套代理,就可以享受Retrofit的美妙设计模式,同时也不违背原来代码(如果同事连你写代理都不赞同,拿砖头拍死他)。这样我们就能更好地单元测试。(可能你同事还没认识到单元测试重要性)当他看到你行云流水的代码,或许某天他就用你写的这个代理了。
关于MPV、Retrofit、单元测试,请参考
我是键盘男。在广州生活,在创业公司上班,猥琐文艺码农。喜欢科学、历史,玩玩投资,偶尔独自旅行。希望成为独当一面的工程师。
Android工程师。
在广州生活,悦跑圈Android工程师,猥琐伪文艺青年。
喜欢科学、历史,玩玩投资,偶尔旅行。

我要回帖

更多关于 请求资源正在使用中 的文章

 

随机推荐