compile 'com.squareup.okhttp:okhttp:2.4.0' compile 'com.squareup.okhttp:okhttp-urlconnection:2.4.0' compile 'com.squareup.okio:okio:1.5.0' compile 'com.google.code.gson:gson:2.2.4' compile 'com.squareup.retrofit:retrofit:2.0.0-beta2' compile 'com.squareup.retrofit:adapter-rxjava:2.0.0-beta2' compile 'com.squareup.retrofit:converter-gson:2.0.0-beta2' compile 'io.reactivex:rxandroid:1.1.0' compile 'io.reactivex:rxjava:1.1.0'
盗两张网上的图:
下面是从创建Retrofit出发,具体的使用流程;Retrofit最终的请求操作都是交由okHttp去执行的,执行的结果返回Response,再根据转换器进行解析成相对应的返回类型T;
Retrofit中使用了动态代理,方便了使用;通过retrofit.create返回的其实是个动态代理类,所有具体的处理逻辑交由MethodHandler进行处理;
下面是Retrofit系统中的整个类图,有点像外观模式,Retrofit持有所有子系统的引用;Retrofit比较重要的是两个Factory,一个使用来生成CallAdapter的CallAdapterFactory;一个是用来转换结果的ConvertFactory;这两个都可以用户自己进行添加。
在自定义的Service中,每一个method对应一个MethodHandler,MethodHandler持有retrofit,前面两个Factory以及生成Request的RequestFactory;在okHttp中,Request需要自己进行定义创建,而Retrofit简化了这个操作,进行了相应的封装,使用注解的方式来定义Request的相关参数信息;注解信息的解析则在RequestFactory中完成,通过RequestFactoryParser对注解信息进行简单解析,RequestBuilderAction是解析method中参数中的注解如@Path这些产生的中间产物,最终通过RequestBuilder来具体产生一个Request,RequestBuilder中持有okHttp中的Request.Builder类的引用,其创建Request过程其实都是交给okHttp来操作的;
生成的Request最终封装成为一个OkHttpCall,OkHttpCall则可以看做是对okHttp中Call的通过,它的enqueue等网络请求操作都是委托个给okHttp来操作的;同时对okHttp的返回Response进行解析,使用convertFactory,将其解析为用户所期望的返回类型;
二、使用
(一)使用Call形式
public interface RetrofirHttpService { @GET("{user}") Call<UserInfo> getData(@Path("user") String user); }注:UserInfo是自己定义的解析类:
public class UserInfo { String username; String password; }
2、使用GET获取信息:
// 原始的CallBack方式 private void getUseCall() { // 添加拦截器 OkHttpClient client = new OkHttpClient(); client.interceptors().add(new LoggingInterceptor()); Retrofit retrofit = new Retrofit.Builder() .baseUrl("http://115.156.187.146/TransferServer/") .client(client) // 添加okHttp .addConverterFactory(GsonConverterFactory.create()) // GSON进行转换 .build(); RetrofirHttpService apiStores = retrofit.create(RetrofirHttpService.class); Call<UserInfo> call = apiStores.getData("ServerMain.php"); // 异步调用 call.enqueue(new Callback<UserInfo>() { @Override public void onResponse(Response<UserInfo> response, Retrofit retrofit) { UserInfo data = response.body(); LogUtils.i("Call Result:" + data.m); } @Override public void onFailure(Throwable t) { LogUtils.e(t.toString()); } }); }
1、定义请求接口
public interface RxHttpService { @GET("{path}") Observable<UserInfo> getData(@Path("path") String path); }
// 使用RxJava方式 private void getUseRxJava() { // 添加拦截器 OkHttpClient client = new OkHttpClient(); client.interceptors().add(new LoggingInterceptor()); Retrofit retrofit = new Retrofit.Builder() .baseUrl("http://115.156.187.146/TransferServer/") .client(client) // 添加okHttp .addConverterFactory(GsonConverterFactory.create()) // GSON进行转换 .addCallAdapterFactory(RxJavaCallAdapterFactory.create()) .build(); RxHttpService apiStores = retrofit.create(RxHttpService.class); Observable<UserInfo> observable = apiStores.getData("ServerMain.php"); observable.subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(new Observer<UserInfo>() { @Override public void onNext(UserInfo user) { LogUtils.i("Call Result:" + user.m); } @Override public void onCompleted() { } @Override public void onError(Throwable error) { LogUtils.e(error.toString()); } }); }
RxHttpService apiStores = retrofit.create(RxHttpService.class);
1)Retrofit#create:
/** Create an implementation of the API defined by the {@code service} interface. */ @SuppressWarnings("unchecked") // Single-interface proxy creation guarded by parameter safety. 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); } return loadMethodHandler(method).invoke(args); } }); }标准的动态代理创建过程;
来看代理类和委托类之间的中间InvocationHandler类的invoke函数具体执行逻辑;
loadMethodHandler(method).invoke(args);
2)Retrofit#loadMethodHandler:
private final Map<Method, MethodHandler<?>> methodHandlerCache = new LinkedHashMap<>(); MethodHandler<?> loadMethodHandler(Method method) { MethodHandler<?> handler; synchronized (methodHandlerCache) { handler = methodHandlerCache.get(method); if (handler == null) { handler = MethodHandler.create(this, method); methodHandlerCache.put(method, handler); } } return handler; }Retrofit维护了一个method对应的Map,这里将method都封装成一个MethodHandler类(可以当作是委托类);调用代理类,其实就是调用MethodHandler的invoke;所以具体的实现逻辑都在MethodHanlder中;
(二)MethodHanlder
1)MethodHandler类:
final class MethodHandler<T> { // MethodHandler持有一个retrofit对象 private final Retrofit retrofit; // 类似于Volley中的 Request ,包含了HTTP请求的Url、Header信息,MediaType、Method以及RequestAction数组 private final RequestFactory requestFactory; // HTTP请求返回数据的类型 private final CallAdapter<T> callAdapter; // 对返回数据进行转换的类型转换器 private final Converter<ResponseBody, T> responseConverter; private MethodHandler(Retrofit retrofit, RequestFactory requestFactory, CallAdapter<T> callAdapter, Converter<ResponseBody, T> responseConverter) { this.retrofit = retrofit; this.requestFactory = requestFactory; this.callAdapter = callAdapter; this.responseConverter = responseConverter; } // 每一次请求的最终具体调用的函数 Object invoke(Object... args) { // 可以发现最终是调用callAdapter的adapt函数 // 并且将相应的请求事务封装成一个OkHttpCall类进行处理 return callAdapter.adapt(new OkHttpCall<>(retrofit, requestFactory, responseConverter, args)); } // 调用该静态类创建一个MethodHandler实例 @SuppressWarnings("unchecked") static MethodHandler<?> create(Retrofit retrofit, Method method) { // 创建CallAdapter CallAdapter<Object> callAdapter = (CallAdapter<Object>) createCallAdapter(method, retrofit); // 根据callAdapter来获取相应的返回类型 Type responseType = callAdapter.responseType(); // 创建结果类型转换器 Converter<ResponseBody, Object> responseConverter = (Converter<ResponseBody, Object>) createResponseConverter(method, retrofit, responseType); // 创建RequestFactory RequestFactory requestFactory = RequestFactoryParser.parse(method, responseType, retrofit); return new MethodHandler<>(retrofit, requestFactory, callAdapter, responseConverter); } // 创建相应的CallAdapter private static CallAdapter<?> createCallAdapter(Method method, Retrofit retrofit) { // 这个method即为service中自定义的一个方法,一般返回都是泛型类 Type returnType = method.getGenericReturnType(); if (Utils.hasUnresolvableType(returnType)) { throw Utils.methodError(method, "Method return type must not include a type variable or wildcard: %s", returnType); } if (returnType == void.class) { throw Utils.methodError(method, "Service methods cannot return void."); } // 获取Method上对应的注解即(GET这些) Annotation[] annotations = method.getAnnotations(); try { // 根据注解来获取相应的Adapter return retrofit.callAdapter(returnType, annotations); } catch (RuntimeException e) { // Wide exception range because factories are user code. throw Utils.methodError(e, method, "Unable to create call adapter for %s", returnType); } } // 最后也是调用retrofit来实现的; private static Converter<ResponseBody, ?> createResponseConverter(Method method, Retrofit retrofit, Type responseType) { Annotation[] annotations = method.getAnnotations(); try { return retrofit.responseConverter(responseType, annotations); } catch (RuntimeException e) { // Wide exception range because factories are user code. throw Utils.methodError(e, method, "Unable to create converter for %s", responseType); } } }MethodHandler是系统为定义的每一个Service中的method中创建的一个具体执行类,通过调用其invoke函数,来执行具体的请求的逻辑;
它主要包含四个变量:
// MethodHandler持有一个retrofit对象
private final Retrofit retrofit;
// 类似于Volley中的 Request ,包含了HTTP请求的Url、Header信息,MediaType、Method以及RequestAction数组
private final RequestFactory requestFactory;
// HTTP请求返回数据的类型
private final CallAdapter<T> callAdapter;
// 对返回数据进行转换的类型转换器
private final Converter<ResponseBody, T> responseConverter;
2)Retrofit#callAdapter:
private final List<CallAdapter.Factory> adapterFactories; public CallAdapter<?> callAdapter(Type returnType, Annotation[] annotations) { return nextCallAdapter(null, returnType, annotations); } /** * Returns the {@link CallAdapter} for {@code returnType} from the available {@linkplain * #callAdapterFactories() factories} except {@code skipPast}. */ // 返回相关的可用的CallAdapter(除了指定跳过的skipPast) public CallAdapter<?> nextCallAdapter(CallAdapter.Factory skipPast, Type returnType, Annotation[] annotations) { checkNotNull(returnType, "returnType == null"); checkNotNull(annotations, "annotations == null"); int start = adapterFactories.indexOf(skipPast) + 1; // 遍历adapterFactories中所有的Factory for (int i = start, count = adapterFactories.size(); i < count; i++) { CallAdapter<?> adapter = adapterFactories.get(i).get(returnType, annotations, this); // 如果找到,则返回该Adapter if (adapter != null) { return adapter; } } // 如果获取不到相应的CallAdapter,则抛出异常 StringBuilder builder = new StringBuilder("Could not locate call adapter for ") .append(returnType) .append(". Tried:"); for (int i = start, count = adapterFactories.size(); i < count; i++) { builder.append("\n * ").append(adapterFactories.get(i).getClass().getName()); } if (skipPast != null) { builder.append("\nSkipped:"); for (int i = 0; i < start; i++) { builder.append("\n * ").append(adapterFactories.get(i).getClass().getName()); } } throw new IllegalArgumentException(builder.toString()); }RetroFit中有一个adapterFactories保存了所有CallAdapter.Factory,通过遍历这些factory来获取相应符合的CallAdapter;
先来看adapterFactories都保存了哪些Factory;从Retrofit创建开始
(三)Retrofit的创建;
private final OkHttpClient client; private final BaseUrl baseUrl; private final List<Converter.Factory> converterFactories; private final List<CallAdapter.Factory> adapterFactories; private final Executor callbackExecutor; private final boolean validateEagerly; private Retrofit(OkHttpClient client, BaseUrl baseUrl, List<Converter.Factory> converterFactories, List<CallAdapter.Factory> adapterFactories, Executor callbackExecutor, boolean validateEagerly) { this.client = client; this.baseUrl = baseUrl; this.converterFactories = converterFactories; this.adapterFactories = adapterFactories; this.callbackExecutor = callbackExecutor; this.validateEagerly = validateEagerly; } /** Create the {@link Retrofit} instances. */ public Retrofit build() { if (baseUrl == null) { throw new IllegalStateException("Base URL required."); } // 默认使用OkhttpClient OkHttpClient client = this.client; if (client == null) { client = new OkHttpClient(); } // 向adapterFactories添加了一个默认的CallAdapterFactory // 如果前面add了CallAdpterfactory(如RxJavaCallAdapterFactory),则可以看到默认的Factory会添加到用户指定的Factory后面 // 则在遍历的时候,会优先遍历用户指定的Factory List<CallAdapter.Factory> adapterFactories = new ArrayList<>(this.adapterFactories); adapterFactories.add(Platform.get().defaultCallAdapterFactory(callbackExecutor)); // Make a defensive copy of the converters. List<Converter.Factory> converterFactories = new ArrayList<>(this.converterFactories); return new Retrofit(client, baseUrl, converterFactories, adapterFactories, callbackExecutor, validateEagerly); } /** * 用户添加自定义的CallAdapterFactory */ public Builder addCallAdapterFactory(CallAdapter.Factory factory) { adapterFactories.add(checkNotNull(factory, "factory == null")); return this; }继续来看系统默认的CallAdapterFactory;
Platform.get()是根据系统不同来创建不同的运行环境,比如是再Android上还是Java上运行时由区别的,如下所示:
1)Platform:
class Platform { private static final Platform PLATFORM = findPlatform(); static Platform get() { return PLATFORM; } private static Platform findPlatform() { // 如果是Android系统 try { Class.forName("android.os.Build"); if (Build.VERSION.SDK_INT != 0) { return new Android(); } } catch (ClassNotFoundException ignored) { } // 如果是Java系统 try { Class.forName("java.util.Optional"); return new Java8(); } catch (ClassNotFoundException ignored) { } // 如果是其他系统 return new Platform(); } static class Android extends Platform { // 创建默认的CallAdapterFactory过程 @Override CallAdapter.Factory defaultCallAdapterFactory(Executor callbackExecutor) { if (callbackExecutor == null) { // 如果没有指定callbackExecutor,或者为null,则新创建一个MainThreadExecutor callbackExecutor = new MainThreadExecutor(); } return new ExecutorCallAdapterFactory(callbackExecutor); } // 该Executor的主要工作逻辑就是调用主线程的Looper来创建一个对应的Handler // 执行execute就是操作Handler进行post事件(Runnable) static class MainThreadExecutor implements Executor { private final Handler handler = new Handler(Looper.getMainLooper()); @Override public void execute(Runnable r) { handler.post(r); } } } }可以看到对应不同的系统平台,Retrofit提供了不同的Platform版本; 默认的CallAdapterFactory是个ExecutorCallAdapterFactory,它是通过 callbackExecutor来创建的;
如果用户没有执行相应的callbackExecutor,系统或创建一个默认的MainThreadExecutor,该MainThreadExecutor的主要执行逻辑就是使用主线程的Handler来post Runnable消息;
然后Retrofit把ExecutorCallAdapterFactory实例添加到adapterFactories中;
2)继续二中MethodHandler的查找CallAdapter的逻辑:
CallAdapter<?> adapter = adapterFactories.get(i).get(returnType, annotations, this);
final class ExecutorCallAdapterFactory implements CallAdapter.Factory { private final Executor callbackExecutor; ExecutorCallAdapterFactory(Executor callbackExecutor) { this.callbackExecutor = callbackExecutor; } @Override public CallAdapter<Call<?>> get(Type returnType, Annotation[] annotations, Retrofit retrofit) { // 根据returnType获得具体的返回类型 if (Utils.getRawType(returnType) != Call.class) { return null; } final Type responseType = Utils.getCallResponseType(returnType); // 返回一个CallAdapter return new CallAdapter<Call<?>>() { @Override public Type responseType() { return responseType; } // 调用adapt返回一个ExecutorCallbackCall类 @Override public <R> Call<R> adapt(Call<R> call) { return new ExecutorCallbackCall<>(callbackExecutor, call); } }; }
将前面所有的逻辑串在一起,使用动态代理创建的代理类使用的InvokeHandler,调用其invoke来实现一个具体的方法调用;委托类是系统自定义创建的MethodHandler,系统根据method来创建相应的MethodHandler;MethodHandler中持有对CallAdapter的引用,CallAdapter是通过相应的CallAdpterFactory来创建的,如果加入了自定义的比如RxJava,最终会添加到Retrofit中的一个factory链表中,系统通过扫描链表来获取合适的CallAdpterFactory来创建CallAdapter;
如果没有自定义CallAdapterFactory,则会默认生成一个CallAdapterFactory;通过这个默认的Factory会创建一个默认的CallAdapter;
代理类的方法实现,是通过InvokeHandler的invoke来实现的;在系统定义的Invokehandler中进而调用MethodHandler的invoke函数,该invoke函数会继续调用CallAdpter的adapt,由上知,最终返回一个ExecutorCallbackCall类;
继续来看ExecutorCallbackCall类:
3)ExecutorCallbackCall类:
final class ExecutorCallAdapterFactory implements CallAdapter.Factory { static final class ExecutorCallbackCall<T> implements Call<T> { private final Executor callbackExecutor; private final Call<T> delegate; // 注意这里的Call传递进来的是一个封装好的OkHttpCall类 ExecutorCallbackCall(Executor callbackExecutor, Call<T> delegate) { this.callbackExecutor = callbackExecutor; this.delegate = delegate; } @Override public void enqueue(Callback<T> callback) { delegate.enqueue(new ExecutorCallback<>(callbackExecutor, callback)); } @Override public Response<T> execute() throws IOException { return delegate.execute(); } @Override public void cancel() { delegate.cancel(); } @SuppressWarnings("CloneDoesntCallSuperClone") // Performing deep clone. @Override public Call<T> clone() { return new ExecutorCallbackCall<>(callbackExecutor, delegate.clone()); } } static final class ExecutorCallback<T> implements Callback<T> { private final Executor callbackExecutor; private final Callback<T> delegate; ExecutorCallback(Executor callbackExecutor, Callback<T> delegate) { this.callbackExecutor = callbackExecutor; this.delegate = delegate; } @Override public void onResponse(final Response<T> response, final Retrofit retrofit) { callbackExecutor.execute(new Runnable() { @Override public void run() { delegate.onResponse(response, retrofit); } }); } @Override public void onFailure(final Throwable t) { callbackExecutor.execute(new Runnable() { @Override public void run() { delegate.onFailure(t); } }); } } }可以看出来ExecutorCallbackCall也是一个代理类,它的委托类是OkHttpCall;所以使用默认的CallAdapterFactory返回的是一个封装了OkHttpCall的一个Call类,其具体的类型是ExecutorCallbackCall;
4)OkHttpCall:
final class OkHttpCall<T> implements Call<T> { private final Retrofit retrofit; private final RequestFactory requestFactory; private final Converter<ResponseBody, T> responseConverter; private final Object[] args; private volatile com.squareup.okhttp.Call rawCall; private boolean executed; // Guarded by this. private volatile boolean canceled; OkHttpCall(Retrofit retrofit, RequestFactory requestFactory, Converter<ResponseBody, T> responseConverter, Object[] args) { this.retrofit = retrofit; this.requestFactory = requestFactory; this.responseConverter = responseConverter; this.args = args; } @SuppressWarnings("CloneDoesntCallSuperClone") // We are a final type & this saves clearing state. @Override public OkHttpCall<T> clone() { return new OkHttpCall<>(retrofit, requestFactory, responseConverter, args); } }来看enqueue操作;在用户代码中:
call.enqueue(new Callback<UserInfo>() { @Override public void onResponse(Response<UserInfo> response, Retrofit retrofit) { UserInfo data = response.body(); LogUtils.i("Call Result:" + data.m); } @Override public void onFailure(Throwable t) { LogUtils.e(t.toString()); } });由前面知这里的Call对应的是一个ExecutorCallbackCall,来看起enqueue操作:
5)ExecutorCallbackCall#enqueue:
@Override public void enqueue(Callback<T> callback) { delegate.enqueue(new ExecutorCallback<>(callbackExecutor, callback)); }delegate对应的即是OkHttpCall,先来看其enqueue传入的参数: ExecutorCallback
static final class ExecutorCallback<T> implements Callback<T> { private final Executor callbackExecutor; private final Callback<T> delegate; ExecutorCallback(Executor callbackExecutor, Callback<T> delegate) { this.callbackExecutor = callbackExecutor; this.delegate = delegate; } @Override public void onResponse(final Response<T> response, final Retrofit retrofit) { callbackExecutor.execute(new Runnable() { @Override public void run() { delegate.onResponse(response, retrofit); } }); } @Override public void onFailure(final Throwable t) { callbackExecutor.execute(new Runnable() { @Override public void run() { delegate.onFailure(t); } }); } }ExecutorCallback也是一个代理类,用来封装用户定义的Callback,即类内部的变量delegate;callbackExecutor是查找Platform过程中new Android时创建的,其execute的逻辑较为简单,就是使用主线程对应的Handler来将Runnable事件post出去;可以看到,这里最终的响应都会在主线程中,而且调用用户自定义的onResponse和onFailure;
ExecutorCallback的主要作用就是封装Callback,将Callback的响应切换到主线程中;
@Override public void enqueue(final Callback<T> callback) { synchronized (this) { if (executed) throw new IllegalStateException("Already executed"); executed = true; } // 注意okHttp的call和retrofit的Call是不同 com.squareup.okhttp.Call rawCall; try { // 创建一个okhttp类型的Call rawCall = createRawCall(); } catch (Throwable t) { callback.onFailure(t); return; } // 如果取消 if (canceled) { rawCall.cancel(); } this.rawCall = rawCall; // 将Call中事务全部委托给okHttp来操作 // 调用okHttp的enqueue,使用okHttp的Callback来封装retrofit中的Callback rawCall.enqueue(new com.squareup.okhttp.Callback() { // 请求失败的响应 private void callFailure(Throwable e) { try { callback.onFailure(e); } catch (Throwable t) { t.printStackTrace(); } } // 请求成功的响应 private void callSuccess(Response<T> response) { try { callback.onResponse(response, retrofit); } catch (Throwable t) { t.printStackTrace(); } } @Override public void onFailure(Request request, IOException e) { callFailure(e); } @Override public void onResponse(com.squareup.okhttp.Response rawResponse) { Response<T> response; try { // 获得结果,并且解析结果 response = parseResponse(rawResponse); } catch (Throwable e) { callFailure(e); return; } callSuccess(response); } }); }OkHttpCall是对OkHttp里面的Call的封装,将所有的操作都委托给该Call进行操作;这里主要的是通过retrofit的call来创建一个okHttp的Call类;这里通过createRawCall来创建:
private com.squareup.okhttp.Call createRawCall() { return retrofit.client().newCall(requestFactory.create(args)); }这里是通过okHttpClient来创建的一个Call;首先来看 requestFactory是如何根据args参数来创建一个相应的Call的;
先来看requestFactory,它是MethodHandler中创建传递过来的;
RequestFactory requestFactory = RequestFactoryParser.parse(method, responseType, retrofit);
(四)RequestFactory:
final class RequestFactoryParser { private final Method method; static RequestFactory parse(Method method, Type responseType, Retrofit retrofit) { RequestFactoryParser parser = new RequestFactoryParser(method); // Method对应的注解这里解析 parser.parseMethodAnnotations(responseType); parser.parseParameters(retrofit); // 创建一个RequestFactory return parser.toRequestFactory(retrofit.baseUrl()); } private RequestFactoryParser(Method method) { this.method = method; } }RequestFactoryParser主要是根据method对应的注解及返回类型进行相应解析,得出相应的解析结果,然后创建一个RequestFactory来保存所有解析后结果,供创建OkHttpCall来使用。
<一>parseMethodAnnotations:
1)RequestFactoryParser#parseMethodAnnotations:
// 这里是对注解进行解析的核心代码 // 根据注解类型的不同(即请求类型类型的不同进行相应解析) // 这里也对应的retrofit的基本用法 private void parseMethodAnnotations(Type responseType) { for (Annotation annotation : method.getAnnotations()) { 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 HEAD) { parseHttpMethodAndPath("HEAD", ((HEAD) annotation).value(), false); if (!Void.class.equals(responseType)) { throw methodError(method, "HEAD method must use Void as response type."); } } else if (annotation instanceof PATCH) { parseHttpMethodAndPath("PATCH", ((PATCH) annotation).value(), true); } else if (annotation instanceof POST) { parseHttpMethodAndPath("POST", ((POST) annotation).value(), true); } else if (annotation instanceof PUT) { parseHttpMethodAndPath("PUT", ((PUT) annotation).value(), true); } else if (annotation instanceof HTTP) { HTTP http = (HTTP) annotation; parseHttpMethodAndPath(http.method(), http.path(), http.hasBody()); } else if (annotation instanceof Headers) { // 只有Headers是通过parseHeaders来解析的 String[] headersToParse = ((Headers) annotation).value(); if (headersToParse.length == 0) { throw methodError(method, "@Headers annotation is empty."); } headers = parseHeaders(headersToParse); } else if (annotation instanceof Multipart) { // 可以看到Multipart和FormUrlEncoded不能同时定义 if (isFormEncoded) { throw methodError(method, "Only one encoding annotation is allowed."); } isMultipart = true; } else if (annotation instanceof FormUrlEncoded) { if (isMultipart) { throw methodError(method, "Only one encoding annotation is allowed."); } isFormEncoded = true; } } if (httpMethod == null) { throw methodError(method, "HTTP method annotation is required (e.g., @GET, @POST, etc.)."); } // 如果不存在实体,但是用户定义为isMultipart或者isFormEncoded类型,则会抛出异常 if (!hasBody) { if (isMultipart) { throw methodError(method, "Multipart can only be specified on HTTP methods with request body (e.g., @POST)."); } if (isFormEncoded) { throw methodError(method, "FormUrlEncoded can only be specified on HTTP methods with request body " + "(e.g., @POST)."); } } }这里是对注解进行解析的核心代码 , 根据注解类型的不同(即请求类型类型的不同) 进行相应解析, 这里也对应的retrofit的基本用法;
可以看到除了headers使用的parseheader进行解析外,其他都是通过 parseHttpMethodAndPath进行解析的;这里来重点分析下parseHttpMethodAndPath;
2)parseHttpMethodAndPath:
// 这个函数只是先做了一个前期判断 private void parseHttpMethodAndPath(String httpMethod, String value, boolean hasBody) { if (this.httpMethod != null) { throw methodError(method, "Only one HTTP method is allowed. Found: %s and %s.", this.httpMethod, httpMethod); } // 对应"GET","POST"等 this.httpMethod = httpMethod; // 标注有没有实体 this.hasBody = hasBody; // 注解值为空直接返回 if (value.isEmpty()) { return; } // 判断URL是否已经有查询字符串query string // Get the relative URL path and existing query string, if present. int question = value.indexOf('?'); if (question != -1 && question < value.length() - 1) { // 保证URL的查询字符串中没有{...}之类的字符 // Ensure the query string does not have any named parameters. String queryParams = value.substring(question + 1); Matcher queryParamMatcher = PARAM_URL_REGEX.matcher(queryParams); if (queryParamMatcher.find()) { throw methodError(method, "URL query string \"%s\" must not have replace block. " + "For dynamic query parameters use @Query.", queryParams); } } // 具体的解析继续由parsePathParameters来完成 this.relativeUrl = value; this.relativeUrlParamNames = parsePathParameters(value); }
这个函数仅是做了一个简单的前期判断,将method对应的请求类型(httpMethod) ,注解值(relativeUrl),以及是否包含实体信息(hasBody)赋值给该RequestFactoryParser;
来看POST和GET的简单调用实例:
parseHttpMethodAndPath("GET", ((GET) annotation).value(), false); parseHttpMethodAndPath("POST", ((POST) annotation).value(), true);继续来看解析函数parsePathParameters :
private static final String PARAM = "[a-zA-Z][a-zA-Z0-9_-]*"; private static final Pattern PARAM_NAME_REGEX = Pattern.compile(PARAM); private static final Pattern PARAM_URL_REGEX = Pattern.compile("\\{(" + PARAM + ")\\}"); /** * Gets the set of unique path parameters used in the given URI. If a parameter is used twice * in the URI, it will only show up once in the set. */ // 检测出路径中所有{..}的字段,如{user}等,添加到一个Set中(因此不会重复添加) static Set<String> parsePathParameters(String path) { Matcher m = PARAM_URL_REGEX.matcher(path); Set<String> patterns = new LinkedHashSet<>(); while (m.find()) { patterns.add(m.group(1)); } return patterns; }这一步仅是根据正则表达式获取URL中所有{...}类型的数据,添加到patterns这个Set中,然后返回给RequestFactoryParser,对relativeUrlParamNames进行赋值;
第一步解析完毕,来到第二步parseParameters:
<二>parseParameters:
private final Method method; private void parseParameters(Retrofit retrofit) { Type[] methodParameterTypes = method.getGenericParameterTypes(); // 获取method所有参数中的所有的注解信息 Annotation[][] methodParameterAnnotationArrays = method.getParameterAnnotations(); boolean gotField = false; boolean gotPart = false; boolean gotBody = false; boolean gotPath = false; boolean gotQuery = false; boolean gotUrl = false; int count = methodParameterAnnotationArrays.length; // 这里是一个重要的类 RequestBuilderAction[] requestBuilderActions = new RequestBuilderAction[count]; for (int i = 0; i < count; i++) { Type methodParameterType = methodParameterTypes[i]; Annotation[] methodParameterAnnotations = methodParameterAnnotationArrays[i]; if (methodParameterAnnotations != null) { for (Annotation methodParameterAnnotation : methodParameterAnnotations) { RequestBuilderAction action = null; // 解析URL if (methodParameterAnnotation instanceof Url) { if (gotUrl) { throw parameterError(i, "Multiple @Url method annotations found."); } if (gotPath) { throw parameterError(i, "@Path parameters may not be used with @Url."); } if (gotQuery) { throw parameterError(i, "A @Url parameter must not come after a @Query"); } if (methodParameterType != String.class) { throw parameterError(i, "@Url must be String type."); } if (relativeUrl != null) { throw parameterError(i, "@Url cannot be used with @%s URL", httpMethod); } gotUrl = true; action = new RequestBuilderAction.Url(); } else if (methodParameterAnnotation instanceof Path) { // 解析Path注解 if (gotQuery) { throw parameterError(i, "A @Path parameter must not come after a @Query."); } if (gotUrl) { throw parameterError(i, "@Path parameters may not be used with @Url."); } if (relativeUrl == null) { throw parameterError(i, "@Path can only be used with relative url on @%s", httpMethod); } gotPath = true; Path path = (Path) methodParameterAnnotation; String name = path.value(); validatePathName(i, name); action = new RequestBuilderAction.Path(name, path.encoded()); } else if (methodParameterAnnotation instanceof Query) { // 解析Query注解 Query query = (Query) methodParameterAnnotation; action = new RequestBuilderAction.Query(query.value(), query.encoded()); gotQuery = true; } else if (methodParameterAnnotation instanceof QueryMap) { // 解析QueryMap注解 if (!Map.class.isAssignableFrom(Utils.getRawType(methodParameterType))) { throw parameterError(i, "@QueryMap parameter type must be Map."); } QueryMap queryMap = (QueryMap) methodParameterAnnotation; action = new RequestBuilderAction.QueryMap(queryMap.encoded()); } else if (methodParameterAnnotation instanceof Header) { // 解析header注解 Header header = (Header) methodParameterAnnotation; action = new RequestBuilderAction.Header(header.value()); } else if (methodParameterAnnotation instanceof Field) { // 解析Field注解 if (!isFormEncoded) { throw parameterError(i, "@Field parameters can only be used with form encoding."); } Field field = (Field) methodParameterAnnotation; action = new RequestBuilderAction.Field(field.value(), field.encoded()); gotField = true; } else if (methodParameterAnnotation instanceof FieldMap) { // 解析FieldMap if (!isFormEncoded) { throw parameterError(i, "@FieldMap parameters can only be used with form encoding."); } if (!Map.class.isAssignableFrom(Utils.getRawType(methodParameterType))) { throw parameterError(i, "@FieldMap parameter type must be Map."); } FieldMap fieldMap = (FieldMap) methodParameterAnnotation; action = new RequestBuilderAction.FieldMap(fieldMap.encoded()); gotField = true; } else if (methodParameterAnnotation instanceof Part) { // 解析Part if (!isMultipart) { throw parameterError(i, "@Part parameters can only be used with multipart encoding."); } Part part = (Part) methodParameterAnnotation; com.squareup.okhttp.Headers headers = com.squareup.okhttp.Headers.of( "Content-Disposition", "form-data; name=\"" + part.value() + "\"", "Content-Transfer-Encoding", part.encoding()); Converter<?, RequestBody> converter; try { converter = retrofit.requestConverter(methodParameterType, methodParameterAnnotations); } catch (RuntimeException e) { // Wide exception range because factories are user code. throw parameterError(e, i, "Unable to create @Part converter for %s", methodParameterType); } action = new RequestBuilderAction.Part<>(headers, converter); gotPart = true; } else if (methodParameterAnnotation instanceof PartMap) { //解析PartMap if (!isMultipart) { throw parameterError(i, "@PartMap parameters can only be used with multipart encoding."); } if (!Map.class.isAssignableFrom(Utils.getRawType(methodParameterType))) { throw parameterError(i, "@PartMap parameter type must be Map."); } PartMap partMap = (PartMap) methodParameterAnnotation; action = new RequestBuilderAction.PartMap(retrofit, partMap.encoding(), methodParameterAnnotations); gotPart = true; } else if (methodParameterAnnotation instanceof Body) { // 解析Body if (isFormEncoded || isMultipart) { throw parameterError(i, "@Body parameters cannot be used with form or multi-part encoding."); } if (gotBody) { throw parameterError(i, "Multiple @Body method annotations found."); } Converter<?, RequestBody> converter; try { converter = retrofit.requestConverter(methodParameterType, methodParameterAnnotations); } catch (RuntimeException e) { // Wide exception range because factories are user code. throw parameterError(e, i, "Unable to create @Body converter for %s", methodParameterType); } action = new RequestBuilderAction.Body<>(converter); gotBody = true; } if (action != null) { if (requestBuilderActions[i] != null) { throw parameterError(i, "Multiple Retrofit annotations found, only one allowed."); } requestBuilderActions[i] = action; } } } if (requestBuilderActions[i] == null) { throw parameterError(i, "No Retrofit annotation found."); } } if (relativeUrl == null && !gotUrl) { throw methodError(method, "Missing either @%s URL or @Url parameter.", httpMethod); } if (!isFormEncoded && !isMultipart && !hasBody && gotBody) { throw methodError(method, "Non-body HTTP method cannot contain @Body."); } if (isFormEncoded && !gotField) { throw methodError(method, "Form-encoded method must contain at least one @Field."); } if (isMultipart && !gotPart) { throw methodError(method, "Multipart method must contain at least one @Part."); } this.requestBuilderActions = requestBuilderActions; }这里是根据method里面对应的参数中的注解比如@(Url,Path,Query,QueryMap,Header,Field,FieldMap,Part,PartMap,Body),它们的解析操作都是通过创建一个RequestBuilderAction类进行相应的解析,比如@Path,解析则调用的是创建一个RequestBuilderAction.Path,Path类是RequestBuilderAction的静态内部类,继承了RequestBuilderAction;最后所有参数中的注解对应一个RequestBuilderAction数组requestBuilderActions,把这个数组赋值给RequestFactoryParser;
abstract class RequestBuilderAction { abstract void perform(RequestBuilder builder, Object value); }RequestBuilderAction是个抽象类,它内部有一个perform的抽象方法;还有一些具体的内部类继承该方法,根据不同的注解,来创建不同的静态内部类,这些静态内部类都继承了RequestBuilderAction,并且重写了自己的perform方法;
如Path:
static final class Path extends RequestBuilderAction { private final String name; private final boolean encoded; Path(String name, boolean encoded) { this.name = checkNotNull(name, "name == null"); this.encoded = encoded; } @Override void perform(RequestBuilder builder, Object value) { if (value == null) { throw new IllegalArgumentException( "Path parameter \"" + name + "\" value must not be null."); } builder.addPathParam(name, value.toString(), encoded); } }可以看到这里涉及到一个 RequestBuilder的概念,等到具体使用的时候再进行分析;
<三>parser.toRequestFactory(retrofit.baseUrl());
private RequestFactory toRequestFactory(BaseUrl baseUrl) { return new RequestFactory(httpMethod, baseUrl, relativeUrl, headers, contentType, hasBody, isFormEncoded, isMultipart, requestBuilderActions); }
最后一步将所有解析完的信息封装成一个 RequestFactory;
则继续来看第(三)步中okHttp创建Call是传递进来的参数:requestFactory.create(args)
1)RequestFactory#create:
final class RequestFactory { private final String method; private final BaseUrl baseUrl; private final String relativeUrl; private final Headers headers; private final MediaType contentType; private final boolean hasBody; private final boolean isFormEncoded; private final boolean isMultipart; private final RequestBuilderAction[] requestBuilderActions; RequestFactory(String method, BaseUrl baseUrl, String relativeUrl, Headers headers, MediaType contentType, boolean hasBody, boolean isFormEncoded, boolean isMultipart, RequestBuilderAction[] requestBuilderActions) { this.method = method; this.baseUrl = baseUrl; this.relativeUrl = relativeUrl; this.headers = headers; this.contentType = contentType; this.hasBody = hasBody; this.isFormEncoded = isFormEncoded; this.isMultipart = isMultipart; this.requestBuilderActions = requestBuilderActions; } Request create(Object... args) { // 创建一个RequestBuilder,它是真正创建Request的类 RequestBuilder requestBuilder = new RequestBuilder(method, baseUrl.url(), relativeUrl, headers, contentType, hasBody, isFormEncoded, isMultipart); if (args != null) { // 前面解析method参数中的注解获取到的RequestBuilderAction RequestBuilderAction[] actions = requestBuilderActions; if (actions.length != args.length) { throw new IllegalArgumentException("Argument count (" + args.length + ") doesn't match action count (" + actions.length + ")"); } for (int i = 0, count = args.length; i < count; i++) { // 这里调用RequestBuilderAction的perform进行创建 actions[i].perform(requestBuilder, args[i]); } } // 建造者模型,返回一个build return requestBuilder.build(); } }可以看到RequestFactory在create时创建了一个RequestBuilder,它是一个典型的Builder模式,也就是类似OkHttp的Request Buidler模式,由用户调用builder自定义来转化为通过注解方式进行定义,而Retrofit通过解析注解信息,分析用户行为,然后将解析信息添加到requestBuidler中来自行构造Request;它暴漏出注解定义的方式,而将具体的Request Builder细节隐藏了。
这里终点来看RequestBuidler,前面分析method参数类型获得的RequestBuilderAction,其具体的perform也是通过RequestBuilderAction来实现的;
<四>RequestBuilder:
final class RequestBuilder { // 注意这里的Request对应的是okHttp的request private final Request.Builder requestBuilder; RequestBuilder(String method, HttpUrl baseUrl, String relativeUrl, Headers headers, MediaType contentType, boolean hasBody, boolean isFormEncoded, boolean isMultipart) { this.method = method; this.baseUrl = baseUrl; this.relativeUrl = relativeUrl; this.requestBuilder = new Request.Builder(); this.contentType = contentType; this.hasBody = hasBody; if (headers != null) { requestBuilder.headers(headers); } if (isFormEncoded) { // Will be set to 'body' in 'build'. formEncodingBuilder = new FormEncodingBuilder(); } else if (isMultipart) { // Will be set to 'body' in 'build'. multipartBuilder = new MultipartBuilder(); multipartBuilder.type(MultipartBuilder.FORM); } } }Retrofit默认是通过okHttp来实现的,RequestBuidler其实将所有Request创建的任务交给真正的类Okhttp中的Request.Builder进行构造器request;相应的FormEncodingBuilder以及MultipartBuilder也都是okHttp内部的;
RequestBuilderAction中的perform也都是通过RequestBuidler的函数来实现的,比如@Path:
static final class Path extends RequestBuilderAction { private final String name; private final boolean encoded; Path(String name, boolean encoded) { this.name = checkNotNull(name, "name == null"); this.encoded = encoded; } @Override void perform(RequestBuilder builder, Object value) { if (value == null) { throw new IllegalArgumentException( "Path parameter \"" + name + "\" value must not be null."); } builder.addPathParam(name, value.toString(), encoded); } }可以看到调用的RequestBuilder的addPathParam方法:
void addPathParam(String name, String value, boolean encoded) { if (relativeUrl == null) { // The relative URL is cleared when the first query parameter is set. throw new AssertionError(); } relativeUrl = relativeUrl.replace("{" + name + "}", canonicalize(value, encoded)); }再来看RequestFactory#create的返回值,即RequestBuilder.builder:
Request build() { HttpUrl url; HttpUrl.Builder urlBuilder = this.urlBuilder; if (urlBuilder != null) { url = urlBuilder.build(); } else { // No query parameters triggered builder creation, just combine the relative URL and base URL. url = baseUrl.resolve(relativeUrl); } RequestBody body = this.body; if (body == null) { // Try to pull from one of the builders. if (formEncodingBuilder != null) { body = formEncodingBuilder.build(); } else if (multipartBuilder != null) { body = multipartBuilder.build(); } else if (hasBody) { // Body is absent, make an empty body. body = RequestBody.create(null, new byte[0]); } } MediaType contentType = this.contentType; if (contentType != null) { if (body != null) { body = new ContentTypeOverridingRequestBody(body, contentType); } else { requestBuilder.addHeader("Content-Type", contentType.toString()); } } return requestBuilder .url(url) .method(method, body) .build(); }
因此返回的是一个okHttp中的Request;来看具体的创建:
retrofit.client().newCall(requestFactory.create(args));OkHttpClient#newCall:
/** * Prepares the {@code request} to be executed at some point in the future. */ public Call newCall(Request request) { return new Call(this, request); }将Request封装成一个Call;因此,接下来的所有网络请求操作都交由okHttp进行处理;
(五)这里来看具体的对返回结果的转换:
在调用OkHttpCall的enqueue时,可以看到Callback中会对结果Response(okHttp中的)进行进一步解析,然后将解析后的结果通过Retrofit中定义的callback进行返回,返回的结果为Response<T>:
rawCall.enqueue(new com.squareup.okhttp.Callback() { private void callFailure(Throwable e) { try { callback.onFailure(e); } catch (Throwable t) { t.printStackTrace(); } } private void callSuccess(Response<T> response) { try { callback.onResponse(response, retrofit); } catch (Throwable t) { t.printStackTrace(); } } @Override public void onFailure(Request request, IOException e) { callFailure(e); } @Override public void onResponse(com.squareup.okhttp.Response rawResponse) { Response<T> response; try { response = parseResponse(rawResponse); } catch (Throwable e) { callFailure(e); return; } callSuccess(response); } });可以看到主要解析过程则在于parseResponse中;
1)OkHttpCall#parseResponse:
private Response<T> parseResponse(com.squareup.okhttp.Response rawResponse) throws IOException { ResponseBody rawBody = rawResponse.body(); // Remove the body's source (the only stateful object) so we can pass the response along. rawResponse = rawResponse.newBuilder() .body(new NoContentResponseBody(rawBody.contentType(), rawBody.contentLength())) .build(); int code = rawResponse.code(); // 如果请求失败 if (code < 200 || code >= 300) { try { // Buffer the entire body to avoid future I/O. ResponseBody bufferedBody = Utils.readBodyToBytesIfNecessary(rawBody); return Response.error(bufferedBody, rawResponse); } finally { closeQuietly(rawBody); } } // 没有数据返回情况,则不用进行解析 if (code == 204 || code == 205) { return Response.success(null, rawResponse); } // ExceptionCatchingRequestBody是对okHttp的RequestBody类的一个封装 ExceptionCatchingRequestBody catchingBody = new ExceptionCatchingRequestBody(rawBody); try { // 利用变换器进行转换 T body = responseConverter.convert(catchingBody); // 将转换后的实体结果和原Response封装成一个Response<T> 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; } }当请求发生错误,获取204情况没有实体返回时,自然不用对实体进行转化;而对于实体的转换是通过最初定义的转换器来(这里用的是GSON)来进行covert的;然后将转换后的结果同原始okHttp返回的Response封装成一个Response<T>进行返回;
先来看传递进来的转换器:
2)ConverterFactory:
是通过在build retrofit的时候添加的:
Retrofit retrofit = new Retrofit.Builder() .baseUrl("http://115.156.187.146/TransferServer/") .client(client) // 添加okHttp .addConverterFactory(GsonConverterFactory.create()) // GSON进行转换 .build();
Retrofit#ConverterFactory:
/** Add converter factory for serialization and deserialization of objects. */ public Builder addConverterFactory(Converter.Factory converterFactory) { converterFactories.add(checkNotNull(converterFactory, "converterFactory == null")); return this; }由前面的分析知,在创建MethodHandler时,
static MethodHandler<?> create(Retrofit retrofit, Method method) { CallAdapter<Object> callAdapter = (CallAdapter<Object>) createCallAdapter(method, retrofit); Type responseType = callAdapter.responseType(); // 创建转换器 Converter<ResponseBody, Object> responseConverter = (Converter<ResponseBody, Object>) createResponseConverter(method, retrofit, responseType); RequestFactory requestFactory = RequestFactoryParser.parse(method, responseType, retrofit); return new MethodHandler<>(retrofit, requestFactory, callAdapter, responseConverter); } private static Converter<ResponseBody, ?> createResponseConverter(Method method, Retrofit retrofit, Type responseType) { Annotation[] annotations = method.getAnnotations(); try { return retrofit.responseConverter(responseType, annotations); } catch (RuntimeException e) { // Wide exception range because factories are user code. throw Utils.methodError(e, method, "Unable to create converter for %s", responseType); } }进而根据返回数据类型调用Retrofit.responseConverter来创建;下面基本上和CallAdapterFactory基本相同的流程;通过遍历converterFactories链表进行获取相应的解析Converter:
public <T> Converter<ResponseBody, T> responseConverter(Type type, Annotation[] annotations) { checkNotNull(type, "type == null"); checkNotNull(annotations, "annotations == null"); for (int i = 0, count = converterFactories.size(); i < count; i++) { // 重点看这个解析函数 Converter<ResponseBody, ?> converter = converterFactories.get(i).fromResponseBody(type, annotations); if (converter != null) { //noinspection unchecked return (Converter<ResponseBody, T>) converter; } } StringBuilder builder = new StringBuilder("Could not locate ResponseBody converter for ") .append(type) .append(". Tried:"); for (Converter.Factory converterFactory : converterFactories) { builder.append("\n * ").append(converterFactory.getClass().getName()); } throw new IllegalArgumentException(builder.toString()); }主要的逻辑在:
// 重点看这个解析函数 Converter<ResponseBody, ?> converter = converterFactories.get(i).fromResponseBody(type, annotations);这里的converterFactories.get(i)即是通过GsonConverterFactory.create()创建的GSON转换器;来看其具体细节;
3)GsonConverterFactory:
public final class GsonConverterFactory extends Converter.Factory { /** * Create an instance using a default {@link Gson} instance for conversion. Encoding to JSON and * decoding from JSON (when no charset is specified by a header) will use UTF-8. */ public static GsonConverterFactory create() { return create(new Gson()); } /** * Create an instance using {@code gson} for conversion. Encoding to JSON and * decoding from JSON (when no charset is specified by a header) will use UTF-8. */ public static GsonConverterFactory create(Gson gson) { return new GsonConverterFactory(gson); } private final Gson gson; private GsonConverterFactory(Gson gson) { if (gson == null) throw new NullPointerException("gson == null"); this.gson = gson; } @Override public Converter<ResponseBody, ?> fromResponseBody(Type type, Annotation[] annotations) { return new GsonResponseBodyConverter<>(gson, type); } @Override public Converter<?, RequestBody> toRequestBody(Type type, Annotation[] annotations) { return new GsonRequestBodyConverter<>(gson, type); } }这个Factory很简单,就是持有了一个GSON实例对象;
上面调用了fromResponseBody,这里根据returntype来创建一个GsonRequestBodyConverter:
final class GsonRequestBodyConverter<T> implements Converter<T, RequestBody> { private static final MediaType MEDIA_TYPE = MediaType.parse("application/json; charset=UTF-8"); private static final Charset UTF_8 = Charset.forName("UTF-8"); private final Gson gson; private final Type type; GsonRequestBodyConverter(Gson gson, Type type) { this.gson = gson; this.type = type; } @Override public RequestBody convert(T value) throws IOException { Buffer buffer = new Buffer(); Writer writer = new OutputStreamWriter(buffer.outputStream(), UTF_8); try { gson.toJson(value, type, writer); writer.flush(); } catch (IOException e) { throw new AssertionError(e); // Writing to Buffer does no I/O. } return RequestBody.create(MEDIA_TYPE, buffer.readByteString()); } }其转换函数convert逻辑也较为简单,将工作交给gson就可以了;所以Retrofit的解析工作在okHttp的Call返回结果时,在其okHttp的Callback中进行解析,解析过程也较为简单,通过获取返回类型Returntype,然后使用gson进行解析即可;