Retrofit의 구현 및 원리

7965 단어 android
Retrofit 사용
1. 우선interface 요청 집합을 만들고 설정해야 할 요청 방법을 주석으로 작성합니다
public interface Service {
@GET("/repos/{user}/repos")
Call> contributors(
    @Path("user") String user);
}

2. 그리고 Retrofit를 통해 위 인터페이스의 실현 클래스를 정의하는데 이른바 동적 에이전트이다
Retrofit retrofit = new Retrofit.Builder()
                    .baseUrl(API_URL)
                    .addConverterFactory(GsonConverterFactory.create())
                    .build();

// Create an instance of our Service API interface.
Service service = retrofit.create(Service.class);

3. 다음은 인터페이스 요청
Call> call = service.repos("GitHub");

원본 코드 해석
Retrofit2 원본은 다음과 같다. 주로 Retrofit.create(Class) 방법을 통해 서비스 인터페이스 실례를 만들어서 서비스에서 설정하는 방법을 사용할 수 있게 하는 것이 바로retrofit의 핵심이다.
@SuppressWarnings("unchecked") // Single-interface proxy creation guarded by parameter safety.
public  T create(final Class 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();

          @Override public 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);
            }
            //          
            ServiceMethod serviceMethod =
            (ServiceMethod) loadServiceMethod(method);
            OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args);
            return serviceMethod.callAdapter.adapt(okHttpCall);
          }
    });
}

방법 내부 구체화: Proxy를 사용합니다.newProxyInstance()를 사용하여 서비스 인스턴스를 만듭니다.이 방법은 매개 변수 중 여러 개의interface(Retrofit까지, 한 인터페이스에 고정적으로 전송되는 것)를 대상으로 대상을 만들 것이다. 이 대상은 모든interface를 실현하는 모든 방법을 실현하고 모든 방법은 대상 실례 내부의 하나InvocationHandler 구성원 변수invoke() 방법을 호출하여 자신의 방법 정보를 전달한다.이렇게 하면 실질적으로 에이전트 논리를 실현했다. 인터페이스의 방법은 모두 다른 설정InvocationHandler 대상에서 에이전트 조작, 즉 동적 에이전트를 한다.이러한 방법의 구체적인 실현은 실행할 때interface 실례를 생성해야만 확정된다.따라서 invoke() 방법의 논리는 바로 Retrofit가 서비스를 만드는 관건으로 세 단계를 완성하는 것이다.
1. method에 대응하는 ServiceMethod 실례를 불러오기(반환값 유형, 방법 주석, 파라미터 유형, 파라미터 주석 포함) 2. ServiceMethod에 포함된 정보를 사용하여 OkHttpCall 대상을 만듭니다.ServiceMethodretrofit2.call 대상에 봉하여 필요할 때 호출하고 ServiceMethod에 포함된 정보를 이용하여 okhttp3.call 대상을 만들고 이 okhttp3.call 대상을 호출하여 네트워크 요청을 합니다.3. ServiceMethod의callAdapter 대상을 사용하여 okHttpCall 대상을 변환하여 새로운retrofit를 생성한다.콜 대상, 이 새로운 콜 대상에서 백엔드 라인이 요청을 하면 해당하는 반환 후 주 라인에서 리셋 방법을 호출하여 라인의 전환을 실현합니다.
첫 번째 단계에서는 method에 해당하는 ServiceMethod를 로드합니다.loadServiceMethod() 방법으로 획득
ServiceMethod, ?> loadServiceMethod(Method method) {
//  serviceMethodCache    method       ,     ,    new
  ServiceMethod, ?> result = serviceMethodCache.get(method);
  if (result != null) return result;

  synchronized (serviceMethodCache) {
      result = serviceMethodCache.get(method);
      if (result == null) {
        result = new ServiceMethod.Builder<>(this, method).build();
        serviceMethodCache.put(method, result);
      }
   }
   return result;
}

