标签:
前面基本的原理和流程已经弄清了.再研究下某些实现.
CallbackRunnable(异步模式时在子线程执行的部分)
abstract class CallbackRunnable<T> implements Runnable { private final Callback<T> callback; private final Executor callbackExecutor; private final ErrorHandler errorHandler; CallbackRunnable(Callback<T> callback, Executor callbackExecutor, ErrorHandler errorHandler) { this.callback = callback; this.callbackExecutor = callbackExecutor; this.errorHandler = errorHandler; } @SuppressWarnings("unchecked") @Override public final void run() { //在异步线程中实际执行的方法. try { final ResponseWrapper wrapper = obtainResponse(); callbackExecutor.execute(new Runnable() { @Override public void run() { callback.success((T) wrapper.responseBody, wrapper.response); //在主线程执行. } }); } catch (RetrofitError e) { Throwable cause = errorHandler.handleError(e); //这里的异常捕捉到了以后没有再次抛出,内部处理了. final RetrofitError handled = cause == e ? e : unexpectedError(e.getUrl(), cause); callbackExecutor.execute(new Runnable() { @Override public void run() { callback.failure(handled); //在主线程执行. } }); } } public abstract ResponseWrapper obtainResponse(); //交给子类实现--->其实就是执行的前面的invokeRequest(). }
ResponseWrapper封装了response和responseBody.因为方法的返回值只能是一个.所以用它包装了下.
final class ResponseWrapper { final Response response; final Object responseBody; ResponseWrapper(Response response, Object responseBody) { this.response = response; this.responseBody = responseBody; } }
Retrofit的Client是真正进行网络访问的逻辑.来看下具体的实现.
public interface Client { /** * Synchronously execute an HTTP represented by {@code request} and encapsulate all response data * into a {@link Response} instance. * <p> * Note: If the request has a body, its length and mime type will have already been added to the * header list as {@code Content-Length} and {@code Content-Type}, respectively. Do NOT alter * these values as they might have been set as a result of an application-level configuration. */ Response execute(Request request) throws IOException; //抽象方法,就是给定一个Request,执行完,返回一个Response. /** * Deferred means of obtaining a {@link Client}. For asynchronous requests this will always be * called on a background thread. */ interface Provider { /** Obtain an HTTP client. Called once for each request. */ Client get(); } }
OkClient
public class OkClient implements Client { private static OkHttpClient generateDefaultOkHttp() { OkHttpClient client = new OkHttpClient(); client.setConnectTimeout(Defaults.CONNECT_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS); client.setReadTimeout(Defaults.READ_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS); return client; } private final OkHttpClient client; public OkClient() { this(generateDefaultOkHttp()); } public OkClient(OkHttpClient client) { if (client == null) throw new NullPointerException("client == null"); this.client = client; } @Override public Response execute(Request request) throws IOException { return parseResponse(client.newCall(createRequest(request)).execute()); //client.newCall().execute() 真正利用okHttp进行网络访问的地方.可以看到是采用的Okhttp的同步的方式. } static com.squareup.okhttp.Request createRequest(Request request) { //把retrofit的Request中url,header等信息提取出来, 封装进okhttp的Request中. com.squareup.okhttp.Request.Builder builder = new com.squareup.okhttp.Request.Builder() .url(request.getUrl()) .method(request.getMethod(), createRequestBody(request.getBody())); List<Header> headers = request.getHeaders(); for (int i = 0, size = headers.size(); i < size; i++) { Header header = headers.get(i); String value = header.getValue(); if (value == null) value = ""; builder.addHeader(header.getName(), value); } return builder.build(); } static Response parseResponse(com.squareup.okhttp.Response response) { return new Response(response.request().urlString(), response.code(), response.message(), createHeaders(response.headers()), createResponseBody(response.body())); } private static RequestBody createRequestBody(final TypedOutput body) { if (body == null) { return null; } final MediaType mediaType = MediaType.parse(body.mimeType()); //解析Response的类型. return new RequestBody() { @Override public MediaType contentType() { return mediaType; } @Override public void writeTo(BufferedSink sink) throws IOException { body.writeTo(sink.outputStream()); } @Override public long contentLength() { return body.length(); } }; } private static TypedInput createResponseBody(final ResponseBody body) { if (body.contentLength() == 0) { return null; } return new TypedInput() { @Override public String mimeType() { MediaType mediaType = body.contentType(); return mediaType == null ? null : mediaType.toString(); } @Override public long length() { return body.contentLength(); } @Override public InputStream in() throws IOException { return body.byteStream(); } }; } private static List<Header> createHeaders(Headers headers) { int size = headers.size(); List<Header> headerList = new ArrayList<Header>(size); for (int i = 0; i < size; i++) { headerList.add(new Header(headers.name(i), headers.value(i))); } return headerList; } }
retrofit的大体流程.
用户自定义配置项的设置(如client,converter,拦截器等)--->解析接口的方法(如果曾经解析过就从缓存中获取),确定http访问的url,header,method等,确定是异步还是同步的方式------>使用具体的Client进行网络访问,并将数据封装到Response---->执行Converter的逻辑(有可能不用执行),把Response数据转换为一个具体对象.--->根据同步或者异步的方式,执行方法或者callBack的逻辑.
retrofit框架的需要注意的几个小点.
1.为什么同步方式不像正常的方式一样要求用户try_catch来提醒用户捕捉异常?
通过上面的逻辑可以看到,真正进行网络访问,converter转换的逻辑都在invokeHandler.invoke()方法执行的时候执行. 而这个方法的调用是在 用户自定义接口调用接口方法的时候执行的.(不明白的可以看下动态代理的原理).而用户自定义的接口方法是没有抛出异常的.在java中,如果父类方法没有抛出异常,子类方法也不能显示的抛出异常.(子类方法只能抛出父类方法抛出异常或其子类).所以Retrofit就不能抛出各种异常(如IO异常). 并且要抓住异常后转换为RuntimeException抛出.(动态代理生成的接口的实现类其实内部也采用了同样的方法.)
异常抓住后不能直接内部处理,应该提醒用户代码执行的时候出了问题,所以必须抓住异常后再次抛出.而对于CallBack的方式,因为有failure()方法提示用户代码逻辑出了问题,所以就不用re-throw异常了.
2.关于 InvocationHandler的invoke()方法, 这个方法有个返回值. 那这个返回值返回的是什么呢?
首先明确Method.invoke(Object receiver,Object.. args)是和 receiver.method(args)等价的,2个方法的返回值是一样的.
public Object invoke(Object receiver, Object... args) 这里的Object是方法执行的的返回值. ---> Returns the result of dynamically invoking this method. Equivalent to {@code receiver.methodName(arg1, arg2, ... , argN)}.
动态代理生成了接口A的代理类B,B的同名方法内部其实调用的是invocationHandler的 invoke()方法.返回的也是invoke方法的返回值. 所以invoke返回的类型就应该和接口方法的返回值类型一样.
标签:
原文地址:http://www.cnblogs.com/laiqurufeng/p/4498646.html