【retrofit学习】

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档


前言

retrofit学习笔记


一、retrofit的使用步骤

步骤1:添加Retrofit库的依赖
步骤2:创建 接收服务器返回数据 的类
步骤3:创建 用于描述网络请求 的接口
步骤4:创建 Retrofit 实例
步骤5:创建 网络请求接口实例 并 配置网络请求参数
步骤6:发送网络请求(异步 / 同步)

二、网络请求接口的书写

接口书写格式:

@方法("地址")
@标记
接收类   方法名(@注解(键名)参数)
例:
 @POST("/form")
 @FormUrlEncoded
 Call<ResponseBody> getCall(@Field("username") String name, @Field("age") int age);
  • 请求类型有GET、POST、PUT、HEAD、OPTION、DELETE、PATH、HTTP
  • 有三种标记:
标记解释规范
@FormUrlEncoded表示请求体是一个Form表单必须使用@Filed注解键名
@MultiPart表示请求体是一个支持文件上传的表单使用@Part注解键名
@Streaming表示以流的形式返回,用于大量数据
  • 参数注解

总结:@Field、@Query和@Part都适用于普通表单字段,其中Field用于POST请求,Query用于GET请求,Part用于有文件上传的表单
@Path是URL地址的缺省值,@URL可以直接作为地址传入,当GET、POST…HTTP等方法中没有设置Url时,则必须使用 {@link Url}提供

public interface GetRequest_Interface {
		//  RxJava 方式:Observable<..>接口形式
        @GET("users/{user}/repos")
        //Observable<ResponseBody>  getBlog(@Path("user") String user );
        Call<ResponseBody>  getBlog(@Path("user") String user );
        // 访问的API是:https://api.github.com/users/{user}/repos
        // 在发起请求时, {user} 会被替换为方法的第一个参数 user(被@Path注解作用)
    }

三、retrofit的配置及使用

创建 retrofit实例

Retrofit retrofit = new Retrofit.Builder()
                .baseUrl("http://fanyi.youdao.com/") // 设置网络请求的Url地址
                .client(sOkHttpClient)
                .addConverterFactory(GsonConverterFactory.create()) // 设置数据解析器
                .addCallAdapterFactory(RxJavaCallAdapterFactory.create()) // 支持RxJava平台
                .build();

创建网络请求接口实例,配置参数

// 创建 网络请求接口 的实例
GetRequest_Interface request = retrofit.create(GetRequest_Interface.class);
//对 发送请求 进行封装
Call<Reception> call = request.getCall();

步骤6:发送网络请求(异步 / 同步)

//发送网络请求(异步)
        call.enqueue(new Callback<Translation>() {
            //请求成功时回调
            @Override
            public void onResponse(Call<Translation> call, Response<Translation> response) {
                //请求处理,输出结果
                response.body().show();
            }

            //请求失败时候的回调
            @Override
            public void onFailure(Call<Translation> call, Throwable throwable) {
                System.out.println("连接失败");
            }
        });

// 发送网络请求(同步)
Response<Reception> response = call.execute();

结合RxJava发送网络请求

// 创建 网络请求接口 的实例
GetRequest_Interface request = retrofit.create(GetRequest_Interface.class);
//用RXJava对 发送请求 进行封装
Observable<Translation> observable = request.getCall();

//发送异步请求
observable.subservableOn(Schedulers.io())
		  .observeOn(AndroidSchedulers.mainThread())
		  .subservable(new Observable<Translation>{
// 发送请求后调用该复写方法(无论请求成功与否)
                    @Override
                    public void onSubscribe(Disposable d) {
                        ...// 初始化工作
                      }
                    // 发送请求成功后调用该复写方法
                    @Override
                    public void onNext(Translation result) {
                        ...// 对返回结果Translation类对象 进行处理
                    }
                    // 发送请求成功后,先调用onNext()再调用该复写方法
                    @Override
                    public void onComplete() {
                        Log.d(TAG, "请求成功");
                    }
                    // 发送请求失败后调用该复写方法
                    @Override
                    public void onError(Throwable e) {
                        Log.d(TAG, "请求失败");
                    }

                });
		  }

