Retrofit源碼調用流程分析

Retrofit的使用

我們以網上很流行的天氣預報的接口爲例,模擬一個Retrofit的使用例子,首先我們需要一個定義請求的接口類WeatherService.java:

public interface WeatherService {
    @GET("/Ecalender/api/v2/weather")
    Call<WeatherData> getWeather(@Query("date") String date, @Query("citykey") String citykey);

    @GET("/Ecalender/api/v2/weather")
    Observable<WeatherData> getWeatherObservable(@Query("date") String date, @Query("citykey") String citykey);

}

WeatherService.java例舉了兩種不同的返回方式,下面我們使用標準的Call<T>的返回方式舉例調用過程代碼,

private void getWeather() {
        retrofit = new Retrofit.Builder()
                .baseUrl("http://v.juhe.cn/calendar/day")
                .addConverterFactory(GsonConverterFactory.create(gson))
                .build(); 
        service = retrofit.create(WeatherService.class); 
        String nowDate = new SimpleDateFormate("YYYY-MM-DD").format(new Date());
        Call<WeatherData> result = service.getWeather(nowDate, "101010100");  

        try {
            Response<WeatherData> r = result.execute();  
            WeatherData data = r.body(); 
            Log.e("David", "ResponseBody data = " + data);
            if (data != null) {
                Log.e("David", "RxLoaderCallback data = " + data.weatherinfo.desc);
                Log.e("David", "RxLoaderCallback data = " + data.result.lunarYear);
                Log.e("David", "RxLoaderCallback data = " + data.result.weekday);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

Retrofit調用步驟:

  1. 通過Retrofit.Builder配置好各項參數,然後調用build()創建Retrofit對象。
  2. 將http接口文件WeatherService.class傳遞給Retrofit對象,然後創建WeatherService代理對象service
  3. 通過WeatherService代理service對象調用方法getWeather()得到Call<WeatherData>對象result
  4. 通過Call<WeatherData>對象resultexcute()方法執行http網絡請求,得到返回結果Response<Weather>對象r
  5. 通過Response<Weather>對象rbody()方法得到我們真正需要的WeatherData對象。

創建Retrofit

首先new了一個Retrofit.Builder對象,然後配置各種參數,這裏類似與setter的功能,其中參數baseUrl是必須要配置的,不然會報錯。最後調用Retrofit.Builderbuild()方法生成Retrofit對象,其內部也是使用了new Retrofit()的方式創建新對象。。


  public static final class Builder {
    ......

    public Retrofit build() {
      if (baseUrl == null) {
        throw new IllegalStateException("Base URL required.");
      }
      ......
      return new Retrofit(callFactory, baseUrl, unmodifiableList(converterFactories),
          unmodifiableList(callAdapterFactories), callbackExecutor, validateEagerly);
    }
  }

創建Http代理對象

得到Retrofit對象後,通過Retrofit對象的create()方法創建Http的代理對象,

public <T> T create(final Class<T> service) {
    Utils.validateServiceInterface(service);
    if (validateEagerly) {
      eagerlyValidateMethods(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);
            }
            if (platform.isDefaultMethod(method)) {
              return platform.invokeDefaultMethod(method, service, proxy, args);
            }
            return loadServiceMethod(method).invoke(args != null ? args : emptyArgs);
          }
        });
  }

通過代理對象得到Call對象

當我們通過代理對象發起請求的時候會進入上面代理對象的InvocationHandler對象的invoke()方法中,然後會進入Retrofit對象的loadServiceMethod()方法:

  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對象,如果有緩存,則直接返回之前緩存的ServiceMethod對象,否則調用ServiceMethod.parseAnnotations()創建新的ServiceMethod對象,並放入緩存。我們看看ServiceMethod是怎樣創建新的對象的:


  static <T> ServiceMethod<T> parseAnnotations(Retrofit retrofit, Method method) {
    RequestFactory requestFactory = RequestFactory.parseAnnotations(retrofit, method);
    .......

    return HttpServiceMethod.parseAnnotations(retrofit, method, requestFactory);
  }

