Retrofit源码解析

Retrofit源码解析

图解分析
在这里插入图片描述

前言

在解析源码之前我们先来,整体了解一下Retrofit。Retrofit它是Square开源的一款优秀的网络框架,这个框架对okhttp进行了封装,让我们使用okhttp做网路请求更加简单。

1.使用了注解表明请求方式,和参数类型,这是retrofit的特性,也正是简化了我们的网络请求过程的地
2.retrofit的实例化采用链式调用的设计,把需要的参数传进去即可
3.调用retrofit的create方法就可以把我们定义的接口转化成实现类,我们可以直接调用我们定义的方法进行网络请求,但是我们只定义了一个接口方法,也没有方法体,请求方式和参数类型都是注解
4.retrofit的一次网络请求示例已经结束,基于对okhttp的封装,让网络请求已经简化了很多。当然retrofit最适合的还是REST API类型的接口,方便简洁。

下面我们来看封装完整的Reftrofit请求 Rx+Retrofit+Okhttp
        params.clear();
        params.put("nickname", binding.etUserName.getText().toString().trim());
        params.put("password", binding.etPassword.getText().toString().trim());
        RetrofitManager.getInstance().create(CommonService.class)
                .login(params)
                .throttleFirst(1, TimeUnit.SECONDS)
                .retryWhen(new RetryWhenNetworkException(2, 500, 500))
                .compose(RxSchedulers.io_main())
                .subscribe(new BaseObserver<UserBean>() {

                    @Override
                    protected void onFinally() {
                        super.onFinally();
                        hideLoadingDialog();
                    }
                    @Override
                    protected void onHandleSuccess(UserBean userBean, String desc, String content) {
                        SPUtil.put(getApplicationContext(), Constant.IS_LOGIN, true);
                        SPUtil.setObject(App.getInstance(), Constant.USER, userBean);
                        startActivity(new Intent(LoginActivity.this,                   AppIndexActivity.class));
                        ToastUtils.success("登录成功");
                        ActivityManager.getInstance().finishActivity();
                    }
                });


    /**
     * 登录
     */
    @POST("user/login")
    @FormUrlEncoded
    Observable<BaseEntity<UserBean>> login(@FieldMap Map<String, Object> params);

这是封装好的请求框架 只需要在onHandleSuccess 方法中对对应的接受参数做对应呃的操作,以及UI刷新即可

一.整体分析

​ 先贴一段简单的例子

 Retrofit retrofit = new Retrofit.Builder()
                .baseUrl("http://www.shiqiblog.com")
                .build();
       Call<ResponseBody> call =  retrofit.create(CommonService.class).ceshi(params);
       //同步
       call.execute();
       //异步
       call.enqueue(new Callback<ResponseBody>() {
            @Override
            public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response)                                       {
                
            }

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

            }
        });
        
    /**
     * 登录
     */
    @POST("userwms/login")
    @FormUrlEncoded
    Call<ResponseBody> ceshi(@FieldMap Map<String, Object> params);
1.初始化

Retrofit.Builder(); //从build 方法可以看出 Reftrofit使用了建造者模式。 Builder() 方法中调用了Platform.get() 方法。这个方法这里通过反射来判断,Clss.forName(String),如果有这个类名,说明就是在这个平台。因为Android也是使用的Java,所以先判断是不是在Android,再判断是不是Java。如果都不是就返回默认Platform对象

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");
       //判断当前的系统 是否为Android
      if (Build.VERSION.SDK_INT != 0) {
        return new Android();
      }
    } catch (ClassNotFoundException ignored) {
    }
    try {
       //判断当前的系统 是否为Java
      Class.forName("java.util.Optional");
      return new Java8();
    } catch (ClassNotFoundException ignored) {
    }
    return new Platform();
  }

