这是我参与「第四届青训营 」笔记创作活动的第8天
TTNet
TTNet是字节跳动通用的网络请求封装框架,用来向服务端发起请求。
目前Android端上的头条,抖音,番茄小说都在使用该库作为网络请求封装框架,作为字节内部的基础库,提供了一整套Android客户端网络请求解决方案。
- 它有哪些突出的优点:
-
- 基于Retrofit改造,具备了Retrofit所具有的优点
- 支持多个Http网络库的动态切换(okhttp和cronet)
- 支持网络拦截配置:添加公共参数,动态切换协议及Host,动态选路等
- 支持流解析,json序列化
为什么要做TTNet?
Retrofit作为网络封装框架,已经足够优秀了,那么我们为什么还要基于Retrofit改造做我们自己的TTNet网络封装框架呢?
Retrofit是开源的网络框架,我们只能被动依赖,无法主动修改以适应我们的业务发展。如:Retrofit只支持OkHttp网络库,不支持其他网络库。
那么,问题来了。我们有更优秀的网络库cronet,我们想使用它,但是我们又迷恋Retrofit的优秀的封装特性和接口调用的简易特性?
答案:打造自己维护的网络框架 TTNet !
TTNet与Retrofit的使用对比
Retrofit使用OkHttp的流程介绍
Retrofit的主流程
Retrofit调用底层OkHttp的方式
在主流程图中,okhttp网络库的api,在Retrofit中是何时发起请求的,对应api已列出,相同颜色即意味着调用关系。
如:
绿色代码:标记1处,创建Retrofit对象时,对应创建了OkHttpClient对象;
红色代码:标记4处,使用Retrofit的Call对象发起请求时,对应创建OkHttp网络库中Request对象和Call对象并发起请求。
Retrofit里的OkHttpClient的调用时机
public void getRequest() { Retrofit retrofit = new Retrofit.Builder() .baseUrl("www.bytedance.com/") //.addCallAdapterFactory(okhttp3.Call.Factory) .build() ; // 标记(1) }
// Retrofit$Builder public Retrofit build() { okhttp3.Call.Factory callFactory = this.callFactory; if (callFactory == null) { callFactory = new OkHttpClient() ; // 标记(2) } List<CallAdapter.Factory> adapterFactories = new ArrayList<>(this.adapterFactories); // platform为Android的实例 // 集合里的实例为ExecutorCallAdapterFactory的实例。这里要留意下 adapterFactories.add(platform.defaultCallAdapterFactory(callbackExecutor)); return new Retrofit(callFactory, baseUrl, converterFactories, adapterFactories, callbackExecutor, validateEagerly); }
static class Android extends Platform { @Override public Executor defaultCallbackExecutor() { return new MainThreadExecutor(); }
@Override CallAdapter.Factory defaultCallAdapterFactory(Executor callbackExecutor) { return new ExecutorCallAdapterFactory(callbackExecutor); } }
// Retrofit public final class Retrofit { private final okhttp3.Call.Factory callFactory; Retrofit(okhttp3.Call.Factory callFactory, HttpUrl baseUrl, List<Converter.Factory> converterFactories, List<CallAdapter.Factory> adapterFactories, Executor callbackExecutor, boolean validateEagerly) { this .callFactory = callFactory; // 标记(3) } }
OkHttpClient对象的创建,我们已经看到了。那么创建好之后,在何处完成调用呢? 我们往下走,答案就在下面的流程梳理里面。
不过,我们先留意下,adapterFactories集合里的对象。里面有一个默认的CallAdapter.Factory实例,它就是子类ExecutorCallAdapterFactory的实例。我们在后续解析请求方法时,会用到它。
OkHttpCall的创建
当我们通过代理对象调用我们的接口方法IUserNameService#getUserName时,会触发InvocationHandler#invoke方法
public void getRequest(){
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://www.bytedance.com/")
.build();
IUserNameService iUserNameService = retrofit.create(IUserNameService.class);
// 标记1
Call<ResponseBody> call = iUserNameService.getUserName( 1123 );
}
// Retrofit
public <T> T create(final Class<T> service) {
return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[] { service },
new InvocationHandler() {
private final Platform platform = Platform.get();
// 标记(2)
@Override public Object invoke(Object proxy, Method method, Object... args) {
ServiceMethod serviceMethod = loadServiceMethod(method);
OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args);
return serviceMethod.callAdapter.adapt(okHttpCall);
}
});
}
我们需要重点关注invoke方法中OkHttpCall对象的创建,serviceMethod.callAdapter.adapt(okHttpCall)中方法调用的实现,serviceMethod.callAdapter.adapt(okHttpCall)的返回值。一会儿会用到
okhttp.Request对象和okhttp.Call对象的创建及请求发起
public void getRequest(){
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://www.bytedance.com/")
.build();
IUserNameService iUserNameService = retrofit.create(IUserNameService.class);
// call对象的取值为InvocationHandler#invoke()方法的返回值,默认为ExecutorCallbackCall对象
Call<ResponseBody> call = iUserNameService.getUserName(1123);
// 标记1: 由请求物料的封装过程,我们知道 call为ExecutorCallbackCall的实例
call.enqueue(new Callback<ResponseBody>() {
@Override
public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
}
@Override
public void onFailure(Call<ResponseBody> call, Throwable t) {
}
});
}
// Retrofit
public <T> T create(final Class<T> service) {
return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[] { service },
new InvocationHandler() {
private final Platform platform = Platform.get();
// 标记(2)
@Override public Object invoke(Object proxy, Method method, Object... args) {
ServiceMethod serviceMethod = loadServiceMethod(method);
OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args);
return serviceMethod.callAdapter.adapt(okHttpCall);
}
});
}
// ExecutorCallbackCall
@Override
public void enqueue(final Callback<T> callback) {
// ExecutorCallbackCall实例所拥有的delegate为OkHttpCall对象。
delegate.enqueue(new Callback<T>() {
@Override
public void onResponse(Call<T> call, final Response<T> response) {
// ...
}
});
}
//OkHttpCall 这里的内容是不是似曾相识,没错它就是OkHttp完成请求的过程。我们上面已贴出
public void enqueue(final Callback<T> callback) {
// 这里完成的了Request的创建
Request request = serviceMethod.toRequest(args);
// 这里完成了Call对象的创建
okhttp3.Call call = serviceMethod.callFactory.newCall(request);
// 通过call对象发起异步请求
call.enqueue( new okhttp3.Callback() {
@Override
public void onResponse(okhttp3.Call call, okhttp3.Response rawResponse)
throws IOException {
Response<T> response = parseResponse(rawResponse);
callSuccess(response);
}
@Override
public void onFailure ( okhttp3.Call call, IOException e ) {
callback.onFailure(OkHttpCall.this, e);
}
});
}
// ServiceMethod
Request toRequest(Object... args) throws IOException {
RequestBuilder requestBuilder = new RequestBuilder(httpMethod, baseUrl, relativeUrl, headers,
contentType, hasBody, isFormEncoded, isMultipart);
ParameterHandler<Object>[] handlers = (ParameterHandler<Object>[]) parameterHandlers;
for (int p = 0; p < argumentCount; p++) {
handlers[p].apply(requestBuilder, args[p]);
}
return requestBuilder.build();
}
在OkHttpCall#enqueue方法里,通过ServiceMethod#toRequest方法,完成了Request的创建。因为ServiceMethod拥有接口方法IUserNameService#getUserName的全部请求物料信息,传入请求实参后,便可直接构造request对象。
构建完request对象后,serviceMethod.callFactory为OkHttpClient实例。通过OkHttpClient#newCall方法构建Call对象。
构建Call对象完成后,通过Call对象发起异步请求。
请求完毕后,返回响应对象Response。到此OkHttp请求调用过程完毕。
到这里Retrofit只支持OkHttp网络库就得到了解答:
通过代理对象对方法接口进行调用时,会在InvocationHandler#invoke方法回调里为serviceMethod.callAdapter.adapt()设置okHttpCall对象,进而发起请求时,委托OkHttpCall对象发起网络请求。而OkHttpCall#enqueue 调用okHttp网络库发起请求。
所以serviceMethod.callAdapter.adapt()设置死了为okHttpCall对象。Retrofit发起请求只能对OkHttp网络库进行支持了。
public <T> T create(final Class<T> service) {
return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[] { service },
new InvocationHandler() {
private final Platform platform = Platform.get();
// 标记(2)
@Override public Object invoke(Object proxy, Method method, Object... args) {
ServiceMethod serviceMethod = loadServiceMethod(method);
OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args);
return serviceMethod.callAdapter.adapt(okHttpCall);
}
});
}
所以我们要支持多个网络库的改造,那么就在**OkHttpCall** 的改造上做文章就好。
总结
- 关于命名的大多数规范核心在于考虑上下文
- 人们在阅读理解代码的时候也可以看成是计算机运行程序,好的命名能让人把关注点留在主流程上,清晰地理解程序的功能,避免频繁切换到分支细节,增加理解成本