Android框架——Retrofit网络框架

介绍

Retrofit是一款针对Android网络请求的框架,它的底层是OKHttp实现的,可以用注解来定义请求的格式。

基本用法

引入依赖

    implementation 'com.squareup.retrofit2:retrofit:2.3.0'//导入retrofit
    //支持json类型的格式转换成model
	implementation 'com.squareup.retrofit2:converter-gson:2.3.0'

Get请求方式

首先我们写一个使用Get方式请求的网络接口。

public interface DemoService {

    @GET("home?name=prozac")
    Call<DemoModel> getAModel();
}

@GET 定义了此请求的方式为Get,参数为访问的地址。返回一个Call类型的参数。接下来创建一下Retrofit,并调用下这个接口。

		//创建Retrofit对象,设置url,数据转换器
		Retrofit retrofit = new Retrofit.Builder()
                .baseUrl("http://www.demo.com/")
                .addConverterFactory(GsonConverterFactory.create())
                .build();
        //通过retrofit.create创建出之前定义的接口对象
        DemoService demoService = retrofit.create(DemoService.class);
        //获得call对象,调用enqueue进行异步网络请求,传入回调对象操作回调
        Call<DemoModel> call = demoService.getDemoModel();
        call.enqueue(new Callback<DemoModel>() {
            @Override
            public void onResponse(Call<DemoModel> call, Response<DemoModel> response) {
            	//调用response对象的body参数就可以获得自动转换的泛型中类型对象了
                DemoModel model = response.body();
            }

            @Override
            public void onFailure(Call<DemoModel> call, Throwable t) {

            }
        });

使用@Path注解来动态配置url地址。

	//传进来的String参数path会取代{path}的位置
    @GET("{path}/home?name=prozac")
    Call<DemoModel> getDemoModel(@Path("path") String path);

使用@Query注解来动态配置查询参数。

	//name参数会在Get请求的url后面自动拼接
    @GET("home")
    Call<DemoModel> getDemoModel(@Query("name")String name);

使用@QueryMap注解动态配置查询参数数组。

	//自动遍历map中的数据并拼接
    @GET("home")
    Call<DemoModel> getDemoModel(@QueryMap Map<String,String> map);

Post请求方式

post请求方式和get请求方式相比需要更改几个注解的名称,改造如下:

	//注明表单请求,post请求时必须加上
    @FormUrlEncoded
    //采用Post注解
    @POST("home")
    //动态参数时采用的注解为@Field而不是@Query
    Call<DemoModel> getDemoModel(@Field("name") String name);

使用@Body注解传输Json格式数据。

	//Retrofit会自动将javaBean对象转换成json进行传输
	@FormUrlEncoded
    @POST("home")
    Call<DemoModel> getDemoModel(@Body User user);

添加消息报头

有时候我们请求网络的时候需要在消息报头里添加一些额外的信息,用Retrofit可以静态或动态的添加这些信息。
使用@Headers注解静态添加头部信息。

	@FormUrlEncoded
    @POST("home")
    //这里可以添加多条头部信息,如果只需要添加一条可以去掉大括号
    @Headers({
            "Accept-Encoding:application/json",
            "User-Agent:Retrofit"
    })
    Call<DemoModel> getDemoModel(@Body User user);

使用@Header注册动态添加头部信息

    @FormUrlEncoded
    @POST("home")
    Call<DemoModel> getDemoModel(@Header("Accept-Encoding")String encoding);

源码解析

接下来看看Retrofit源码里是怎样做的。

创建Retrofit

Retrofit通过它的Builder进行创建,我们从Builder方法开始看起。

public Builder() {
      this(Platform.get());
}
  private static final Platform PLATFORM = findPlatform();
  static Platform get() {
    return PLATFORM;
  }
//可以看到这个方法里根据当前执行的平台,返回了相应的对象。
private static Platform findPlatform() {
    try {
      Class.forName("android.os.Build");
      if (Build.VERSION.SDK_INT != 0) {
        return new Android();
      }
    } catch (ClassNotFoundException ignored) {
    }
    try {
      Class.forName("java.util.Optional");
      return new Java8();
    } catch (ClassNotFoundException ignored) {
    }
    return new Platform();
  }
    

下面看看返回Retrofit对象的Build()方法。