方法內部是通過HttpServiceMethod來創建的,我們在進入HttpServiceMethod

  static <ResponseT, ReturnT> HttpServiceMethod<ResponseT, ReturnT> parseAnnotations(
      Retrofit retrofit, Method method, RequestFactory requestFactory) {
    ........
    
    CallAdapter<ResponseT, ReturnT> callAdapter =
        createCallAdapter(retrofit, method, adapterType, annotations);
        
    ........
    okhttp3.Call.Factory callFactory = retrofit.callFactory;
    if (!isKotlinSuspendFunction) {
      return new CallAdapted<>(requestFactory, callFactory, responseConverter, callAdapter);
    } else if (continuationWantsResponse) {
      //noinspection unchecked Kotlin compiler guarantees ReturnT to be Object.
      return (HttpServiceMethod<ResponseT, ReturnT>) new SuspendForResponse<>(requestFactory,
          callFactory, responseConverter, (CallAdapter<ResponseT, Call<ResponseT>>) callAdapter);
    } else {
      //noinspection unchecked Kotlin compiler guarantees ReturnT to be Object.
      return (HttpServiceMethod<ResponseT, ReturnT>) new SuspendForBody<>(requestFactory,
          callFactory, responseConverter, (CallAdapter<ResponseT, Call<ResponseT>>) callAdapter,
          continuationBodyNullable);
    }
  }

根據我們上面的調用例子,沒有Kotlin支持,所以直接返回的就是HttpServiceMethod子類CallAdated對象,注意此時我們返回的HttpServiceMethod對象創建時傳入了okhttp3.Call.Factory對象,CallAdapter對象因爲沒有配置CallAdapter.Factory所以使用的默認,直接返回Call<T>,後面會使用到。
承接上面的代理對象的請求調用其實是調用了我們剛剛生成的HttpServiceMethod對象的invoke()方法,

  @Override final @Nullable ReturnT invoke(Object[] args) {
    Call<ResponseT> call = new OkHttpCall<>(requestFactory, args, callFactory, responseConverter);
    return adapt(call, args);
  }

HttpServiceMethod對象的invoke()方法中,新建了一個OkHttpCall對象,然後通過適配方法adapt轉換成需要的目標接口類型。根據我們上面的demo調用例子,也就是執行了CallAdated對象的adapt()

    @Override protected ReturnT adapt(Call<ResponseT> call, Object[] args) {
      return callAdapter.adapt(call);
    }

這裏的CallAdapter對象因爲沒有配置CallAdapter.Factory所以使用的默認,直接返回Call<T>,也就是上面第3步得到Call<WeatherData>對象result。其實返回的也是一個OkHttpCall對象。

通過OkHttpCall對象執行網絡請求

上面地4步,Call<WeatherData>對象resultexcute()也就是發起網絡請求,實際執行的是OkHttpCall對象的excute()方法

  @Override public Response<T> execute() throws IOException {
    okhttp3.Call call;

    synchronized (this) {
      .......

      call = rawCall;
      if (call == null) {
        try {
          call = rawCall = createRawCall();
        } catch (IOException | RuntimeException | Error e) {
          throwIfFatal(e); //  Do not assign a fatal error to creationFailure.
          creationFailure = e;
          throw e;
        }
      }
    }

    if (canceled) {
      call.cancel();
    }

    return parseResponse(call.execute());
  }

  private okhttp3.Call createRawCall() throws IOException {
    okhttp3.Call call = callFactory.newCall(requestFactory.create(args));
    if (call == null) {
      throw new NullPointerException("Call.Factory returned null.");
    }
    return call;
  }

裏面通過createRawCall()方法調用了OkHttpClientnewCall()創建了一個RealCall對象:

  @Override public Call newCall(Request request) {
    return RealCall.newRealCall(this, request, false /* for web socket */);
  }

然後OkHttpCall對象的excute()方法最後一行,先調用excute()方法執行網絡請求。再跟進去就是OkHttp的網絡請求細節了,所以不再跟進。執行完OkHttp的網絡請求後,胡調用parseResponse()方法對返回結果進行處理。

  Response<T> parseResponse(okhttp3.Response rawResponse) throws IOException {
    ResponseBody rawBody = rawResponse.body();

    ......

    ExceptionCatchingResponseBody catchingBody = new ExceptionCatchingResponseBody(rawBody);
    try {
      T body = responseConverter.convert(catchingBody);
      return Response.success(body, rawResponse);
    } catch (RuntimeException e) {
      .......
    }
  }

可以看到請求成功後,會將返回成功body轉換成目標類型之後放入Retrofit中的Responsebody。所以我們上面在第5步的時候是通過Response<Weather>對象rbody()方法得到我們真正需要的WeatherData對象。

由於個人水平可能有限,如果上述博文有不正確的地方,歡迎大家指正

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章