HTTP网络NetAndroid进阶之路

2017-10-6(Retrofit使用范例的源码分析 )

2017-10-07  本文已影响160人  721d739b6619

讲到Retrofit先聊一下什么是rest风格的url,为什么说Retrofit十分适合rest风格。

以前客户端发送请求给服务端 一般请求的url都是这样子的:

//get请求
www.baidu.com/search?id=1&name='abc'
//post请求
www.baidu.com/update
//post请求体
id=1
name='abc'

然而你会发现我们的请求不是post就是get;一般把请求的操作(即增删改查)都放在url上,请求的方式(即post或get)是固定的。这样我们的客户端的http请求都会把请求方式封装成post或get,一般网络请求框架都是这样。

rest风格:是把资源请求(即url)与操作方式(post请求或get或put或delete)分开。

//rest风格url
www.baidu.com/rest/user/1
//上面请求方式通过get请求,获取user id为1的客户信息。get请求代表是查询  而url仅仅是一种资源请求。把资源请求与操作分开。

//删除操作
www.baidu.com/rest/user/1
//这样上面的url与之前的查询user id为1的url一样,但这次是用delete请求,这样更直观说明rest风格的核心就是资源请求与操作分开。

那为什么Retrofit适合rest风格呢?主要是因为它的注解方式。post或get或delete都可以通过注释的方式操作。不同以往的框架耦合在工具类上。

下面说一下Retrofit使用中的源码分析

//示例代码
//第一步,创建一个接口  ApiService就是该接口

        //第二步,创建retrofit对象
        Retrofit retrofit = new Retrofit.Builder()
                .baseUrl("")
                //使用的网络请求 OkHttpClient;这个可以添加个过滤,超时
                .client(okHttpClient)
                //返回数据转换处理
                .addConverterFactory(GsonConverterFactory.create())
                .build();
        //第三步,创建一个实现了接口的代理对象;这里retrofit.create(ApiService.class);其实就是动态代理,生成代理对象
       ApiService apiService = retrofit.create(ApiService.class);
        //第四步,调用接口方法,返回一个Call对象(这里不一定是Call对象可以是RxJava的Observer)。生成一个OKHttpCall的代理对象,其实返回什么主要看ApiService接口定义的返回值
       Call<ResponseBody> call = apiService.getMovie(1,1);
        //第五步,调用execute执行同步请求。 返回结果
        retrofit2.Response<ResponseBody> response = call.execute();

        //第六步,从响应获取数据。打印数据
        System.out.println(response.body().string());

一般就是这么个步骤,下面来逐一分析每步Retrofit干什么的。

第一步定义接口

这一步其实没有什么好讲的,就是像代理模式一样定义一个接口,接口写上一些注解,为Retrofit之后的获取各个参数做的。

@POST("top250")
    Call<ResponseBody> getMovie(@Query("start")int start, @Query("count")int count);

第二步,创建retrofit对象

Retrofit retrofit = new Retrofit.Builder()
                .baseUrl("")
                //使用的网络请求 OkHttpClient;这个可以添加个过滤,超时
                .client(okHttpClient)
                //返回数据转换处理
                .addConverterFactory(GsonConverterFactory.create())
                .build();

我们一句句看看源码:

Retrofit retrofit = new Retrofit.Builder()

获取平台

这个说在前面就是判断是什么平台的java或Android或IOS;继续往下看:

Paste_Image.png Paste_Image.png

通过反射判断该类对象是否存在。看看new Android()干什么的。

Paste_Image.png

就这么点代码;在这阶段还没有操作什么之后那几个方法就会有用处了。继续看下一句代码:

.baseUrl("")
这个很简单就是获取基本的url然后封装成okhttp的httpUrl

![Paste_Image.png](http://upload-images.jianshu.io/upload_images/1717635-5b87eceddb8554fa.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/

.client(okHttpClient)
就是设置okhttpClient

Paste_Image.png

这里只是检测非空。

.addConverterFactory(GsonConverterFactory.create())
这里添加默认的返参转化,当然可以自己实现一个。

Paste_Image.png

converterFactories是一个list集合用于存放这些转换器。其实就是使用第一个。

.build();
所有设置完成就是build()构建retrofit对象。

public Retrofit build() {
      if (baseUrl == null) {
        throw new IllegalStateException("Base URL required.");
      }

      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);
    }
  }

代码不多也容易理解:

Paste_Image.png Paste_Image.png

事实上就是获取主线程的Handler。Platform这个类的作用基本上就是判断平台,二获取Handler,为了之后http请求后在主线程处理结果。

Paste_Image.png Paste_Image.png

第二步,构建代理对象;这里不了解java的动态代理可以看看我写的这篇文章:动态代理

ApiService apiService = retrofit.create(ApiService.class);
//调用接口方法,返回一个Call对象(这里不一定是Call对象可以是RxJava的Observer)。生成一个OKHttpCall的代理对象,其实返回什么主要看ApiService接口定义的返回值
Call<ResponseBody> call = apiService.getMovie(1,1);

其实我这里觉得它是没有使用到代理模式的,只是使用了Java中的Proxy类和InvocationHandler接口来实现它对我们自定义那些接口获取相应的参数封装成一个Okhttp的Request请求来发送http请求而已。因为作为代理模式,它并没有委托对象,只是有一个接口而已。具体做什么其实Retrofit也很清楚,我们自定义接口只是为了因应不同请求作出不同的参数变化而已。不知道我这样理解对不对。
言归正传看看第三步干了什么

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

第三步,就是调用接口方法,而调用接口方法都会调用到InvocationHandler接口的invoke方法。

其实就关注invoke方法。刚才也说了Retrofit通过Proxy和InvocationHandler实现我们自定义接口的对象,而对象调用任何一个方法都会调用invoke方法;