public Retrofit build() {
	  //baseUrl不能为空
      if (baseUrl == null) {
        throw new IllegalStateException("Base URL required.");
      }
      //创建一个OkHttpClient对象
      okhttp3.Call.Factory callFactory = this.callFactory;
      if (callFactory == null) {
        callFactory = new OkHttpClient();
      }
      //将回调传回主线程的对象
      Executor callbackExecutor = this.callbackExecutor;
      if (callbackExecutor == null) {
        callbackExecutor = platform.defaultCallbackExecutor();
      }

      // Make a defensive copy of the adapters and add the default Call adapter.
      List<CallAdapter.Factory> adapterFactories = new ArrayList<>(this.adapterFactories);
      adapterFactories.add(platform.defaultCallAdapterFactory(callbackExecutor));

      // Make a defensive copy of the converters.
      // 对象转换器
      List<Converter.Factory> converterFactories = new ArrayList<>(this.converterFactories);

      return new Retrofit(callFactory, baseUrl, converterFactories, adapterFactories,
          callbackExecutor, validateEagerly);
    }

这个方法设置了Retrofit的参数,返回了一个配置好的Retrofit对象。

Call的创建过程

先来看下我们的接口对象是怎么实例化的,也就是retrofit.create()方法。

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();
          //当我们调用接口对象里的方法时,会调用到这个函数
          //三个参数是代理对象,要调用的方法,方法参数
          @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<Object, Object> serviceMethod =
                (ServiceMethod<Object, Object>) loadServiceMethod(method);
            OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);
            return serviceMethod.callAdapter.adapt(okHttpCall);
          }
        });
  }

跟进一下loadServiceMethod,看看对method对象做了什么操作。

ServiceMethod<?, ?> loadServiceMethod(Method method) {
	//检查缓存
    ServiceMethod<?, ?> result = serviceMethodCache.get(method);
    if (result != null) return result;
    
    synchronized (serviceMethodCache) {
      result = serviceMethodCache.get(method);
      if (result == null) {
      	//通过Builder.build()创建Call对象,跟进
        result = new ServiceMethod.Builder<>(this, method).build();
        serviceMethodCache.put(method, result);
      }
    }
    return result;
  }

这里检测如果没有生成方法的缓存,则会通过ServiceMethod.Builder去创建一个新的对象。

public ServiceMethod build() {

	  //这个方法会获取我们之前设置的callAdapter的get方法,如果没设置系统默认设置了一个,为DefaultCallAdapterFactory.get(),代码放下方
      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 = createResponseConverter();
      //对方法的注解进行解析
      for (Annotation annotation : methodAnnotations) {
        parseMethodAnnotation(annotation);
      }

    ...
      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);
      }
      ...
      //创建对象并返回
      return new ServiceMethod<>(this);
    }

//createCallAdapter()会调用到此处的get方法
final class DefaultCallAdapterFactory extends CallAdapter.Factory {
  static final CallAdapter.Factory INSTANCE = new DefaultCallAdapterFactory();

  @Override
  public CallAdapter<?, ?> get(Type returnType, Annotation[] annotations, Retrofit retrofit) {
    if (getRawType(returnType) != Call.class) {
      return null;
    }
    //我们自己定义的实际返回的类型
    final Type responseType = Utils.getCallResponseType(returnType);
    return new CallAdapter<Object, Call<?>>() {
      //返回数据类型
      @Override public Type responseType() {
        return responseType;
      }
      @Override public Call<Object> adapt(Call<Object> call) {
        return call;
      }
    };
  }
}

接下来我们回到retrofit.create()方法,看看ServiceMethod被创建之后做了什么操作。

public <T> T create(final Class<T> 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 {
            ...
            ServiceMethod<Object, Object> serviceMethod =
                (ServiceMethod<Object, Object>) loadServiceMethod(method);
            //创建一个OkHttpCall对象,传入serviceMethod,和参数
            OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);
            //调用之前定义的CallAdapter中的方法,返回Call对象
            return serviceMethod.callAdapter.adapt(okHttpCall);
          }
        });
  }

这里我们就创建了接口的代理对象,接下来看看请求时的enqueue方法。

Call的enqueue方法

call是一个接口类,这里的实际对象类型为OkHttpCall。

