Android open source framework: Retrofit

1 Brief description

A type-safe HTTP client for Android and Java.
Retrofit is a wrapper around a RESTful HTTP web request framework. Its bottom layer is still implemented based on OkHttp, which is an improvement of OkHttp used on Android.

// 这是一个例子
Retrofit retrofit = new Retrofit.Builder()
    .baseUrl("https://api.github.com/")
    .addConverterFactory(GsonConverterFactory.create())
    .build();

GitHubService service = retrofit.create(GitHubService.class);
Call<List<Repo>> repos = service.listRepos("halloayu");

repos.enqueue(new Callback<List<Repo>>() {
    @Override
    public void onResponse(Call<List<Repo>> call, Response<List<Repo>> response) {
        Log.e("Main", response.body().get(0).toString());
    }
    
    @Override
    public void onFailure(Call<List<Repo>> call, Throwable t) {
    }
});
复制代码

2 Why is there a Retrofit framework

OkHttp is a network request framework officially certified by Android. It is very powerful, but it also has many shortcomings, as follows.

  1. OkHtt is very tedious to configure network requests, especially when configuring complex network request bodies, request headers, and parameters.
  2. The data parsing process requires the user to manually obtain the ResponseBody for parsing, which is difficult to reuse.
  3. Thread switching cannot be done automatically.
  4. In case we have nested network requests, we will fall into "callback hell".

Therefore, in order to solve these shortcomings and make network requests easy to use in Android development, the Retrofit framework was born, which essentially puts a layer of "shell" on OkHttp, and the requests inside are still implemented based on OkHttp.
At the same time Retrofit is designed for two purposes.

  • before request
    • Network request headers can be configured uniformly
    • It is very convenient to reuse Request, or create a request
  • after request
    • The child thread needs to be switched to the UI thread to facilitate interface refresh
    • The returned data can be parsed as Java Bean, which is convenient to use

3 Main points of packaging

Retrofit encapsulates OkHttp mainly in the following points:

  1. The build mode creates basic configuration for network requests
  2. Combining HTTP network requests with annotated classes
  3. Provides the data Gsonreturned by the parsingjson
  4. Executor completes thread switching

The core of its request implementation lies in "annotation", "dynamic proxy", and "reflection".

4 Notes

Http has 8 network requests: GET, POST, PUT, DELETE, PATCH, HEAD, OPTIONS, HTTP

Retrofit implements these kinds of requests through a combination of more than 20 annotations.企业微信截图_bbd04297-5bd9-4f56-910b-8d60d495ecb3.png

5 Dynamic proxies

熟悉设计模式的都知道,代理模式的思想是通过一个“代理商”,让”用户“通过它处理难以处理的”事情“。
在最上面Retrofit的使用例子中,先是通过建造者模式创建了一个retrofit实例,然后走GitHubService service = retrofit.create(GitHubService.class),这里面就是一个动态代码,我们来具体看看做了什么。

public <T> T create(final Class<T> service) {
    // 验证接口
    validateServiceInterface(service);
    // 动态代理
    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 {
                // If the method is a method from Object then defer to normal invocation.
                if (method.getDeclaringClass() == Object.class) {
                    return method.invoke(this, args);
                }
                args = args != null ? args : emptyArgs;
                return platform.isDefaultMethod(method)
                    ? platform.invokeDefaultMethod(method, service, proxy, args)
                    : loadServiceMethod(method).invoke(args);
            }
        });
}
复制代码

从代码中我们可以看出,它代理了传入接口类中的所有接口方法,当我们调,service.listRepos("halloayu"),就会走InvocationHandler中的invoke方法。

// 从缓存中拿,没拿到,ServiceMethod进行处理
ServiceMethod<?> loadServiceMethod(Method method) {
    ServiceMethod<?> result = serviceMethodCache.get(method);
    if (result != null) return result;
    
    synchronized (serviceMethodCache) {
        result = serviceMethodCache.get(method);
        if (result == null) {
            result = ServiceMethod.parseAnnotations(this, method);
            serviceMethodCache.put(method, result);
        }
    }
    return result;
}
复制代码

ServiceMethod就像是一个中央处理器,传入Retrofit对象和Method对象,调用各个接口和解析器,最终生成一个Request,包含api 的域名、path、http请求方法、请求头、是否有body、是否是multipart等等。最后返回一个Call对象,Call接口的默认实现是OkHttpCall,它默认使用OkHttp3作为底层http请求client。

@Override
final @Nullable ReturnT invoke(Object[] args) {
    Call<ResponseT> call = new OkHttpCall<>(requestFactory, args, callFactory, responseConverter);
    // 使用适配器去转换返回数据
    return adapt(call, args);
}
复制代码

使用Java动态代理的目的就要拦截被调用的Java方法,然后解析这个Java方法的注解,最后生成Request由OkHttp发送。

// 异步请求源码
@Override
public void enqueue(final Callback<T> callback) {
    Objects.requireNonNull(callback, "callback == null");
    
    ...
    
    call.enqueue(
        new okhttp3.Callback() {
            @Override
            public void onResponse(okhttp3.Call call, okhttp3.Response rawResponse) {
                Response<T> response;
                try {
                    // 解析返回数据,里面用我们配置的GSON解析
                    response = parseResponse(rawResponse);
                } catch (Throwable e) {
                    throwIfFatal(e);
                    callFailure(e);
                    return;
                }
                
                try {
                    // 先走我们定义适配器转换数据,然后走自定义callback
                    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
                }
            }
        });
}
复制代码

6 线程切换

Retrofit实现子线程到主线程的切换也是基于Handler

static final class Android extends Platform {
    Android() {
        super(Build.VERSION.SDK_INT >= 24);
    }
    
    @Override
    public Executor defaultCallbackExecutor() {
        return new MainThreadExecutor();
    }
    
    @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);
    }
    
    static final class MainThreadExecutor implements Executor {
        // Handler
        private final Handler handler = new Handler(Looper.getMainLooper());
        
        @Override
        public void execute(Runnable r) {
            handler.post(r);
        }
    }
  }
复制代码

请求后,先走默认的线程切换适配器DefaultCallAdapterFactory,然后走自定义的callback。

@Override
public void enqueue(final Callback<T> callback) {
    Objects.requireNonNull(callback, "callback == null");
    
    // 切换线程
    delegate.enqueue(
        new Callback<T>() {
            @Override
            public void onResponse(Call<T> call, final Response<T> response) {
                callbackExecutor.execute(
                    () -> {
                        if (delegate.isCanceled()) {
                            // Emulate OkHttp's behavior of throwing/delivering an IOException on
                            // cancellation.
                            callback.onFailure(ExecutorCallbackCall.this, new IOException("Canceled"));
                        } else {
                            callback.onResponse(ExecutorCallbackCall.this, response);
                        }
                    });
            }
            
            @Override
            public void onFailure(Call<T> call, final Throwable t) {
                callbackExecutor.execute(() -> callback.onFailure(ExecutorCallbackCall.this, t));
            }
        });
}
复制代码

7 总结

Retrofit的源码十分优秀,用注解的形式组合HTTP请求,通过代理接口,处理请求的逻辑,最后再执行请求。里面也大量使用反射,工厂方法,抽象接口来实现低耦合高扩展,非常值得一读的开源框架!

Guess you like

Origin juejin.im/post/7080366577379442719