【摘要】 前言
为防止误会本文分析Retrofit2.9版本
retrofit github开源地址
官网tutorial
本文是记录博主在看Retrofit学习到的东西,如默认接口方法的调用,如何判断android平台还是java平台,Coroutine等.
预备知识
接口的默认方法
interface Test{ default public void te…
前言
为防止误会本文分析Retrofit2.9
版本
本文是记录博主在看Retrofit
学习到的东西,如默认接口方法的调用,如何判断android平台还是java平台,Coroutine
等.
预备知识
接口的默认方法
interface Test{ default public void test(){ System.out.println("hello"); } default public void test2(){ System.out.println("hello"); } }
默认情况接口函数都是一个声明,但是在jdk8
提供方法体的功能.
我们首先提前理解下Retrofit
一些类
//Platform.java
static final class Android extends Platform { //我们首先看下这个内部类.MainThreadExecutor用于将提交的任务放入主线程队列执行
static final class MainThreadExecutor implements Executor { private final Handler handler = new Handler(Looper.getMainLooper()); @Override public void execute(Runnable r) { handler.post(r); } } //构造函数 Android() { //Retrofit内部利用JDK8的一些特性, //但是Android7.0才开始支持.但是注意不是全部支持 //这里在本文的扩展章节讲解 super(Build.VERSION.SDK_INT >= 24); }
//返回MainThreadExecutor给Retrofit使用,用切换线程
//具体我们后面分析源码会看到 @Override public Executor defaultCallbackExecutor() { return new MainThreadExecutor(); } //调用接口的默认方法,注意Android仅有26以上才支持.虽然24支持了jdk8,但是不完全.
//关于这个函数将放在扩展章节讲解 @Nullable @Override Object invokeDefaultMethod( Method method, Class<?> declaringClass, Object object, Object... args) throws Throwable { if (Build.VERSION.SDK_INT < 26) { throw new UnsupportedOperationException( "Calling default methods on API 24 and 25 is not supported"); } return super.invokeDefaultMethod(method, declaringClass, object, args); } }
Retrofit
内部提供了一个Android
类,我们现在只需要理解它内部会返回一个可调度到android主线程
的Excutor
对象.
我们最后看下他的父类Platform
//Platform.java
class Platform {
//可见这个PLATFORM对象在初始化的时候就被赋值了
private static final Platform PLATFORM = findPlatform(); static Platform get() { return PLATFORM;
}
//判断是java平台还是android平台
//又学会一个知识点
private static Platform findPlatform() { return "Dalvik".equals(System.getProperty("java.vm.name")) ? new Android() // : new Platform(true);
}
//当前平台是否支持jdk8
private final boolean hasJava8Types;
//扩展章节具体讲解这里可以先忽略
private final @Nullable Constructor<Lookup> lookupConstructor;
//这个函数涉及很多方面的知识,这里可以先不追究细节,扩展章节具体讲解
Platform(boolean hasJava8Types) { this.hasJava8Types = hasJava8Types;
//看不懂没关系扩展章节简单提及 Constructor<Lookup> lookupConstructor = null; if (hasJava8Types) { try { // 因为声明的服务接口可能不是public,所以Retrofit使用了Lookup去查找.这里涉及一个 //MethodHandle的知识,扩展知识点简单提及,这里可以理解为这个类的存在仅仅为了处理默认接口的默认方法 lookupConstructor = Lookup.class.getDeclaredConstructor(Class.class, int.class); lookupConstructor.setAccessible(true); } catch (NoClassDefFoundError ignored) { //在API 24 (android7.0)或者API 25 (android 8.0)不存在这个类.所以调用接口的默认方法时 //Retrofit的将无法处理 } catch (NoSuchMethodException ignored) { //这个异常这博主无法深刻理解,不过对于Android同学可以忽略,貌似是JDK14的一个bug? // Assume JDK 14+ which contains a fix that allows a regular lookup to succeed. // See https://bugs.openjdk.java.net/browse/JDK-8209005. } } this.lookupConstructor = lookupConstructor;
}
//返回一个线程调度器,android返回一个会在主线程调度的对象
@Nullable
Executor defaultCallbackExecutor() { return null;
} List<? extends CallAdapter.Factory> defaultCallAdapterFactories( @Nullable Executor callbackExecutor) { DefaultCallAdapterFactory executorFactory = new DefaultCallAdapterFactory(callbackExecutor); return hasJava8Types ? asList(CompletableFutureCallAdapterFactory.INSTANCE, executorFactory) : singletonList(executorFactory);
} int defaultCallAdapterFactoriesSize() { return hasJava8Types ? 2 : 1;
} List<? extends Converter.Factory> defaultConverterFactories() { return hasJava8Types ? singletonList(OptionalConverterFactory.INSTANCE) : emptyList();
} int defaultConverterFactoriesSize() { return hasJava8Types ? 1 : 0;
}
//判断当前方法是不是默认接口方法,
//method.isDefault();是JDK8提供的,也就是要求API 24以上
@IgnoreJRERequirement // Only called on API 24+.
boolean isDefaultMethod(Method method) { return hasJava8Types && method.isDefault();
} //调用接口的默认方法,这个仅能在android api 26+才能成功调用
//扩展章节在简单提及,这里可以不看细节
@IgnoreJRERequirement // Only called on API 26+.
@Nullable
Object invokeDefaultMethod(Method method, Class<?> declaringClass, Object object, Object... args) throws Throwable { Lookup lookup = lookupConstructor != null ? lookupConstructor.newInstance(declaringClass, -1 /* trusted */) : MethodHandles.lookup(); return lookup.unreflectSpecial(method, declaringClass).bindTo(object).invokeWithArguments(args);
} }
简单的源码分析
我们看一个Retrofit
官方第一个示例程序(可以从github自行clone下载完整示例程序.)
val API_URL = "https://api.github.com" class Contributor(val login: String, val contributions: Int) interface GitHub { @GET("/repos/{owner}/{repo}/contributors") fun contributors( @Path("owner") owner: String?, @Path("repo") repo: String? ): Call<List<Contributor>>
} fun onclick() { val retrofit: Retrofit = Retrofit.Builder() .baseUrl(API_URL) .addConverterFactory(GsonConverterFactory.create()) .build() //一个动态代理生成的接口实现 val github = retrofit.create(GitHub::class.java) //返回OkHttpCall对象 val call: Call<List<Contributor>> = github.contributors("square", "retrofit") //OkHttpCall call.enqueue(object:Callback<List<Contributor>>{ override fun onResponse( call: Call<List<Contributor>>, response: Response<List<Contributor>> ) { //回调主线程哦 log("onResponse ${response}") } override fun onFailure(call: Call<List<Contributor>>, t: Throwable) { //回调主线程哦 log("onFailure") } })
} fun log(msg: String){ Log.e("MainActivity", "${msg}")
}
为防止混乱我们首先从构造Retrofit
的源码开始分析
val retrofit: Retrofit = Retrofit.Builder() .baseUrl(API_URL) .addConverterFactory(GsonConverterFactory.create()) .build()
Retrofit.Builder()
//Retrofit.java
//利用Builder去构造一个Retrofit.这里就是建造者模式
public static final class Builder { private final Platform platform; Builder(Platform platform) { this.platform = platform; } public Builder() { this(Platform.get()); }
}
Retrofit.baseUrl()
//Retrofit.java
public static final class Builder { public Builder baseUrl(String baseUrl) { //非空检查 Objects.requireNonNull(baseUrl, "baseUrl == null"); //HttpUrl是OKhttp3提供了一个封装类 return baseUrl(HttpUrl.get(baseUrl)); } //存储url字符串的封装类 private @Nullable HttpUrl baseUrl; public Builder baseUrl(HttpUrl baseUrl) { //非空检查 Objects.requireNonNull(baseUrl, "baseUrl == null"); this.baseUrl = baseUrl; return this; }
}
addConverterFactory(GsonConverterFactory.create())
//Retrofit.java
public static final class Builder { //存储所有转化器的大小,转化器的作用是retrofit基础内容不再这讲解 private final List<Converter.Factory> converterFactories = new ArrayList<>(); public Builder addConverterFactory(Converter.Factory factory) { converterFactories.add(factory); return this; }
}
build()
//Retrofit.java
public static final class Builder { public Retrofit build() { //因为这里我们没有设置callFactory.所以callFactory == null 为true //关于okhttp3的内容读者如果不熟悉可以参考其他文献 okhttp3.Call.Factory callFactory = this.callFactory; if (callFactory == null) { callFactory = new OkHttpClient(); } //构造时没有设置,所以这里直接用Android平台的主线程, Executor callbackExecutor = this.callbackExecutor; if (callbackExecutor == null) { //platform为android.执行时默认在主线程 callbackExecutor = platform.defaultCallbackExecutor(); } // Make a defensive copy of the adapters and add the default Call adapter. List<CallAdapter.Factory> callAdapterFactories = new ArrayList<>(this.callAdapterFactories); callAdapterFactories.addAll(platform.defaultCallAdapterFactories(callbackExecutor)); // Make a defensive copy of the converters. List<Converter.Factory> converterFactories = new ArrayList<>( 1 + this.converterFactories.size() + platform.defaultConverterFactoriesSize()); // Add the built-in converter factory first. This prevents overriding its behavior but also // ensures correct behavior when using converters that consume all types. converterFactories.add(new BuiltInConverters()); converterFactories.addAll(this.converterFactories); converterFactories.addAll(platform.defaultConverterFactories()); return new Retrofit( callFactory, baseUrl, unmodifiableList(converterFactories), unmodifiableList(callAdapterFactories), callbackExecutor, validateEagerly); }
}
上面便构造了一个Retrofit
对象返回,这里先不深究细节代码.
我们分析如下代码的生成
//一个动态代理生成的接口实现
val github = retrofit.create(GitHub::class.java)
//Retrofit.java
public final class Retrofit { public <T> T create(final Class<T> service) { //校验接口是否有效,已经是否有权限访问 //如果接口非public利用构造时的loockup去寻找 validateServiceInterface(service); //retrofit核心实现代码,利用动态代理创建一个接口的实例 return (T) Proxy.newProxyInstance( service.getClassLoader(), new Class<?>[] {service}, new InvocationHandler() { private final Platform platform = Platform.get(); private final Object[] emptyArgs = new Object[0]; @Override public @Nullable Object invoke(Object proxy, Method method, @Nullable Object[] args) throws Throwable { // 如果代理的method是来自object的方法那么直接调用 if (method.getDeclaringClass() == Object.class) { return method.invoke(this, args); } args = args != null ? args : emptyArgs; //代理的核心逻辑,platform是android //java8 推出了接口函数可以存在默认的实现 //isDefaultMethod仅在api 24以上,如果是默认实现方法,直接调用即可 //我们这里直接看loadServiceMethod函数即可 //loadServiceMethod返回一个CallAdapted对象 return platform.isDefaultMethod(method) ? platform.invokeDefaultMethod(method, service, proxy, args) : loadServiceMethod(method).invoke(args); } });
}
//返回CallAdapted
ServiceMethod<?> loadServiceMethod(Method method) { //初始化时比如为空 ServiceMethod<?> result = serviceMethodCache.get(method); if (result != null) return result; synchronized (serviceMethodCache) { //类似dcl的思想 //loadServiceMethod可能会被多线程环境调用,从而多次创建ServiceMethod对象 result = serviceMethodCache.get(method); //第一次默认是null if (result == null) { //ServiceMethod对象包含对这个方法的引用和方法注解的解析后的结果 //ServiceMethod可以方便我们下次快速调用 result = ServiceMethod.parseAnnotations(this, method); serviceMethodCache.put(method, result); } } //CallAdapted 继承自HttpServiceMethod对象 return result;
}
}
retrofit.create
相关函数实现采用java
动态代理实现,这里有些细节需要特别指出.接口有可能存在默认方法,所以retrofit
需要判断.如果是接口的默认方法直接调用即可.
比如下面的代码:
interface Test{ default public void GerUserInfo(){ System.out.println("hello"); } public void GetUserInfo2(); }
retrofit
会代理到GerUserInfo,GetUserInfo2
函数,但是存在默认方法GerUserInfo
也会被动态代理所调用.因此要区分彼此,默认方法按正常调用即可.(当然这种写法不推荐在Retrofit
使用).
我们这里还需要记住一点,动态代理会将方法转发到CallAdapted.invoke
.
我们看下下面的代码:
//返回OkHttpCall对象 val call: Call<List<Contributor>> = github.contributors("square", "retrofit")
github是动态代理生成的对象
调用函数后会到CallAdapted.invoke
上.
//HttpServiceMethod.java
class abstract class HttpServiceMethod<ResponseT, ReturnT> extends ServiceMethod<ReturnT>{ //内部包含了一个http请求的相关信息.如请求体,和那个method对象
private final RequestFactory requestFactory;
//okhttp3 对象
private final okhttp3.Call.Factory callFactory;
//转化器,用于转化返回的请求体
private final Converter<ResponseBody, ResponseT> responseConverter; final ReturnT invoke(Object[] args) { //这个OkHttpCall包含我们所需的信息 Call<ResponseT> call = new OkHttpCall<>(requestFactory, args, callFactory, responseConverter); //adapt函数可以先无视,adapt函数在本例中也会直接返回call,并没有做特殊处理 return adapt(call, args);
}
}
我们不需要记住太多记得返回的OkHttpCall
对象即可.
我们看最后打网络请求
call.enqueue(object:Callback<List<Contributor>>{ override fun onResponse( call: Call<List<Contributor>>, response: Response<List<Contributor>> ) { //回调主线程哦 log("onResponse ${response}") } override fun onFailure(call: Call<List<Contributor>>, t: Throwable) { //回调主线程哦 log("onFailure") } })
call.enqueue
会回调到OkHttpCall.enqueue
//OkHttpCall.java
final class OkHttpCall<T> implements Call<T> {
public void enqueue(final Callback<T> callback) { okhttp3.Call call; Throwable failure; synchronized (this) { executed = true; call = rawCall; failure = creationFailure; if (call == null && failure == null) { try { //通过createRawCall本质我们看到retrofit,本质是封装了okhttp3 //createRawCall内部封装了请求体请求头等信息,这里我们首先不需要深究 call = rawCall = createRawCall(); } catch (Throwable t) { throwIfFatal(t); failure = creationFailure = t; } } } //失败直接回调 if (failure != null) { callback.onFailure(this, failure); return; } //判断是否被取消 //canceled变量必须被volatile修饰,不然存在可见性问题 if (canceled) { call.cancel(); } // okhttp3.Call //异步调用okhttp进行网络请求 call.enqueue( new okhttp3.Callback() { @Override public void onResponse(okhttp3.Call call, okhttp3.Response rawResponse) { Response<T> response; try { //内部会调用转化器哦 response = parseResponse(rawResponse); } catch (Throwable e) { throwIfFatal(e); callFailure(e); return; } try { //切换线程后回调,切换线程调用的对象是`Android extends Platform` callback.onResponse(OkHttpCall.this, response); } catch (Throwable t) { throwIfFatal(t); t.printStackTrace(); // TODO this is not great } } @Override public void onFailure(okhttp3.Call call, IOException e) { callFailure(e); } private void callFailure(Throwable e) { try { callback.onFailure(OkHttpCall.this, e); } catch (Throwable t) { throwIfFatal(t); t.printStackTrace(); // TODO this is not great } } });
}
}
上面大概便是比较核心的调用流程.比较简单不过为了文章的完整性贴出大致调用流程
协程相关源码
这部代码可能比较跳,另外需要读者了解协程和泛型Type基础知识.
泛型擦除
Kotlin协程系列目录索引
interface GitHub { suspend fun contributors( ): List<String>
}
请问上面的代码给你,你如何判断contributors
是否不是协程方法?
这里需要涉及两个小知识,协程函数编译后的样子,和泛型Type
的使用.
上面是一个简单协程函数.编译后会变成如下代码
interface GitHub { fun contributors(con: Continuation<List<String>>)
}
于是乎我们通过参数即可判断,这里给出Retrofit
的判断方式.
fun main() { val declaredField: Method = GitHub::class.java.getDeclaredMethod("contributors", Continuation::class.java) //这里会返回所有的参数的type,由于协程编译后会多一个Continuation<List<String>>参数 //所以数组必然不为0 val genericParameterTypes: Array<Type> = declaredField.getGenericParameterTypes() //由于第一个参数是Continuation<List<String>>,并且带有泛型 //所以必然是ParameterizedType类型 val continuationType = genericParameterTypes[0] as ParameterizedType //回去rawType方法返回声明泛型所在的类,所以这里返回Continuation的class对象 val rawType:Type = continuationType.rawType //Class也实现了Type接口哦,所以这里会相等 val continuationClass = Continuation::class.java; //输出true println(rawType == continuationClass)
}
回到Retrofit
的协程源码分析流程,修改为协程调用方式
interface GitHub { @GET("/repos/{owner}/{repo}/contributors") suspend fun contributors( @Path("owner") owner: String?, @Path("repo") repo: String? ): List<Contributor>
}
GlobalScope.launch { //返回OkHttpCall对象 val call: List<Contributor> = github.contributors("square", "retrofit") println("MainActivity.onclick")
}
调用github.contributors
会触发动态代理函数,
//Retrofit.java
Proxy.newProxyInstance( service.getClassLoader(), new Class<?>[] {service}, new InvocationHandler() { private final Platform platform = Platform.get(); private final Object[] emptyArgs = new Object[0]; @Override public @Nullable Object invoke(Object proxy, Method method, @Nullable Object[] args)throws Throwable { if (method.getDeclaringClass() == Object.class) { return method.invoke(this, args); } args = args != null ? args : emptyArgs; //我们首先分析loadServiceMethod在分析invoke return loadServiceMethod(method).invoke(args); } });
我们首先分析loadServiceMethod在协程类会返回什么对象
//Retrifit.java
ServiceMethod<?> loadServiceMethod(Method method) { ServiceMethod<?> result = serviceMethodCache.get(method); if (result != null) return result;
//类DCL单例的写法,这里不铺开将 synchronized (serviceMethodCache) { result = serviceMethodCache.get(method); //必然为空 if (result == null) { //这里返回一个SuspendForBody对象 //内部会判断是否是协程函数,如果是返回SuspendForBody,注意继承自HttpServiceMethod //如果不是返回CallAdapted result = ServiceMethod.parseAnnotations(this, method); serviceMethodCache.put(method, result); } } return result;
}
我们最后看下SuspendForBody.invoke
函数
//HttpServiceMethod.java
class HttpServiceMethod{
final @Nullable ReturnT invoke(Object[] args) { Call<ResponseT> call = new OkHttpCall<>(requestFactory, args, callFactory, responseConverter); return adapt(call, args);
} @Override protected Object adapt(Call<ResponseT> call, Object[] args) { call = callAdapter.adapt(call); //协程函数会在编译后在最后一个参数传入一个continuation对象 Continuation<ResponseT> continuation = (Continuation<ResponseT>) args[args.length - 1]; try { //isNullable是否可空,这里一般为为false //我们分析KotlinExtensions.await即可 return isNullable ? KotlinExtensions.awaitNullable(call, continuation) : KotlinExtensions.await(call, continuation); } catch (Exception e) { return KotlinExtensions.suspendAndThrow(e, continuation); } }
}
//KotlinExtensions.kt
suspend fun <T : Any> Call<T>.await(): T {
//suspendCancellableCoroutine闭包可以获取当前协程的continuation
//在内部continuation的resume即可回调协程
return suspendCancellableCoroutine { continuation -> //协程取消时候会收到一个回调 continuation.invokeOnCancellation { //调用cancel函数 cancel() } //enqueue上文已经简单提及,就是交给okhttp请求任务 enqueue(object : Callback<T> { override fun onResponse(call: Call<T>, response: Response<T>) { //回调后返回协程 if (response.isSuccessful) { val body = response.body() if (body == null) { val invocation = call.request().tag(Invocation::class.java)!! val method = invocation.method() val e = KotlinNullPointerException("Response from " + method.declaringClass.name + '.' + method.name + " was null but response body type was declared as non-null") continuation.resumeWithException(e) } else { continuation.resume(body) } } else { continuation.resumeWithException(HttpException(response)) } } override fun onFailure(call: Call<T>, t: Throwable) { continuation.resumeWithException(t) } })
}
}
整体看Retrofit
没有太多难的,实现都非常优雅.
扩展
其实Retrofit
最让我敬佩的是第一个用动态代理实现优雅的网络请求,以及对于各种兼容性的处理.
在看Retrofit源码中一直让我疑惑的是默认方法为什么要用方法句柄
interface MyInterface { @GET("/repos/{owner}/{repo}/contributors") Call<String> contributors( @Path("owner") String owner, @Path("repo") String repo ); default void defaultFun() { System.out.println("MyInterface.defaultFun"); }
}
public class MainTest { public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, InterruptedException { MyInterface myInterface = (MyInterface) Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), new Class[]{MyInterface.class}, new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //contributors函数就调用okhttp然后返回 // if(是contributors函数){ // return 略 // } //如果调用defaultFun函数怎么处理 //第一反应是这样 //很不幸你讲引起死递归 return method.invoke(proxy, args); } }); myInterface.contributors("",""); }
}
MyInterface
函数有一个默认方法contributors
(当然这里就是有非主流的大佬就要写,作为框架兼容了这种写法).
当我们代理调用时method.invoke(proxy, args);
出现无限递归情况.
为什么出现无限递归?我们看下生产的动态代理类(博主用HSDB导出)
final class $Proxy0 extends Proxy implements MyInterface { private static Method m4; //略 public final void defaultFun() { super.h.invoke(this, m4, (Object[])null); } public $Proxy0(InvocationHandler var1) { super(var1); }
public final Call contributors(String var1, String var2) { return (Call)super.h.invoke(this, m3, new Object[]{var1, var2}); } static { m3 = Class.forName("main.MyInterface").getMethod("contributors", Class.forName("java.lang.String"), Class.forName("java.lang.String")); m4 = Class.forName("main.MyInterface").getMethod("defaultFun"); } }
我们的proxy
便是上面的类,当我们myInterface.contributors("","");
时调用 super.h.invoke(this, m4, (Object[])null);
,
而super.h.invoke(this, m4, (Object[])null);
会回调我们自定义的InvocationHandler
类.
既然知道问题了那么证明解决?通过方法句柄(MethodHandle
)可以解决
Java8 dynamic proxy and default methods
文章来源: blog.csdn.net,作者:不会写代码的丝丽,版权归原作者所有,如需转载,请联系作者。
原文链接:blog.csdn.net/qfanmingyiq/article/details/115797499