四、retrofit源码解读

retrofit网络通信流程

在这里插入图片描述
具体过程

  1. 通过解析 网络请求接口的注解 配置 网络请求参数
  2. 通过 动态代理 生成 网络请求对象
  3. 通过 网络请求适配器 将 网络请求对象 进行平台适配(CallJavaAdapter)
  4. 通过 网络请求执行器 发送网络请求(OkHttp3.Call)
  5. 通过 数据转换器 解析服务器返回的数据(Converter)
  6. 通过 回调执行器 切换线程(子线程 ->>主线程)(CallBackExecutor)
  7. 用户在主线程处理返回结果

1.创建retrofit实例

Retrofit 使用建造者模式通过Builder类建立了一个Retrofit实例,具体创建细节是配置了:

平台类型对象(Platform - Android)
网络请求的url地址(baseUrl)
网络请求工厂(callFactory)

默认使用OkHttpCall

网络请求适配器工厂的集合(adapterFactories)

本质是配置了网络请求适配器工厂- 默认是ExecutorCallAdapterFactory

数据转换器工厂的集合(converterFactories)

本质是配置了数据转换器工厂

回调方法执行器(callbackExecutor)

默认回调方法执行器作用是:切换线程(子线程 - 主线程)

由于使用了建造者模式,所以开发者并不需要关心配置细节就可以创建好Retrofit实例,建造者模式get。

在创建Retrofit对象时,你可以通过更多更灵活的方式去处理你的需求,如使用不同的Converter、使用不同的CallAdapter,这也就提供了你使用RxJava来调用Retrofit的可能

2.创建网络请求接口实例

2.1使用步骤
<-- 步骤1:定义接收网络数据的类 -->
<-- JavaBean.java -->
public class JavaBean {
  .. // 这里就不介绍了
  }

<-- 步骤2:定义网络请求的接口类 -->
<-- AccessApi.java -->
public interface AccessApi {
    // 注解GET:采用Get方法发送网络请求
    // Retrofit把网络请求的URL分成了2部分:1部分baseurl放在创建Retrofit对象时设置;另一部分在网络请求接口设置(即这里)
    // 如果接口里的URL是一个完整的网址,那么放在创建Retrofit对象时设置的部分可以不设置
    @GET("openapi.do?keyfrom=Yanzhikai&key=2032414398&type=data&doctype=json&version=1.1&q=car")

    // 接受网络请求数据的方法
    Call<JavaBean> getCall();
    // 返回类型为Call<*>,*是解析得到的数据类型,即JavaBean
}

<-- 步骤3:在MainActivity创建接口类实例  -->
AccessApi NetService = retrofit.create(AccessApi.class);
       
<-- 步骤4:对发送请求的url进行封装,即生成最终的网络请求对象  --> 
        Call<JavaBean> call = NetService.getCall();
步骤3讲解:AccessApi NetService = retrofit.create(NetService.class);
 public <T> T create(final Class<T> service) {
 	//从class是不是接口和有没有继承其他接口进行校验
	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);
          }
        });
  }

// 特别注意
// return (T) roxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces,  InvocationHandler invocationHandler)
// 可以解读为:getProxyClass(loader, interfaces) .getConstructor(InvocationHandler.class).newInstance(invocationHandler);
// 即通过动态生成的代理类,调用interfaces接口的方法实际上是通过调用InvocationHandler对象的invoke()来完成指定的功能

<-- 关注点1:eagerlyValidateMethods() -->
private void eagerlyValidateMethods(Class<?> service) {  
    Platform platform = Platform.get();  
    for (Method method : service.getDeclaredMethods()) {  
      if (!platform.isDefaultMethod(method)) {  loadServiceMethod(method); } 
      // 将传入的ServiceMethod对象加入LinkedHashMap<Method, ServiceMethod>集合
     // 使用LinkedHashMap集合的好处:lruEntries.values().iterator().next()获取到的是集合最不经常用到的元素,提供了一种Lru算法的实现
    }  
}  

