前言
Retrofit是对OkHttp的封装,本次分析的源码版本是retrofit:2.4.0。
使用实例
带着问题去看源码,从1到5一步一步来分析,Retrofit怎么创建实例?怎么实现接口?怎么解析注解?怎么进行网络请求?
var retrofit = Retrofit.Builder() //1
.baseUrl("http://v.juhe.cn/xhzd/")//2
.addConverterFactory(GsonConverterFactory.create())//3
.build()//4
var wordService = retrofit.create(WordService::class.java)//5
创建Retrofit
①创建Retrofit对象使用了建造者模式。
Builder(Platform platform) {
this.platform = platform;
}
public Builder() {
///获取Platform
this(Platform.get());
}
获取Platform,主要用于构建Retrofit时获取默认CallAdapterFactory和CallbackExecutor。
class Platform {
//找到相对应的Platform
private static final Platform PLATFORM = findPlatform();
static Platform get() {
return PLATFORM;
}
private static Platform findPlatform() {
//因为是Android平台,因此new Android();
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();
}
@Nullable Executor defaultCallbackExecutor() {
return null;
}
boolean isDefaultMethod(Method method) {
return false;
}
static class Android extends Platform {
//创建默认CallbackExecutor
@Override public Executor defaultCallbackExecutor() {
return new MainThreadExecutor();
}
//创建默认CallAdapterFactory
@Override CallAdapter.Factory defaultCallAdapterFactory(@Nullable Executor callbackExecutor) {
if (callbackExecutor == null) throw new AssertionError();
return new ExecutorCallAdapterFactory(callbackExecutor);
}
//创建一个主线程Executor,用于切换线程,在主线程中显示数据。
static class MainThreadExecutor implements Executor {
private final Handler handler = new Handler(Looper.getMainLooper());
@Override public void execute(Runnable r) {
handler.post(r);
}
}
}
}
②调用baseUrl方法,传入String转换为HttpUrl,或者可以直接传HttpUrl。
public Builder baseUrl(String baseUrl) {
//url不能为空,否则会报空指针异常
checkNotNull(baseUrl, "baseUrl == null");
//解析Url,判断Url是否是一个格式良好的Http或者Https,否则返回null。
HttpUrl httpUrl = HttpUrl.parse(baseUrl);
if (httpUrl == null) {
throw new IllegalArgumentException("Illegal URL: " + baseUrl);
}
return baseUrl(httpUrl);
}
传入HttpUrl,对HttpUrl格式做判断。
public Builder baseUrl(HttpUrl baseUrl) {
checkNotNull(baseUrl, "baseUrl == null");
List<String> pathSegments = baseUrl.pathSegments();
//判断baseUrl结尾是否为“/”,否则抛出异常。baseUrl总是要以“/”结尾。
if (!"".equals(pathSegments.get(pathSegments.size() - 1))) {
throw new IllegalArgumentException("baseUrl must end in /: " + baseUrl);
}
this.baseUrl = baseUrl;
return this;
}
③调用addConverterFactory传入GsonConverterFactory.create()或者其他转换器。
public Builder addConverterFactory(Converter.Factory factory) {
//如果为null则抛出空指针异常。
converterFactories.add(checkNotNull(factory, "factory == null"));
return this;
}
④调用build方法,返回Retrofit。
public Retrofit build() {
if (baseUrl == null) {
throw new IllegalStateException("Base URL required.");
}
//如果自己设置OkHttpClient,则使用。如果没有则创建新的OkHttpClient。
okhttp3.Call.Factory callFactory = this.callFactory;
if (callFactory == null) {
callFactory = new OkHttpClient();
}
//如果自己设置Executor则使用。
//如果没有获取默认的CallbackExecutor,也就是MainThreadExecutor。
Executor callbackExecutor = this.callbackExecutor;
if (callbackExecutor == null) {
callbackExecutor = platform.defaultCallbackExecutor();
}
//添加默认的CallAdapter.Factory,从platform取得的默认CallAdapter.Factory
//也就是ExecutorCallAdapterFactory
List<CallAdapter.Factory> callAdapterFactories = new ArrayList<>(this.callAdapterFactories);
callAdapterFactories.add(platform.defaultCallAdapterFactory(callbackExecutor));
List<Converter.Factory> converterFactories =
new ArrayList<>(1 + this.converterFactories.size());
//添加第一个converters,如果设置Converters则都添加进converterFactories。
converterFactories.add(new BuiltInConverters());
converterFactories.addAll(this.converterFactories);//把两个converterFactories合并
//validateEagerly默认为false,它的作用是在创建代理类之前验证接口。
return new Retrofit(callFactory, baseUrl, unmodifiableList(converterFactories),
unmodifiableList(callAdapterFactories), callbackExecutor, validateEagerly);
}
实现接口
⑤首先调用retrofit的create方法,一般情况下默认返回代理类,不调用eagerlyValidateMethods方法。
public <T> T create(final Class<T> service) {
//传进来的类只能是接口,且该接口没有继承其他的接口
Utils.validateServiceInterface(service);
//快速验证方法,validateEagerly默认为false
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 {
// 如果该方法的类是Object类,则直接调用,不做别的处理。
if (method.getDeclaringClass() == Object.class) {
return method.invoke(this, args);
}
//Android默认返回false
if (platform.isDefaultMethod(method)) {
//只有java8才会调用
return platform.invokeDefaultMethod(method, service, proxy, args);
}
//获取合适的CallAdapterFactory、ConverterFactory,解析注解。
ServiceMethod<Object, Object> serviceMethod =
(ServiceMethod<Object, Object>) loadServiceMethod(method);
//封装OkHttp
OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);
return serviceMethod.adapt(okHttpCall);
}
});
}
eagerlyValidateMethods最终会调用loadServiceMethod方法,如果不将validateEagerly设置为true,则不会调用此方法。此方法用于快速验证接口,eagerlyValidateMethods的源码如下所示:
private void eagerlyValidateMethods(Class<?> service) {
Platform platform = Platform.get();
for (Method method : service.getDeclaredMethods()) {
//Android调用isDefaultMethod方法默认返回false
if (!platform.isDefaultMethod(method)) {
loadServiceMethod(method);
}
}
}
获取合适的CallAdapterFactory、ConverterFactory、解析注解
调用loadServiceMethod方法,获取ServiceMethod。
serviceMethodCache.get(method)为null的时候创建新的ServiceMethod。
ServiceMethod<?, ?> loadServiceMethod(Method method) {
ServiceMethod<?, ?> result = serviceMethodCache.get(method);
if (result != null) return result;
synchronized (serviceMethodCache) {
result = serviceMethodCache.get(method);
if (result == null) {
//构造ServiceMethod
result = new ServiceMethod.Builder<>(this, method).build();
serviceMethodCache.put(method, result);
}
}
return result;
}
调用Builder方法,初始化参数。
Builder(Retrofit retrofit, Method method) {
this.retrofit = retrofit;
this.method = method;
//获取接口方法注解
this.methodAnnotations = method.getAnnotations();
//获取接口方法的参数类型
this.parameterTypes = method.getGenericParameterTypes();
//获取接口参数注解
this.parameterAnnotationsArray = method.getParameterAnnotations();
}
调用build()方法,创建callAdapter、converters、注解解析,返回ServiceMethod。
public ServiceMethod build() {
//根据接口返回类型创建callAdapter
callAdapter = createCallAdapter();
//获取callAdapter的响应类型
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?");
}
//根绝接口返回参数responseType,创建Converter。
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);
}
build方法中调用的createCallAdapter源码,根据接口返回类型创建合适的callAdapter。
private CallAdapter<T, R> createCallAdapter() {
//获取方法返回值类型
Type returnType = method.getGenericReturnType();
if (Utils.hasUnresolvableType(returnType)) {
throw methodError(
"Method return type must not include a type variable or wildcard: %s", returnType);
}
if (returnType == void.class) {
throw methodError("Service methods cannot return void.");
}
//获取方法注解
Annotation[] annotations = method.getAnnotations();
try {
//创建callAdapter,默认情况下如果returnType不是Call,则会抛出异常。
return (CallAdapter<T, R>) retrofit.callAdapter(returnType, annotations);
} catch (RuntimeException e) { // Wide exception range because factories are user code.
throw methodError(e, "Unable to create call adapter for %s", returnType);
}
}
public CallAdapter<?, ?> callAdapter(Type returnType, Annotation[] annotations) {
return nextCallAdapter(null, returnType, annotations);
}
public CallAdapter<?, ?> nextCallAdapter(@Nullable CallAdapter.Factory skipPast, Type returnType,
Annotation[] annotations) {
checkNotNull(returnType, "returnType == null");
checkNotNull(annotations, "annotations == null");
int start = callAdapterFactories.indexOf(skipPast) + 1;
for (int i = start, count = callAdapterFactories.size(); i < count; i++) {
//根据创建时的callAdapterFactories,获取CallAdapter。在没有设置CallAdapter的默认情况下,返回参数不是Call则返回null。
//调用callAdapterFactory的get方法
CallAdapter<?, ?> adapter = callAdapterFactories.get(i).get(returnType, annotations, this);
if (adapter != null) {
return adapter;
}
}
//如果adapter = null,会抛出异常。
StringBuilder builder = new StringBuilder("Could not locate call adapter for ")
.append(returnType)
.append(".\n");
if (skipPast != null) {
builder.append(" Skipped:");
for (int i = 0; i < start; i++) {
builder.append("\n * ").append(callAdapterFactories.get(i).getClass().getName());
}
builder.append('\n');
}
builder.append(" Tried:");
for (int i = start, count = callAdapterFactories.size(); i < count; i++) {
builder.append("\n * ").append(callAdapterFactories.get(i).getClass().getName());
}
throw new IllegalArgumentException(builder.toString());
}
build方法中调用的createResponseConverter源码。和createCallAdapter类似,根据接口返回类型获取合适的convertersFactory。
private Converter<ResponseBody, T> createResponseConverter() {
Annotation[] annotations = method.getAnnotations();
try {
return retrofit.responseBodyConverter(responseType, annotations);
} catch (RuntimeException e) { // Wide exception range because factories are user code.
throw methodError(e, "Unable to create converter for %s", responseType);
}
}
public <T> Converter<ResponseBody, T> responseBodyConverter(Type type, Annotation[] annotations) {
return nextResponseBodyConverter(null, type, annotations);
}
public <T> Converter<ResponseBody, T> nextResponseBodyConverter(
@Nullable Converter.Factory skipPast, Type type, Annotation[] annotations) {
checkNotNull(type, "type == null");
checkNotNull(annotations, "annotations == null");
int start = converterFactories.indexOf(skipPast) + 1;
for (int i = start, count = converterFactories.size(); i < count; i++) {
//获取合适的converters,在构造Retrofit时传入的是GsonConverterFactory,除此之外还可以传入别的工厂,例如xml等等。
Converter<ResponseBody, ?> converter =
converterFactories.get(i).responseBodyConverter(type, annotations, this);
if (converter != null) {
return (Converter<ResponseBody, T>) converter;
}
}
StringBuilder builder = new StringBuilder("Could not locate ResponseBody converter for ")
.append(type)
.append(".\n");
if (skipPast != null) {
builder.append(" Skipped:");
for (int i = 0; i < start; i++) {
builder.append("\n * ").append(converterFactories.get(i).getClass().getName());
}
builder.append('\n');
}
builder.append(" Tried:");
for (int i = start, count = converterFactories.size(); i < count; i++) {
builder.append("\n * ").append(converterFactories.get(i).getClass().getName());
}
throw new IllegalArgumentException(builder.toString());
}
网络请求
在Retrofit.Create方法中,执行完loadServiceMethod方法后,接着创建OkHttpCall实例,代码如下所示:
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,封装OkHttp用于网络请求。
OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);
//返回Call
return serviceMethod.adapt(okHttpCall);
}
});
}
在使用中,有同步网络请求和异步网络请求。
同步网络请求代码实例:
var wordData = wordService.getWord(map).execute() as Response<WordData>
execute方法在OkHttpCall中的源码如下所示:
@Override public Response<T> execute() throws IOException {
okhttp3.Call call;
synchronized (this) {
if (executed) throw new IllegalStateException("Already executed.");
executed = true;
if (creationFailure != null) {
if (creationFailure instanceof IOException) {
throw (IOException) creationFailure;
} else if (creationFailure instanceof RuntimeException) {
throw (RuntimeException) creationFailure;
} else {
throw (Error) creationFailure;
}
}
call = rawCall;
if (call == null) {
try {
//封装OkHttp,createRawCall的源码在后面
call = rawCall = createRawCall();
} catch (IOException | RuntimeException | Error e) {
throwIfFatal(e); // Do not assign a fatal error to creationFailure.
creationFailure = e;
throw e;
}
}
}
if (canceled) {
call.cancel();
}
//执行同步网络请求,并进行返回数据解析
return parseResponse(call.execute());
}
Response<T> parseResponse(okhttp3.Response rawResponse) throws IOException {
ResponseBody rawBody = rawResponse.body();
rawResponse = rawResponse.newBuilder()
.body(new NoContentResponseBody(rawBody.contentType(), rawBody.contentLength()))
.build();
int code = rawResponse.code();
if (code < 200 || code >= 300) {
try {
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) {
catchingBody.throwIfCaught();
throw e;
}
}
使用异步网络请求,代码示例如下所示:
wordService.getWord(map).enqueue(object :Callback<WordData> {
override fun onFailure(call: Call<WordData>, t: Throwable) {
}
override fun onResponse(call: Call<WordData>, response: Response<WordData>) {
Log.d(tag,response.body().toString())
}
})
enqueue方法,在OkHttpCall中的源码如下所示:
@Override public void enqueue(final Callback<T> callback) {
checkNotNull(callback, "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 {
//封装OkHttp,createRawCall的源码在后面
call = rawCall = createRawCall();
} catch (Throwable t) {
throwIfFatal(t);
failure = creationFailure = t;
}
}
}
if (failure != null) {
callback.onFailure(this, failure);
return;
}
if (canceled) {
call.cancel();
}
//OkHttp新建线程池进行网络请求
call.enqueue(new okhttp3.Callback() {
@Override public void onResponse(okhttp3.Call call, okhttp3.Response rawResponse) {
Response<T> response;
try {
response = parseResponse(rawResponse);
} catch (Throwable e) {
callFailure(e);
return;
}
try {
callback.onResponse(OkHttpCall.this, response);
} catch (Throwable t) {
t.printStackTrace();
}
}
@Override public void onFailure(okhttp3.Call call, IOException e) {
callFailure(e);
}
private void callFailure(Throwable e) {
try {
callback.onFailure(OkHttpCall.this, e);
} catch (Throwable t) {
t.printStackTrace();
}
}
});
}
Response<T> parseResponse(okhttp3.Response rawResponse) throws IOException {
ResponseBody rawBody = rawResponse.body();
rawResponse = rawResponse.newBuilder()
.body(new NoContentResponseBody(rawBody.contentType(), rawBody.contentLength()))
.build();
int code = rawResponse.code();
if (code < 200 || code >= 300) {
try {
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) {
catchingBody.throwIfCaught();
throw e;
}
}
execute方法和enqueue方法都调用了createRawCall方法,该方法主要是完成OkHttp的封装,使用工厂方法新建一个OkHttp并返回。
private okhttp3.Call createRawCall() throws IOException {
okhttp3.Call call = serviceMethod.toCall(args);
if (call == null) {
throw new NullPointerException("Call.Factory returned null.");
}
return call;
}
首先使用RequestBuilder将Http请求封装起来,再传给默认的OkHttpFactory,或者设置的OkHttpFactory,默认情况下是OkHttpClient。
okhttp3.Call toCall(@Nullable Object... args) throws IOException {
//创建RequestBuilder对象封装Http请求信息。
RequestBuilder requestBuilder = new RequestBuilder(httpMethod, baseUrl, relativeUrl, headers,
contentType, hasBody, isFormEncoded, isMultipart);
@SuppressWarnings("unchecked") // It is an error to invoke a method with the wrong arg types.
ParameterHandler<Object>[] handlers = (ParameterHandler<Object>[]) parameterHandlers;
int argumentCount = args != null ? args.length : 0;
if (argumentCount != handlers.length) {
throw new IllegalArgumentException("Argument count (" + argumentCount
+ ") doesn't match expected count (" + handlers.length + ")");
}
for (int p = 0; p < argumentCount; p++) {
handlers[p].apply(requestBuilder, args[p]);
}
//默认情况下callFactory是OkHttpClient
return callFactory.newCall(requestBuilder.build());
}
在OkHttpClient的newCall中,最后调用了RealCall.newRealCall。
@Override public Call newCall(Request request) {
return RealCall.newRealCall(this, request, false /* for web socket */);
}
最后返回serviceMethod.adapt(okHttpCall),adapt方法源码如下所示:
T adapt(Call<R> call) {
//从callAdapter中获取call
return callAdapter.adapt(call);
}
从之前继承自Platform的Android类中以及Retrofit.build()方法可以看出,默认情况创建Retrofit时创建ExecutorCallAdapterFactory,代码如下所示:
static class Android extends Platform {
@Override public Executor defaultCallbackExecutor() {
return new MainThreadExecutor();
}
@Override CallAdapter.Factory defaultCallAdapterFactory(@Nullable Executor callbackExecutor) {
if (callbackExecutor == null) throw new AssertionError();
return new ExecutorCallAdapterFactory(callbackExecutor);
}
static class MainThreadExecutor implements Executor {
private final Handler handler = new Handler(Looper.getMainLooper());
@Override public void execute(Runnable r) {
handler.post(r);
}
}
}
ExecutorCallAdapterFactory的get()方法就是在创建合适callAdapterFactory时调用的方法,该方法返回一个匿名内部类,最后调用该匿名内部类的adapt方法,返回call。
final class ExecutorCallAdapterFactory extends CallAdapter.Factory {
final Executor callbackExecutor;
ExecutorCallAdapterFactory(Executor callbackExecutor) {
this.callbackExecutor = callbackExecutor;
}
@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;
}
//serviceMethod.adapt(okHttpCall)最后调用的是该方法。
@Override public Call<Object> adapt(Call<Object> call) {
return new ExecutorCallbackCall<>(callbackExecutor, call);
}
};
}
static final class ExecutorCallbackCall<T> implements Call<T> {
final Executor callbackExecutor;
final Call<T> delegate;
ExecutorCallbackCall(Executor callbackExecutor, Call<T> delegate) {
//默认的callbackExecutor,是MainThreadExecutor。
this.callbackExecutor = callbackExecutor;
//调用adapt传进来的call,也就是OkHttpCall
this.delegate = delegate;
}
@Override public void enqueue(final Callback<T> callback) {
checkNotNull(callback, "callback == null");
//调用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()) {
callback.onFailure(ExecutorCallbackCall.this, new IOException("Canceled"));
} else {
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();
}
//直接调用OkHttpCall的execute()方法。
@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();
}
}
}
网友评论