Paste_Image.png

主要关注这三行代码,其他都是校验语句;

ServiceMethod serviceMethod = loadServiceMethod(method);

Paste_Image.png

这里代码大致意思就是有ServiceMethod对象就从缓存取,没有就构建一个。我们看看构建的代码。

result = new ServiceMethod.Builder(this, method).build();

new ServiceMethod.Builder(this, method)

这段代码很明显就是从我们自定义的接口方法中获取各种Retrofit需要的参数:

public ServiceMethod build() {
      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);
      }

      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.");
      }

      return new ServiceMethod<>(this);
    }

callAdapter = createCallAdapter();

获取接口方法的返回类型:Type returnType = method.getGenericReturnType();
获取方法的注解
Annotation[] annotations = method.getAnnotations();
retrofit.callAdapter(returnType, annotations);这个其实就是一个Call对象
其实一直看下去就会发现调用了Retrofit的一个叫nextCallAdapter方法

Paste_Image.png

这里其实就是获取最后一个,因为第一个是默认的,而自定义的会放在最后。
再看看之前的代码:

Paste_Image.png Paste_Image.png

这样应该很清楚:

我们的CallAdapter就是ExecutorCallAdapterFactory,因为我们的示例代码必没有添加addCallAdapterFactory,如果与RxJava一并使用就需要添加builder.addCallAdapterFactory(RxJavaCallAdapterFactory.create());

而回调方法就是主线程Handler的Runnable;
ExecutorCallAdapterFactory这个类的方法不多,到时候再看看它长成什么样。

刚看完createCallAdapter();现在继续往下看:
其实后面的方法就是对我们自定义接口里面方法的参数作解释继而组装成一个http请求

OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args);
这个就是通过serviceMethod和我们方法的参数生成一个okhttpCall

serviceMethod.callAdapter.adapt(okHttpCall);

由于callAdapter是一个抽象类,而实现它的类是一个匿名内部类:

createCallAdapter() Paste_Image.png nextCallAdapter() Paste_Image.png

上面已经说了这里的CallAdapter实现类是ExecutorCallAdapterFactory
那么看看ExecutorCallAdapterFactory类的get方法:

Paste_Image.png

ExecutorCallAdapterFactory类的get方法最终返回一个匿名的CallAdapter。
而invoke方法最终返回serviceMethod.callAdapter.adapt(okHttpCall);即截图的箭头:new ExecutorCallbackCall<>(callbackExecutor, call);
其实这个是ExecutorCallAdapterFactory的内部类同事也是一个Call;

Call<ResponseBody> call = apiService.getMovie(1,1);

那么第四步其实就是返回一个Call对象,即示例代码返回一个ExecutorCallbackCall对象。

retrofit2.Response<ResponseBody> response = call.execute();

第五步,执行同步,当然也有异步方法enqueue

在示例代码中其实就是调用ExecutorCallbackCall类实现Call接口的execute()或enqueue()方法。
而还有一处就是callbackExecutor.execute(new Runnable() {其实就是执行Platform的execute,即将回调放回主线程的Handler处理:

Paste_Image.png

这里其实不管是调用execute()或enqueue()方法都是调用delegate的:

Paste_Image.png Paste_Image.png

而delegate其实就是OkHttpCall。

Paste_Image.png

最后再看看OkHttpCall:这里只看enqueue吧,都是大同小异

@Override public void enqueue(final Callback<T> callback) {
    if (callback == null) throw new NullPointerException("callback == null");

    okhttp3.Call call;
    Throwable failure;

    synchronized (this) {
      if (executed) throw new IllegalStateException("Already executed.");
      executed = true;

      call = rawCall;
      failure = creationFailure;
      if (call == null && failure == null) {
        try {
          call = rawCall = createRawCall();
        } catch (Throwable t) {
          failure = creationFailure = t;
        }
      }
    }

    if (failure != null) {
      callback.onFailure(this, failure);
      return;
    }

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

    call.enqueue(new okhttp3.Callback() {
      @Override public void onResponse(okhttp3.Call call, okhttp3.Response rawResponse)
          throws IOException {
        Response<T> response;
        try {
          response = parseResponse(rawResponse);
        } catch (Throwable e) {
          callFailure(e);
          return;
        }
        callSuccess(response);
      }

      @Override public void onFailure(okhttp3.Call call, IOException e) {
        try {
          callback.onFailure(OkHttpCall.this, e);
        } catch (Throwable t) {
          t.printStackTrace();
        }
      }

      private void callFailure(Throwable e) {
        try {
          callback.onFailure(OkHttpCall.this, e);
        } catch (Throwable t) {
          t.printStackTrace();
        }
      }

      private void callSuccess(Response<T> response) {
        try {
          callback.onResponse(OkHttpCall.this, response);
        } catch (Throwable t) {
          t.printStackTrace();
        }
      }
    });
  }

其实这里基本上已经不是Retrofit范畴了,Retrofit已经将http请求交由okhttp处理了。
这里的Call已经是Okhttp的Call了。

Paste_Image.png Paste_Image.png

serviceMethod.toRequest(args);就是将我们自定义的method参数给serviceMethod组装Request对象;
serviceMethod.callFactory.newCall(request);就是通过OkHttpClient对象创建okhttp3.Call对象;

Paste_Image.png

后面的代码就是Okhttp的Call执行然后将结果给回调的CallBack对象

Paste_Image.png

现在归纳一下Retrofit几个重要的类和它们的作用:

Retrofit核心的几个类基本上就这些了。如果本文有哪里有误希望指出,大家共同学习。本人是一名菜鸟,希望通过阅读源码另自己的编程能力上一个台阶。

补充UML关系图

RetrofitUML.png
上一篇 下一篇

猜你喜欢

热点阅读