动态代理的使用:

  • return (T) roxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler invocationHandler)
  • 通过代理模式中的动态代理模式,动态生成网络请求接口的代理类,并将代理类的实例创建交给InvocationHandler类 作为具体的实现,并最终返回一个动态代理对象。当我们调用请求接口时,InvocationHandler中的Invoke方法就会拦截到

retrofit的原理其实就是这样,当我们调用请求接口时,InvocationHandler中的Invoke方法就会拦截到方法、参数,再根据我们在方法上的注解,去拼接为一个正常的Okhttp请求,然后执行。

ServiceMethod(网络请求方法)的创建
  • ServiceMethod serviceMethod = loadServiceMethod(method);这个方法首先是想查看serviceMethodCache中有没有缓存之前创建的网络请求实例,没有就通过建造者模式创建serviceMethod对象
 if (result == null) {
      // 下面会详细介绍ServiceMethod生成实例的过程
        result = ServiceMethod.parseAnnotations(this, method);
        serviceMethodCache.put(method, result);
      }
ServiceMethod.parseAnnotations
abstract class ServiceMethod<T> {
  static <T> ServiceMethod<T> parseAnnotations(Retrofit retrofit, Method method) {
  	//获取注解信息
    RequestFactory requestFactory = RequestFactory.parseAnnotations(retrofit, method);

    Type returnType = method.getGenericReturnType();
    if (Utils.hasUnresolvableType(returnType)) {
      throw methodError(method,
          "Method return type must not include a type variable or wildcard: %s", returnType);
    }
    if (returnType == void.class) {
      throw methodError(method, "Service methods cannot return void.");
    }
	//HttpService实现大部分的功能
    return HttpServiceMethod.parseAnnotations(retrofit, method, requestFactory);
  }

  abstract @Nullable T invoke(Object[] args);
}

可以看到通过ServiceMethod通过RequestFactory.parseAnnotations(retrofit, method);创建了一个RequestFactory实例,RequestFactory中已经包含了访问网络所需的所有基本信息,并且在通过Builder创建的过程中会对参数进行验证

