Retrofit学习笔记

注:以下内容为本人自己做(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 getResult();

  @GET("/search.json")
  Call 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 result = apiService.getResult();

//异步执行请求
result.enqueue(new Callback() {
  @override
  public void onResponse(Response 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 create(final Class 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 serviceMethod =
                (ServiceMethod) loadServiceMethod(method);
            OkHttpCall 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() {
                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 create(final Class 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 serviceMethod =
                (ServiceMethod) loadServiceMethod(method);
            OkHttpCall 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 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 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 serviceMethod =
                (ServiceMethod) loadServiceMethod(method);
  OkHttpCall 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 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 create(final Class 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 serviceMethod =
                (ServiceMethod) loadServiceMethod(method);
            OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args);
            return serviceMethod.callAdapter.adapt(okHttpCall);
          }
        });
  }
 
 

其实也没那么复杂...
再往回套一层,回到使用上面来,咱们已经调用了动态代理实例的方法,拿到了Call对象。往下就是enqueue执行了,这个是OkHttp的东西,在这里就不深究了。

你可能感兴趣的:(Retrofit学习笔记)