//传一个回调对象进来
@Override public void enqueue(final Callback<T> callback) {
    checkNotNull(callback, "callback == null");

    okhttp3.Call call;
    Throwable failure;

    ...

	//新建了一个call对象,调用对象的enqueue方法
    call.enqueue(new okhttp3.Callback() {
      @Override public void onResponse(okhttp3.Call call, okhttp3.Response rawResponse)
          throws IOException {
        Response<T> response;
        try {
          //转化成response对象
          response = parseResponse(rawResponse);
        } catch (Throwable e) {
          callFailure(e);
          return;
        }
        callSuccess(response);
      }

	  ...

      private void callSuccess(Response<T> response) {
        try {
          //调用成功的回调函数
          callback.onResponse(OkHttpCall.this, response);
        } catch (Throwable t) {
          t.printStackTrace();
        }
      }
    });
  }

接下来看一下okhttp3.Response对象转换成Response对象的方法parseResponse。

Response<T> parseResponse(okhttp3.Response rawResponse) throws IOException {
	//获得响应体
    ResponseBody rawBody = rawResponse.body();

    // Remove the body's source (the only stateful object) so we can pass the response along.
    //移除body字段,以便单独处理相应的状态
    rawResponse = rawResponse.newBuilder()
        .body(new NoContentResponseBody(rawBody.contentType(), rawBody.contentLength()))
        .build();
    //根据返回的code调用回调
    int code = rawResponse.code();
    if (code < 200 || code >= 300) {
      try {
        // Buffer the entire body to avoid future I/O.
        ResponseBody bufferedBody = Utils.buffer(rawBody);
        return Response.error(bufferedBody, rawResponse);
      } finally {
        rawBody.close();
      }
    }

    if (code == 204 || code == 205) {
      rawBody.close();
      return Response.success(null, rawResponse);
    }

    ExceptionCatchingRequestBody catchingBody = new ExceptionCatchingRequestBody(rawBody);
    try {
      //正常成功情况下会调用到这个方法
      T body = serviceMethod.toResponse(catchingBody);
      return Response.success(body, rawResponse);
    } catch (RuntimeException e) {
      // If the underlying source threw an exception, propagate that rather than indicating it was
      // a runtime exception.
      catchingBody.throwIfCaught();
      throw e;
    }
  }

正常情况下会调用serviceMethod.toResponse方法,来看下具体内容:

	R toResponse(ResponseBody body) throws IOException {
    return responseConverter.convert(body);

这里也就是根据我们初始化Retrofit时设置的转换器,去转换body对象,我们设置的转换器是GsonConverterFactory创建的,跟进去看下它的convert方法。

  @Override
  public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations,
      Retrofit retrofit) {
    TypeAdapter<?> adapter = gson.getAdapter(TypeToken.get(type));
    return new GsonResponseBodyConverter<>(gson, adapter);
  }
  
  final class GsonResponseBodyConverter<T> implements Converter<ResponseBody, T> {
  private final Gson gson;
  private final TypeAdapter<T> adapter;

  GsonResponseBodyConverter(Gson gson, TypeAdapter<T> adapter) {
    this.gson = gson;
    this.adapter = adapter;
  }

  @Override public T convert(ResponseBody value) throws IOException {
  	//使用Gson使用body获取一个JsonReader
    JsonReader jsonReader = gson.newJsonReader(value.charStream());
    try {
      //使用JsonReader,将Json格式数据映射成对象返回
      return adapter.read(jsonReader);
    } finally {
      value.close();
    }
  }
}

转换器转换的过程就是把规范的body内容转换成我们期望的对象返回。关于Retrofit源码的细节分析也就到这

总结

总结一下Retrofit从创建到获取到数据对象的整体过程:
1.使用Retrofit.Builder创建Retrofit对象,可以在Builder里设置baseUrl地址,数据适配器,数据转换器等。
2.创建数据Service,根据格式使用注解创建接口方法。
3.通过Retrofit对象的create方法,可以创建出接口的一个代理对象。
4.在调用代理对象中的方法时,会调用到内部InvocationHandler对象的invoke方法,通过对接口方法注解与参数的解析,封装成一个ServiceMethod,最终放在一个Call对象里进行返回。
5.异步访问时调用call.queue方法,内部通过 okhttp3.Call对象的enqueue方法进行异步执行,数据返回时根据状态码选择回调函数。
6.如果设置了数据转换器,在返回时会将body传入转换器,转换成自定义的泛型类型返回给回调函数。

本篇内容就到这了,如果有不对的地方,欢迎大家指正。

你可能感兴趣的:(android)