关闭

retrofit的实现原理(三)

490人阅读 评论(0) 收藏 举报

前面基本的原理和流程已经弄清了.再研究下某些实现.

CallbackRunnable(异步模式时在子线程执行的部分)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
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.因为方法的返回值只能是一个.所以用它包装了下.

1
2
3
4
5
6
7
8
9
final class ResponseWrapper {
  final Response response;
  final Object responseBody;
 
  ResponseWrapper(Response response, Object responseBody) {
    this.response = response;
    this.responseBody = responseBody;
  }
}

Retrofit的Client是真正进行网络访问的逻辑.来看下具体的实现.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
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 == nullthrow 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个方法的返回值是一样的.

1
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返回的类型就应该和接口方法的返回值类型一样.

0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:91975次
    • 积分:1555
    • 等级:
    • 排名:千里之外
    • 原创:16篇
    • 转载:295篇
    • 译文:0篇
    • 评论:6条