注:以下内容为本人自己做(chao)的学习笔记,可能含有少儿不宜的误导性内容,学习Retrofit请看参考文章。。。
参考文章:http://www.jianshu.com/p/0d919e54eef0
OkHttp使用:
//创建client对象
OkHttpClient client = new OkHttpClient();
//创建Request
Request request = new Request.Builder()
.url(url)
.build();
//执行请求,获取结果
client.newCall(request).enqueue(new Callback() {
@override
public void onFailure(Call call, IOException e) {
}
@override
public void onResponse(Call call, Response response) throws IOException {
content.setText(response.body().string());
}
});
以上既是OkHttp的使用方法,使用OkHttp时需要自己处理多线程,开启子线程发送请求,收到数据后需要使用Handler来更新UI。
放屁,OkHttp支持同步和异步两种方式发送请求,execute及enqueue。
Retrofit使用:
//定义API接口
public interface APIService {
@GET("/test.json")
Call<Model> getResult();
@GET("/search.json")
Call<SearchResult> getResult();
}
//创建Retrofit
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.client(new OkHttpClient())
.build();
//获取接口Service的代理对象
APIService apiService = retrofit.create(APIService.class);
//调用代理对象的方法获取到Call对象(Call对象是对Request的一层封装)
Call<Model> result = apiService.getResult();
//异步执行请求
result.enqueue(new Callback<Model>() {
@override
public void onResponse(Response<Model> response, Retrofit retrofit) {
content.setText(response.body().toString());
}
@override
public void onFailure(Throwable t) {
content.setText("error");
}
});
单从代码量来看,Retrofit并不比OkHttp需要写的代码少。之所以要对OkHttp做这样的封装,我觉得原因在于:
1.不需要知道什么是Request,什么是Client以及Call对象,闭着眼用就行。
2.把网络接口统一封装到一个接口类中,看上去更规整。
3.使用Annotation标识请求类型和路径,虽然我并不知道这么干有什么卵用,大家都说好才是真的好。
既然要分享框架,那就必须得看源码。
创建API接口类和用Builder创建Retrofit不提也罢。直接看重点:
APIService apiService = retrofit.create(APIService.class);
传一个接口类进去,返回一个实例给我。
厉害了。
其实也并没那么神奇,我猜测这个方法是这样的:传一个接口进去,里面用代码创建一个类,实现我传进去的这个接口,把那个类的实例返回给我。
咦?用代码创建类?对我这种白菜来说也是很新鲜的,用代码写代码看上去也是屌屌的。
喽一眼源码
public <T> T create(final Class<T> service) {
Utils.validateServiceInterface(service);
if (validateEagerly) {
eagerlyValidateMethods(service);
}
return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[] { service },
new InvocationHandler() {
private final Platform platform = Platform.get();
@Override 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);
}
if (platform.isDefaultMethod(method)) {
return platform.invokeDefaultMethod(method, service, proxy, args);
}
ServiceMethod<Object, Object> serviceMethod =
(ServiceMethod<Object, Object>) loadServiceMethod(method);
OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);
return serviceMethod.callAdapter.adapt(okHttpCall);
}
});
}
关键当然是在return那一句。咦,Proxy是个啥...
Java动态代理
先说说我是怎么理解代理的。作为年轻程序猿,大都非常老实本分加勤奋,不愿意卷入世俗纷争,假装与Boss谈笑风生。所以这个时候,就要给每一个程序猿配送一个解说员,由解说员去解决谈笑风生的事,程序猿专心写好程序就行了。这个解说员就可以看做是一个代理,事实上,这个代理的目的就是把程序猿和Boss进行解耦,让Boss专心谈笑风生,程序猿专心写程序。
然而事与愿违,小公司请不起那么多人当解说员,所以就有了动态代理,也就是Leader。Leader在不出事儿的时候(编译时)是喝茶的,一旦一个团队的某个程序猿出了问题(运行时),Leader就会跳出来变身成该程序猿的解说员去与Boss谈笑风生。
扯完了蛋,来看看动态代理的代码:
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
{
//权限判断等,忽略
//生成代理类
Class<?> cl = getProxyClass0(loader, intfs);
//调用构造器,生成实例
final Constructor<?> cons = cl.getConstructor(constructorParams);
final InvocationHandler ih = h;
if (!Modifier.isPublic(cl.getModifiers())) {
AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() {
cons.setAccessible(true);
return null;
}
});
}
return cons.newInstance(new Object[]{h});
} catch (IllegalAccessException|InstantiationException e) {
//各种异常处理
}
}
看方法名就能看出来,此方法是用来new代理实例的。
有三个参数:
1.ClassLoader,类加载器,用来加载这个类...算了其实我不懂,反正就是要这个参数拿去创建类的。
2.Class<?>[] interfaces,要继承的接口数组,这个我懂。
3.InvocationHandler,祈祷处理器......这个是代理类要完成的任务的具体实现。
Class<?> cl = getProxyClass0(loader, intfs);
这行代码创建了一个代理类,实现了传进去的接口。
return cons.newInstance(new Object[]{h});
这行代码返回上面创建的Class的新的实例,顺便把接收到的InvocationHandler传了进去。
跟Retrofit结合来看一下。还记得这句代码吧:
RestApi restApi = retrofit.create(RestApi.class);
我们传一个接口类进去,再看一遍Retrofit的create:
public <T> T create(final Class<T> service) {
Utils.validateServiceInterface(service);
if (validateEagerly) {
eagerlyValidateMethods(service);
}
return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[] { service },
new InvocationHandler() {
private final Platform platform = Platform.get();
@Override 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);
}
if (platform.isDefaultMethod(method)) {
return platform.invokeDefaultMethod(method, service, proxy, args);
}
ServiceMethod<Object, Object> serviceMethod =
(ServiceMethod<Object, Object>) loadServiceMethod(method);
OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);
return serviceMethod.callAdapter.adapt(okHttpCall);
}
});
}
它拿到了我们传进去的接口类的ClassLoader作为创建动态代理方法的第一个参数,新建一个Class数组承载我们传进去的接口,最后它自己写了一个InvocationHandler传进去。
最终,动态代理的newProxyInstance返回一个包含InvocationHandler,实现了Retrofit传进去接口的APIService接口类的代理类实例给Retrofit(自己看着都累)。Retrofit再把这个代理类实例返给我们。
OK,上面回顾的是传入一个接口类给Retrofit,Retrofit返回代理实例给我们的实现流程。这时候还剩一个磨人的小妖精,就是那个InvocationHandler究竟是干啥的。
要探究这个问题就得看看生成的动态代理类里面是什么样子的了。
Class<?> cl = getProxyClass0(loader, intfs);
这句代码就是用来生成代理类的,那么来看看getProxyClass这个方法里头干了啥。
private static Class<?> getProxyClass0(ClassLoader loader,
Class<?>... interfaces) {
//一个代理类最多能够实现65535个接口
if (interfaces.length > 65535) {
throw new IllegalArgumentException("interface limit exceeded");
}
// 这里Proxy做了一次缓存,如果之前生成过这个Classloader和interfaces的代理类,那么这里直接返回
//否则新生成类的字节码文件
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
proxyName, interfaces);
try {
//将字节码加载到JVM
proxyClass = defineClass0(loader, proxyName,
proxyClassFile, 0, proxyClassFile.length);
}
retrun proxyClass;
}
可以看到,最终是调用ProxyGenerator的generateProxyClass方法生成类的字节码并加载到JVM,再返回。
至于这个方法是怎么生成的,咱就不追下去了(因为参考的文章里没追...)。不过可以通过它生成的代理类反推它干了什么。
把生成的代理类的Class文件导出并反编译,可以看到,这个代理类继承自Proxy,实现了传进去的所有接口。它除了实现了Object基础的equals,toString和hashCode这几个方法外,还实现了我们接口定义的方法。
//以下代码纯手打,不一定准确
public final Call<Model> getResult() throws {
try {
return (Call)super.h.invoke(this, m3, new Object[]{"retrofit"});
} catch (RuntimeException | Error var4) {
throw var4;
} catch (Throwable var5) {
throw new UndeclaredThrowableException(var5);
}
}
其中,m3是通过反射拿到的Method对象。
private static Method m3;
m3 = Class.forName("proxy.GenerateClass$APIService").getMethod("getResult", new Class[] {});
我们拿到接口的动态代理实例后,直接调用动态代理中实现的我们的接口定义的getResult方法,此方法会把调用转发给父类(Proxy)的InvocationHandler的invoke方法。
Call<Model> result = apiService.getResult();
此时Retrofit的create方法中的InvocationHandler的invoke回调被调用。
@Override
public Object invoke(Object proxy, Method method, Object... args)
throws Throwable {
// 如果method是Object的基础方法,就正常执行方法
if (method.getDeclaringClass() == Object.class) {
return method.invoke(this, args);
}
//据说是Java8兼容,忽略
if (platform.isDefaultMethod(method)) {
return platform.invokeDefaultMethod(method, service, proxy, args);
}
ServiceMethod<Object, Object> serviceMethod =
(ServiceMethod<Object, Object>) loadServiceMethod(method);
OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);
return serviceMethod.callAdapter.adapt(okHttpCall);
}
}
现在来看invoke回调。重点在最后三行代码。
进到ServiceMethod类中,看见注释:
/** Adapts an invocation of an interface method into an HTTP call. */
翻译一下就是说:把一个接口方法的invocation翻译(适配)成一个HTTP 请求。可以直接理解为:ServiceMethod把我们定义在APIService接口类中的方法翻译成一个HTTP请求。具体就是翻译接口类方法的注释、参数,然后自动加上什么Header啦,body啦的什么鬼。
ServiceMethod<?, ?> loadServiceMethod(Method method) {
ServiceMethod<?, ?> result = serviceMethodCache.get(method);
if (result != null) return result;
synchronized (serviceMethodCache) {
result = serviceMethodCache.get(method);
if (result == null) {
result = new ServiceMethod.Builder<>(this, method).build();
serviceMethodCache.put(method, result);
}
}
return result;
}
从这个方法可以看出来,每一个method对应一个serviceMethod。
所以第一行代码就是获取到当前method对应的serviceMethod。
第二行代码
OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);
这里是Retrofit封装Call对象的OkHttpCall类。它的构造函数可以接受一个serviceMethod和参数。
第三行代码
return serviceMethod.callAdapter.adapt(okHttpCall);
不用说,肯定是把okHttpCall变成Call对象返回给我们。这里Retrofit留了拓展可能性。这里的callAdapter由CallAdapter.Factory生产,默认使用的是DefaultCallAdapterFactory。我们可以对其进行定制,自定义一个CallAdapter.Factory的子类,然后返回其他的类型。
到现在为止,终于把Retrofit的create方法走完了。再看一遍这个方法回忆一下整个流程吧。
public <T> T create(final Class<T> service) {
Utils.validateServiceInterface(service);
if (validateEagerly) {
eagerlyValidateMethods(service);
}
return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[] { service },
new InvocationHandler() {
private final Platform platform = Platform.get();
@Override 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);
}
if (platform.isDefaultMethod(method)) {
return platform.invokeDefaultMethod(method, service, proxy, args);
}
ServiceMethod<Object, Object> serviceMethod =
(ServiceMethod<Object, Object>) loadServiceMethod(method);
OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);
return serviceMethod.callAdapter.adapt(okHttpCall);
}
});
}
其实也没那么复杂...
再往回套一层,回到使用上面来,咱们已经调用了动态代理实例的方法,拿到了Call对象。往下就是enqueue执行了,这个是OkHttp的东西,在这里就不深究了。