final class RequestFactory {
  static RequestFactory parseAnnotations(Retrofit retrofit, Method method) {
    return new Builder(retrofit, method).build();
  }
  
//2、ServiceMethod文件中ServiceMethod.Builder
Builder(Retrofit retrofit, Method method) {
      this.retrofit = retrofit;
      this.method = method;
      this.methodAnnotations = method.getAnnotations(); //方法的注解
      this.parameterTypes = method.getGenericParameterTypes(); //方法的参数类型
      this.parameterAnnotationsArray = method.getParameterAnnotations(); //方法参数的注解

HttpServiceMethod.parseAnnotations(retrofit, method, requestFactory);返回了一个HttpServiceMethod对象去执行invoke调用

//部分关键代码
static <ResponseT, ReturnT> HttpServiceMethod<ResponseT, ReturnT> parseAnnotations(
    Retrofit retrofit, Method method, RequestFactory requestFactory) {
    ...
    //创建了网络请求适配器
    CallAdapter<ResponseT, ReturnT> callAdapter =
        createCallAdapter(retrofit, method, adapterType, annotations);
    Type responseType = callAdapter.responseType();
    ...
    //创建网络请求执行器
    Converter<ResponseBody, ResponseT> responseConverter =
        createResponseConverter(retrofit, method, responseType);
	
    okhttp3.Call.Factory callFactory = retrofit.callFactory;
    ...
     return (HttpServiceMethod<ResponseT, ReturnT>) new SuspendForBody<>(requestFactory,
          callFactory, responseConverter, (CallAdapter<ResponseT, Call<ResponseT>>) callAdapter,
          continuationBodyNullable);
    }
//HttpServiceMethod中具体实现了invoke如下:
@Override final @Nullable ReturnT invoke(Object[] args) {
//创建Call对象
Call<ResponseT> call = new OkHttpCall<>(requestFactory, args, callFactory, responseConverter);
    // adapt 也是个抽象方法,具体实现在CallAdapted,SuspendForResponse,SuspendForBody
    return adapt(call, args);
}

HttpServiceMethod的SuspendForBody的实现:

  static final class SuspendForBody<ResponseT> extends HttpServiceMethod<ResponseT, Object> {
    private final CallAdapter<ResponseT, Call<ResponseT>> callAdapter;
    private final boolean isNullable;

    ......

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

      //noinspection unchecked Checked by reflection inside RequestFactory.
      Continuation<ResponseT> continuation = (Continuation<ResponseT>) args[args.length - 1];

      /**
      像在await和awaitNullable内部一样,
      对OkHttp Call.enqueue()的调用有时可以在异常返回之前调用提供的回调,
      而调用栈帧可以返回。 协程将拦截Continuation的后续调用,并同步引发异常。
      如果未在接口方法中声明检查的异常,则Java代理无法引发检查的异常。 
      为了避免将同步检查的异常包装在UndeclaredThrowableException中,
      将其拦截并提供给帮助程序,该帮助程序将强制执行挂起操作,
      以便可以将其传递给继续操作以绕过此限制。
      **/
      try {
        // 调用具体的扩赞方法
        return isNullable
            ? KotlinExtensions.awaitNullable(call, continuation)
            : KotlinExtensions.await(call, continuation);
      } catch (Exception e) {
        return KotlinExtensions.yieldAndThrow(e, continuation);
      }
    }
  }

接下来是扩展方法await实现如下:

suspend fun <T : Any> Call<T>.await(): T {
  return suspendCancellableCoroutine { continuation ->
    continuation.invokeOnCancellation {
      cancel()
    }
    // 实际就是调用call.enqueue
    enqueue(object : Callback<T> {
      override fun onResponse(call: Call<T>, response: Response<T>) {
        if (response.isSuccessful) {
          val body = response.body()
          if (body == null) {
            val invocation = call.request().tag(Invocation::class.java)!!
            val method = invocation.method()
            val e = KotlinNullPointerException("Response from " +
                method.declaringClass.name +
                '.' +
                method.name +
                " was null but response body type was declared as non-null")
            continuation.resumeWithException(e)
          } else {
            //携带数据切回挂起处,等同于callback
            continuation.resume(body)
          }
        } else {
          continuation.resumeWithException(HttpException(response))
        }
      }

      override fun onFailure(call: Call<T>, t: Throwable) {
        continuation.resumeWithException(t)
      }
    })
  }
}

当执行到KotlinExtensions.await/awaitNullable时,就会执行相应扩展函数挂起。不难发现最终还是通过Call.enqueue发起请求,得到相应结果则通过相应的resume函数恢复挂起。这个Call对象即是上面invoke中的OkHttpCall,而它的创建方式如下。不难猜测最终发起请求和数据转换都在它里面,所以上面的await扩展中调用的enqueue方法也在其中实现。
//避免回滚上去看OkHttpCall,所以在这儿由cv了一次

Call<ResponseT> call = new OkHttpCall<>(requestFactory, args, callFactory, responseConverter);
复制代码看看OkHttpCall的enqueue方法
@Override public void enqueue(final Callback<T> callback) {
    checkNotNull(callback, "callback == null");

    ......
    
    //经过一些列判断操作 最终发起请求
    call.enqueue(new okhttp3.Callback() {
        @Override public void onResponse(okhttp3.Call call, okhttp3.Response rawResponse) {
            Response<T> response;
            try {
                // 解析Response
                response = parseResponse(rawResponse);
            } catch (Throwable e) {
                throwIfFatal(e);
                callFailure(e);
                return;
            }

            try {
                callback.onResponse(OkHttpCall.this, response);
            } catch (Throwable t) {
                throwIfFatal(t);
                t.printStackTrace(); // TODO this is not great
            }
        }

        ......
    });
}

// 解析响应数据
Response<T> parseResponse(okhttp3.Response rawResponse) throws IOException {
    ResponseBody rawBody = rawResponse.body();

    ......

    ExceptionCatchingResponseBody catchingBody = new ExceptionCatchingResponseBody(rawBody);
    try {
        //responseConverter 最终通过Converter解析完毕数据
        T body = responseConverter.convert(catchingBody);
        return Response.success(body, rawResponse);
    } catch (RuntimeException e) {
        ......
    }

四、总结

Retrofit通过接口注解简化网络请求配置,采用动态代理加载解析注解执行方法。然后通过OkHttpCall发起请求,获得响应后通过Converter进行数据转换后交给CallAdapter把最终数据交给开发者。
通过对Retrofit的使用和源码了解,Retrofit确实是封装了OkHttp,并且大量使用注解和设计模式,比如建造者模式、动态代理模式、工厂模式、适配器模式等等。
虽然这是基于2.6.2解析,但是看最新提交已有些许改版,前面也提到一点儿。

五、补充

线程切换
因为上面是用的协程,所以没有看到是如何进行线程切换的,现在我们来see 1 see,注释都加了。如下:

final class DefaultCallAdapterFactory extends CallAdapter.Factory {
  private final @Nullable Executor callbackExecutor;

  // 传入参数为回调执行者,还记得在哪儿传入的么?
  // Retrofit的build方法,根据相关平台里面来的
  DefaultCallAdapterFactory(@Nullable Executor callbackExecutor) {
    this.callbackExecutor = callbackExecutor;
  }

  // 此方法用于createCallAdapter
  @Override public @Nullable CallAdapter<?, ?> get(
      Type returnType, Annotation[] annotations, Retrofit retrofit) {
    if (getRawType(returnType) != Call.class) {
      return null;
    }
    if (!(returnType instanceof ParameterizedType)) {
      throw new IllegalArgumentException(
          "Call return type must be parameterized as Call<Foo> or Call<? extends Foo>");
    }
    final Type responseType = Utils.getParameterUpperBound(0, (ParameterizedType) returnType);
//获取执行者(协程不需要)
final Executor executor = Utils.isAnnotationPresent(annotations, SkipCallbackExecutor.class)
    ? null
    : callbackExecutor;

return new CallAdapter<Object, Call<?>>() {
  @Override public Type responseType() {
    return responseType;
  }

  @Override public Call<Object> adapt(Call<Object> call) {
    // 返回call,非协程方式用ExecutorCallbackCall包一层
    return executor == null
        ? call
        : new ExecutorCallbackCall<>(executor, call);
  }
};
}

static final class ExecutorCallbackCall<T> implements Call<T> {

final Executor callbackExecutor;

final Call<T> delegate;

ExecutorCallbackCall(Executor callbackExecutor, Call<T> delegate) {
  this.callbackExecutor = callbackExecutor;
  this.delegate = delegate;
}

@Override public void enqueue(final Callback<T> callback) {
  checkNotNull(callback, "callback == null");

  // 此处的delegate就是OkHttpCall
  delegate.enqueue(new Callback<T>() {
    @Override public void onResponse(Call<T> call, final Response<T> response) {
      //回调执行者完成线程切换
      callbackExecutor.execute(new Runnable() {
        @Override public void run() {
          if (delegate.isCanceled()) {
            // Emulate OkHttp's behavior of throwing/delivering an IOException on cancellation.
            callback.onFailure(ExecutorCallbackCall.this, new IOException("Canceled"));
          } else {
            // 通过callback回调了结果
            callback.onResponse(ExecutorCallbackCall.this, response);
          }
        }
      });
    }

    @Override public void onFailure(Call<T> call, final Throwable t) {
      callbackExecutor.execute(new Runnable() {
        @Override public void run() {
          callback.onFailure(ExecutorCallbackCall.this, t);
        }
      });
    }
  });
}

@Override public boolean isExecuted() {
  return delegate.isExecuted();
}

@Override public Response<T> execute() throws IOException {
  return delegate.execute();
}

@Override public void cancel() {
  delegate.cancel();
}

@Override public boolean isCanceled() {
  return delegate.isCanceled();
}

@SuppressWarnings("CloneDoesntCallSuperClone") // Performing deep clone.
@Override public Call<T> clone() {
  return new ExecutorCallbackCall<>(callbackExecutor, delegate.clone());
}

@Override public Request request() {
  return delegate.request();
}

}
}

# 总结
提示:这里对文章进行总结:
例如:以上就是今天要讲的内容,本文仅仅简单介绍了pandas的使用,而pandas提供了大量能使我们快速便捷地处理数据的函数和方法。
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值