下面我们再来看BaseUrl方法 HttpUrl.parse(baseUrl)方法主要判断当前的 url 是否是一个合法的Http 或者https url连接 最终调用了baseUrl 参数网址转换成了一个HttpUrl对象 这个方法主要判断 网址是否以 "/"结尾

   public Builder baseUrl(String baseUrl) {
      checkNotNull(baseUrl, "baseUrl == null");
      HttpUrl httpUrl = HttpUrl.parse(baseUrl);
     //返回空抛出异常
      if (httpUrl == null) {
        throw new IllegalArgumentException("Illegal URL: " + baseUrl);
      }
      return baseUrl(httpUrl);
    }
   //盘点当前的URL 是否符合http https要求。不符合 返回null
   public Builder baseUrl(String baseUrl) {
      checkNotNull(baseUrl, "baseUrl == null");
      HttpUrl httpUrl = HttpUrl.parse(baseUrl);
      if (httpUrl == null) {
        throw new IllegalArgumentException("Illegal URL: " + baseUrl);
      }
      return baseUrl(httpUrl);
    }
    // return baseUrl(httpUrl)方法 主要判断 BaseUrl是否以 / 结尾
    public Builder baseUrl(HttpUrl baseUrl) {
      checkNotNull(baseUrl, "baseUrl == null");
      List<String> pathSegments = baseUrl.pathSegments();
      if (!"".equals(pathSegments.get(pathSegments.size() - 1))) {
        throw new IllegalArgumentException("baseUrl must end in /: " + baseUrl);
      }
      this.baseUrl = baseUrl;
      return this;
    }
client()
public Builder client(OkHttpClient client) {
      return callFactory(checkNotNull(client, "client == null"));
    }

指定网络请求执行器工厂,我们发现网络请求执行器工厂是OkHttpClient,所以网络请求执行器也就是call

build()

最后最后就到了通过Builder创建Retrofit对象的时候了。

最后就到了通过Builder创建Retrofit对象的时候了。1.创建okhttp 2.指定回调工厂3.网络请求工厂4.数据转换工厂

 public Retrofit build() {
      //判断baseurl是否为空
      if (baseUrl == null) {
        throw new IllegalStateException("Base URL required.");
      }
      //okhttp是否为空 默认为 OkHttpClient
      okhttp3.Call.Factory callFactory = this.callFactory;
      if (callFactory == null) {
        callFactory = new OkHttpClient();
      }
      /*
        设置回调执行器,如果未指定,Platfrom.defaultCallbackExecutor
        这里的platform在new Builder()时已经设置好,是Android,
        所以回调执行器就是MainThreadExecutor,主线程
        */
      Executor callbackExecutor = this.callbackExecutor;
      if (callbackExecutor == null) {
        callbackExecutor = platform.defaultCallbackExecutor();
      }

      // Make a defensive copy of the adapters and add the default Call adapter.
      //网络请求适配器
      //首先加入build()之前用户指定的callAdapterFactory
      //然后再加入platfrom默认的,这里是Android,对于API>=24,
      //会返回CompletableFutureCallAdapterFactory,否则返回ExecutorCallAdapterFactory
      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

1.创建对应请求接口 create (接口)

public interface RequestApi{
    @GET("user/login")
    Call<User> getCall();
}
RequestApi request = retrofit.create(RequestApi.class);

2.开始_创建retrofit.create(接口);当我们创建的这个接口的时候。 主要做了两件事情 1).验证当前传入的接口。validateServiceInterface 遍历接口 缓存 2).创建默认 ServiceMethod 方法中 CallAdapter<T, R> callAdapter 3).创建接口代理对象 返回代理对象的真实对象