서비스MethodCache에 캐시가 존재하지 않으면 ServiceMethod.Build.build() 방법으로 새로 만듭니다
public ServiceMethod build() {
  //  callAdapter
  callAdapter = createCallAdapter();
  //        ,         
  responseType = callAdapter.responseType();
  if (responseType == Response.class || responseType == okhttp3.Response.class) {
    throw methodError("'"
        + Utils.getRawType(responseType).getName()
        + "' is not a valid response body type. Did you mean ResponseBody?");
  }
  //  responseConverter,     
  responseConverter = createResponseConverter();
  //  ,    ,     GET、POST ,    url
  for (Annotation annotation : methodAnnotations) {
    parseMethodAnnotation(annotation);
  }
  //        、      
  if (httpMethod == null) {
    throw methodError("HTTP method annotation is required (e.g., @GET, @POST, etc.).");
  }

  if (!hasBody) {
    if (isMultipart) {
      throw methodError(
          "Multipart can only be specified on HTTP methods with request body (e.g., @POST).");
    }
    if (isFormEncoded) {
      throw methodError("FormUrlEncoded can only be specified on HTTP methods with "
          + "request body (e.g., @POST).");
    }
  }

  int parameterCount = parameterAnnotationsArray.length;
  parameterHandlers = new ParameterHandler>[parameterCount];
  for (int p = 0; p < parameterCount; p++) {
    Type parameterType = parameterTypes[p];
    if (Utils.hasUnresolvableType(parameterType)) {
      throw parameterError(p, "Parameter type must not include a type variable or wildcard: %s",
          parameterType);
    }

    Annotation[] parameterAnnotations = parameterAnnotationsArray[p];
    if (parameterAnnotations == null) {
      throw parameterError(p, "No Retrofit annotation found.");
    }

    parameterHandlers[p] = parseParameter(p, parameterType, parameterAnnotations);
  }

  if (relativeUrl == null && !gotUrl) {
    throw methodError("Missing either @%s URL or @Url parameter.", httpMethod);
  }
  if (!isFormEncoded && !isMultipart && !hasBody && gotBody) {
    throw methodError("Non-body HTTP method cannot contain @Body.");
  }
  if (isFormEncoded && !gotField) {
    throw methodError("Form-encoded method must contain at least one @Field.");
  }
  if (isMultipart && !gotPart) {
    throw methodError("Multipart method must contain at least one @Part.");
  }
  //    ServiceMethod
  return new ServiceMethod<>(this);
}

두 번째 단계에서는 method, 메모의 매개변수args가 OkHttpCall을 생성합니다.
OkHttpCall은 사실상 OkHttp3.call의 포장 클래스이고 OkHttpCall 내부에서 실제적으로 최종적으로 호출된 것은 serviceMethod.callFactory.newCall(request) 방법으로 만든 OkHttp3.call로 네트워크 요청을 실행합니다.
세 번째 단계, 서비스 Method를 호출합니다.callAdapter.adapt(okHttpCall)를 사용하여 method에서 정의한 반환을 생성합니다.
이 방법은 서비스 Method의callAdapter 대상을 사용하여 okHttpCall 대상을 줄줄이 변환하고 새 Retrofit 2.Call 대상, 이 새로운 Call 대상에서 백엔드 라인에서 요청한 것은 상응하는 반환 후 주 라인에서 리셋 방법으로 라인의 자동 전환을 실현합니다.여기서 Android 플랫폼 Retrofit2.build () 시 자동으로 주 라인handler를 사용하여 ExecutorCallAdapterFactory를 구성합니다. 호출 enqueue(Callback), 콜백 리셋은 주 라인에서 리셋됩니다.
static class Android extends Platform {
  @Override public Executor defaultCallbackExecutor() {
    return new MainThreadExecutor();
  }

  @Override CallAdapter.Factory defaultCallAdapterFactory(@Nullable Executor callbackExecutor) {
    if (callbackExecutor == null) throw new AssertionError();
      return new ExecutorCallAdapterFactory(callbackExecutor);
  }

  static class MainThreadExecutor implements Executor {
    private final Handler handler = new Handler(Looper.getMainLooper());

    @Override public void execute(Runnable r) {
      handler.post(r);
    }
  }
}

확장: T adapt(Call< R > call) 유형 변환이 가능하고 Retrofit의 확장 어댑터에 RxJavaCallAdapterFactory, Java8CallAdapterFactory, GuavaCallAdapterFactory가 제공되기 때문입니다.
RxJavaCallAdapterFactory의 경우 RxJavaCallAdapterFactory가 만든callAdapter는adapt를 실행할 때 OkHttpCall을 Rx의Observable로 포장하여 Observable가subscribe에 의해 실행될 때 http 요청을 실행합니다.
참조: Retrofit 원리 분석

좋은 웹페이지 즐겨찾기