Retrofit 通过invoke为我们构造了一个 OkHttpCall ,实际上每一个 OkHttpCall 都对应于一个请求,它主要完成最基础的网络请求,而我们在接口的返回中看到的 Call 默认情况下就是 OkHttpCall 了,如果我们添加了自定义的 callAdapter,那么它就会将 OkHttp 适配成我们需要的返回值,并返回给我们。

  @SuppressWarnings("unchecked") 
  public <T> T create(final Class<T> service) {
    //验证是否为接口 长度>0
    Utils.validateServiceInterface(service);
    if (validateEagerly) {
      //首先获取当前的Android平台,然后遍历当前接口的所有方法方法,再加载
      eagerlyValidateMethods(service);
    }
    //返回接口代理对象 参数 
    1.一个classloader对象,定义了由哪个classloader对象对生成的代理类进行加载
    2.一个interface对象数组,表示我们将要给我们的代理对象提供一组什么样的接口
    3.一个InvocationHandler对象,表示的是当动态代理对象调用方法的时候会关联到哪一个InvocationHandler对 象上,并最终由其调用。
      不多做解释文末 有随风 大兄弟的文章链接
    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 (method.getDeclaringClass() == Object.class) {
              return method.invoke(this, args);
            }
            //判断这个方法是不是DefaultMethod,Java 8新属性,表示接口的方法体。
            if (platform.isDefaultMethod(method)) {
              return platform.invokeDefaultMethod(method, service, proxy, args);
            }
            
            //loadServiceMethod 请求参数 接口 参数 注解
            ServiceMethod<Object, Object> serviceMethod =
                (ServiceMethod<Object, Object>) loadServiceMethod(method);
            OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);
            //返回我们需要的AdapterServiceMethod OkHttpCall 两者进行绑定
            return serviceMethod.callAdapter.adapt(okHttpCall);
          }
        });
  }

//loadServiceMethod加载缓存ServiceMethod,或者构建新的ServiceMethod并加入缓存serviceMethodCache
  private final Map<Method, ServiceMethod<?, ?>> serviceMethodCache = new ConcurrentHashMap<>();
private void eagerlyValidateMethods(Class<?> service) {
    Platform platform = Platform.get();
    for (Method method : service.getDeclaredMethods()) {
      //非接口主体方法 
      if (!platform.isDefaultMethod(method)) {
        //参数赋值
        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) {
        //链式调用
        result = new ServiceMethod.Builder<>(this, method).build();
        //赋值给定义好的Map
        serviceMethodCache.put(method, result);
      }
    }
    return result;
  }

  //Builder 链式
  ServiceMethod(Builder<R, T> builder) {
    this.callFactory = builder.retrofit.callFactory();
     .........................省略部分
    this.isMultipart = builder.isMultipart;
    this.parameterHandlers = builder.parameterHandlers;
  }

ServiceMethod —— build 方法

主要创建adapter ,获取response 参数 ,创建response转换器 , 遍历处理 方法注解

public ServiceMethod build() {
      //创建Adapter
      callAdapter = createCallAdapter();
      // response 对象
      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?");
      }
      // 创建responseBodyConverter 方法
      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);
        }
        //省略部分代码 判断合法性代码
      }
      return new ServiceMethod<>(this);
    }

 // 处理添加注解 不做深究 
 1.parseHttpMethodAndPath()
 2.parseHttpMethodAndPath()
 private void parseMethodAnnotation(Annotation annotation) {
      if (annotation instanceof DELETE) {
        parseHttpMethodAndPath("DELETE", ((DELETE) annotation).value(), false);
      } else if (annotation instanceof GET) {
        parseHttpMethodAndPath("GET", ((GET) annotation).value(), false);
      } 
        /***
         * 省略部分代码
         */
         else if (annotation instanceof FormUrlEncoded) {
        if (isMultipart) {
          throw methodError("Only one encoding annotation is allowed.");
        }
        isFormEncoded = true;
      }
    }
  
     return serviceMethod.callAdapter.adapt(okHttpCall); 

获取到对应的Adapter 在由Okhttp发起网络请求

总结:

  • Retrofit通过动态代理,用MethodHandler完成接口方法。
  • Retrofit的MethodHandler通过RequestFactoryParser.parse解析,获得接口方法的参数和注解的值,传入到OkHttpCall,OkHttpCall生成okhttp3.Call完成Http请求并使用Converter解析数据回调。
  • Retrofit通过工厂设置CallAdapter和Converter,CallAdapter包装转换Call,Converter转换(解析)服务器返回的数据、接口方法的注解参数。

Retrofit使用动态代理模式实现我们定义的网络请求接口,在重写invoke方法的时候构建了一个ServiceMethod对象,在构建这个对象的过程中进行了方法的注解解析得到网络请求方式httpMethod,以及参数的注解分析,拼接成一个省略baseurl的参数

动态代理参考随风大兄弟: https://blog.csdn.net/yaomingyang/article/details/81040390

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值