Retrofit是一个不错的网络请求库,用官方本身的介绍就是:java
A type-safe REST client for Android and Javaandroid
看官网的介绍用起来很省事,不过若是不了解它是怎么实现的也不太敢用,否则出问题了就不知道怎么办了。这几天比较闲就下下来看了一下,了解一下大概实现方法,细节就不追究了。先来看一个官网的例子,详细说明去网官看git
首先定义请求接口,即程序中都须要什么请求操做github
public interface GitHubService { @GET("/users/{user}/repos") List<Repo> listRepos(@Path("user") String user); }
而后经过RestAdapter
生成一个刚才定义的接口的实现类,使用的是动态代理。api
RestAdapter restAdapter = new RestAdapter.Builder() .setEndpoint("https://api.github.com") .build(); GitHubService service = restAdapter.create(GitHubService.class);
如今就能够调用接口进行请求了数组
List<Repo> repos = service.listRepos("octocat");
使用就是这么简单,请求时直接调用接口就好了,甚至不用封装参数,由于参数的信息已经在定义接口时经过Annotation定义好了。缓存
从上面的例子能够看到接口直接返回了须要的Java类型,而不是byte[]或String,解析数据的地方就是Converter
,这个是能够自定义的,默认是用Gson
解析,也就是说默认认为服务器返回的是Json数据,能够经过指定不一样的Convert
使用不一样的解析方法,如用Jackson
解析Json,或自定义XmlConvert解析xml数据。服务器
Retrofit的使用就是如下几步:网络
RestAdapter
生成一个接口的实现类(动态代理)接口的定义要用用Rtrofit定义的一些Annotation,因此先看一下Annotation的。数据结构
以上面的示例中的接口来看
@GET("/group/{id}/users") List<User> groupList(@Path("id") int groupId);
先看@GET
/** Make a GET request to a REST path relative to base URL. */ @Documented @Target(METHOD) @Retention(RUNTIME) @RestMethod("GET") public @interface GET { String value(); }
@GET自己也被几个Anotation注解,@Target表示@GET注解是用于方法的,value方法就返回这个注解的value值,在上例中就是/group/{id}/users,而后就是@RestMethod
@Documented @Target(ANNOTATION_TYPE) @Retention(RUNTIME) public @interface RestMethod { String value(); boolean hasBody() default false; }
RestMethod
是一个用于Annotation的Annotation,好比上面的例子中用来注解的@GET,value方法就返回GET,hasBody表示是否有Body,对于POST这个方法就返回true
@Documented @Target(METHOD) @Retention(RUNTIME) @RestMethod(value = "POST", hasBody = true) public @interface POST { String value(); }
Retrofit的Annotation包含请求方法相关的@GET、@POST、@HEAD、@PUT、@DELETA、@PATCH,和参数相关的@Path、@Field、@Multipart等。
定义了Annotation要就有解析它的方法,在Retrofit中解析的位置就是RestMethodInfo
,但在这以前须要先看哪里使用了RestMethodInfo
,前面说了Retrofit使用了动态代理生成了咱们定义的接口的实现类,而这个实现类是经过RestAdapter.create
返回的,因此使用动态代理的位置就是RestAdapter
,接下来就看一下RestAdapter
。
RestAdapter restAdapter = new RestAdapter.Builder() .setEndpoint("https://api.github.com") .build(); GitHubService service = restAdapter.create(GitHubService.class); public RestAdapter build() { if (endpoint == null) { throw new IllegalArgumentException("Endpoint may not be null."); } ensureSaneDefaults(); return new RestAdapter(endpoint, clientProvider, httpExecutor, callbackExecutor, requestInterceptor, converter, profiler, errorHandler, log, logLevel); }
setEndPoint
就不说了,接口中定义的都是相对Url,EndPoint就是域名,build
方法调用ensureSaneDefaults()
方法,而后就构造了一个RestAdapter对象,构造函数的参数中传入了EndPoint外的几个对象,这几个对象就是在ensureSaneDefaults()
中初始化的。
private void ensureSaneDefaults() { if (converter == null) { converter = Platform.get().defaultConverter(); } if (clientProvider == null) { clientProvider = Platform.get().defaultClient(); } if (httpExecutor == null) { httpExecutor = Platform.get().defaultHttpExecutor(); } if (callbackExecutor == null) { callbackExecutor = Platform.get().defaultCallbackExecutor(); } if (errorHandler == null) { errorHandler = ErrorHandler.DEFAULT; } if (log == null) { log = Platform.get().defaultLog(); } if (requestInterceptor == null) { requestInterceptor = RequestInterceptor.NONE; } }
ensureSaneDefaults()
中初始化了不少成员,errorHandler、log就不看了,其余的除了requestInterceptor
都是经过Platform
对象得到的,因此要先看下Platform
private static final Platform PLATFORM = findPlatform(); static final boolean HAS_RX_JAVA = hasRxJavaOnClasspath(); static Platform get() { return PLATFORM; } private static Platform findPlatform() { try { Class.forName("android.os.Build"); if (Build.VERSION.SDK_INT != 0) { return new Android(); } } catch (ClassNotFoundException ignored) { } if (System.getProperty("com.google.appengine.runtime.version") != null) { return new AppEngine(); } return new Base(); }
使用了单例的PLATFORM
,经过findPlatform()
初始化实例,若是是Android平台就使用Platform.Android
,若是是Google AppEngine就使用Platform.AppEngine
,不然使用Platform.Base
,这些都是Platform
的子类,其中AppEngine
又是Base
的子类。
Platform
是一个抽象类,定义了如下几个抽象方法,这几个方法的做用就是返回一些RestAdapter
中须要要用到成员的默认实现
abstract Converter defaultConverter(); // 默认的Converter,用于将请求结果转化成须要的数据,如GsonConverter将JSON请求结果用Gson解析成Java对象 abstract Client.Provider defaultClient(); // Http请求类,若是是AppEngine就使用`UrlFetchClient`,不然若是有OKHttp就使用OKHttp,若是是Android,2.3之后使用HttpURLConnection,2.3之前使用HttpClient abstract Executor defaultHttpExecutor(); // 用于执行Http请求的Executor abstract Executor defaultCallbackExecutor(); // Callback调用中用于执行Callback的Executor(多是同步的) abstract RestAdapter.Log defaultLog(); // Log接口,用于输出Log
Platform
的接口再看
ensureSaneDefaults
就清楚了,初始化转化数据的Converter、执行请求的Client、执行请求的Executor、执行Callback的Executor、Log输出类、错误处理类和用于在请求前添加额外处理的拦截请求的Interceptor。
Converter
默认都是用的GsonConverter
,就不看了,defaultClient
返回执行网络请求的Client
Platform.Android
@Override Client.Provider defaultClient() { final Client client; if (hasOkHttpOnClasspath()) { client = OkClientInstantiator.instantiate(); } else if (Build.VERSION.SDK_INT < Build.VERSION_CODES.GINGERBREAD) { client = new AndroidApacheClient(); } else { client = new UrlConnectionClient(); } return new Client.Provider() { @Override public Client get() { return client; } }; }
@Override Client.Provider defaultClient() { final Client client; if (hasOkHttpOnClasspath()) { client = OkClientInstantiator.instantiate(); } else { client = new UrlConnectionClient(); } return new Client.Provider() { @Override public Client get() { return client; } }; }
Platform.AppEngine
@Override Client.Provider defaultClient() { final UrlFetchClient client = new UrlFetchClient(); return new Client.Provider() { @Override public Client get() { return client; } }; }
对于Android,优先使用OKHttp,不然2.3之后使用HttpUrlConnection,2.3之前使用HttpClient
defaultHttpExecutor
就是返回一个Executor,执行请求的线程在这个Executor中执行,就作了一件事,把线程设置为后台线程
defaultCallbackExecutor
用于执行Callback类型的请求时,提供一个Executor执行Callback的Runnable
Platform.Base
@Override Executor defaultCallbackExecutor() { return new Utils.SynchronousExecutor(); }
Platform.Android
@Override Executor defaultCallbackExecutor() { return new MainThreadExecutor(); }
SynchronousExecutor
static class SynchronousExecutor implements Executor { @Override public void execute(Runnable runnable) { runnable.run(); } }
public final class MainThreadExecutor implements Executor { private final Handler handler = new Handler(Looper.getMainLooper()); @Override public void execute(Runnable r) { handler.post(r); } }
Platform
看完了,RestAdapter的成员初始化完成,就要看怎么经过RestAdapter.create
生成咱们定义的接口的实现类了
public <T> T create(Class<T> service) { Utils.validateServiceClass(service); return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[] { service }, new RestHandler(getMethodInfoCache(service))); } Map<Method, RestMethodInfo> getMethodInfoCache(Class<?> service) { synchronized (serviceMethodInfoCache) { Map<Method, RestMethodInfo> methodInfoCache = serviceMethodInfoCache.get(service); if (methodInfoCache == null) { methodInfoCache = new LinkedHashMap<Method, RestMethodInfo>(); serviceMethodInfoCache.put(service, methodInfoCache); } return methodInfoCache; } }
使用了动态代理,InvocationHandler
是RestHandler
,RestHandler
有一个参数,是Method
->RestMethodInfo
的映射,初始化时这个映射是空的。重点就是这两个了:RestHandler
,RestMethodInfo
,
@Override public Object invoke(Object proxy, Method method, final Object[] args) throws Throwable { // If the method is a method from Object then defer to normal invocation. if (method.getDeclaringClass() == Object.class) { // 1 return method.invoke(this, args); } // Load or create the details cache for the current method. final RestMethodInfo methodInfo = getMethodInfo(methodDetailsCache, method); // 2 if (methodInfo.isSynchronous) { // 3 try { return invokeRequest(requestInterceptor, methodInfo, args); } catch (RetrofitError error) { Throwable newError = errorHandler.handleError(error); if (newError == null) { throw new IllegalStateException("Error handler returned null for wrapped exception.", error); } throw newError; } } if (httpExecutor == null || callbackExecutor == null) { throw new IllegalStateException("Asynchronous invocation requires calling setExecutors."); } // Apply the interceptor synchronously, recording the interception so we can replay it later. // This way we still defer argument serialization to the background thread. final RequestInterceptorTape interceptorTape = new RequestInterceptorTape(); requestInterceptor.intercept(interceptorTape); // 4 if (methodInfo.isObservable) { // 5 if (rxSupport == null) { if (Platform.HAS_RX_JAVA) { rxSupport = new RxSupport(httpExecutor, errorHandler); } else { throw new IllegalStateException("Observable method found but no RxJava on classpath"); } } return rxSupport.createRequestObservable(new Callable<ResponseWrapper>() { @Override public ResponseWrapper call() throws Exception { return (ResponseWrapper) invokeRequest(interceptorTape, methodInfo, args); } }); } Callback<?> callback = (Callback<?>) args[args.length - 1]; // 6 httpExecutor.execute(new CallbackRunnable(callback, callbackExecutor, errorHandler) { @Override public ResponseWrapper obtainResponse() { return (ResponseWrapper) invokeRequest(interceptorTape, methodInfo, args); } }); return null; // Asynchronous methods should have return type of void. }
执行请求时会调用RestHandler
的invoke
方法,如上所示,主要是上面代码中标注有6点
getMethodInfo
获取调用的Method
对应的RestMethodInfo
,前面说了,构造RestHandler
对象时传进来了一个Method
->RestMethodInfo
的映射,初始时是空的。static RestMethodInfo getMethodInfo(Map<Method, RestMethodInfo> cache, Method method) { synchronized (cache) { RestMethodInfo methodInfo = cache.get(method); if (methodInfo == null) { methodInfo = new RestMethodInfo(method); cache.put(method, methodInfo); } return methodInfo; }
在getMethodInfo
中判断若是相应的映射不存在,就创建这个映射,并如名字所示缓存起来
3. 若是是同步调用(接口中直接返回数据,不经过Callback或Observe),直接调用invokeRequest
4. 若是是非同步调用,先经过RequestInterceptorTape
记录拦截请求,记录后在后台线程作实际拦截,后面会提到。
5. 若是是Observe请求(RxJava),执行第5步,对RxJava不了解,略过
6. 若是是Callback形式,交由线程池执行
接口中的每个Method有一个对应的RestMethodInfo,关于接口中Annotation信息的处理就都在这里了
private enum ResponseType { VOID, OBSERVABLE, OBJECT } RestMethodInfo(Method method) { this.method = method; responseType = parseResponseType(); isSynchronous = (responseType == ResponseType.OBJECT); isObservable = (responseType == ResponseType.OBSERVABLE); }
在构造函数中调用了parseResponseType
,parseResponseType
解析了方法签名,根据方法的返回值类型及最后一个参数的类型判断方法的类型是哪一种ResponseType
不管是哪一种ResponseType,最终都是调用invokeRequest
执行实际的请求,接下来依次看下invokeRequest
的执行步骤
第一步是调用methodInfo.init()
解析调用的方法,方法里有作判断,只在第一次调用时解析,由于处一次解析后这个对象就被缓存起来了,下次调同一个方法时能够直接使用
synchronized void init() { if (loaded) return; parseMethodAnnotations(); parseParameters(); loaded = true; }
在RestMethodInfo.init
中分别调用
parseMethodAnnotations()
:解析全部方法的AnnotationparseParameters()
:解析全部参数的Annotationfor (Annotation methodAnnotation : method.getAnnotations()) { Class<? extends Annotation> annotationType = methodAnnotation.annotationType(); RestMethod methodInfo = null; // Look for a @RestMethod annotation on the parameter annotation indicating request method. for (Annotation innerAnnotation : annotationType.getAnnotations()) { if (RestMethod.class == innerAnnotation.annotationType()) { methodInfo = (RestMethod) innerAnnotation; break; } } ... }
在parseMethodAnnotations
中,会获取方法全部的Annotation并遍历:
parsePath
解析请求的Url,requestParam(URL中问号后的内容)及Url中须要替换的参数名(Url中大括号括起来的部分)parseParameters
解析请求参数,即参数的Annotation,@PATH
、@HEADER
、@FIELD
等
第二步是RequestBuilder和Interceptor,这两个是有关联的,因此一块儿看。
RequestBuilder requestBuilder = new RequestBuilder(serverUrl, methodInfo, converter); requestBuilder.setArguments(args); requestInterceptor.intercept(requestBuilder); Request request = requestBuilder.build();
先说RequestInterceptor,做用很明显,当执行请求时拦截请求以作一些特殊处理,好比添加一些额外的请求参数。
/** Intercept every request before it is executed in order to add additional data. */ public interface RequestInterceptor { /** Called for every request. Add data using methods on the supplied {@link RequestFacade}. */ void intercept(RequestFacade request); interface RequestFacade { void addHeader(String name, String value); void addPathParam(String name, String value); void addEncodedPathParam(String name, String value); void addQueryParam(String name, String value); void addEncodedQueryParam(String name, String value); } /** A {@link RequestInterceptor} which does no modification of requests. */ RequestInterceptor NONE = new RequestInterceptor() { @Override public void intercept(RequestFacade request) { // Do nothing. } }; }
RequestInterceptor
只有一个方法intercept
,接收一个RequestFacade
参数,RequestFacade
是RequestInterceptor
内部的一个接口,这个接口的方法就是添加请求参数,Query、Header什么的。大概能够看出RequestInterceptor
的做用了,若是RequestFacade
表示一个请求相关的数据,RequestInteceptor.intercept
的做用就是向这个RequestFacade
中添加额外Header,Param等参数。
RequestFacade
的一个子类叫RequestBuilder
,用来处理Request
请求参数,在invokeRequest
中会对RequestBuilder
调用intercept
方法向RequestBuilder
添加额外的参数。
有一个叫RequestInterceptorTape
的类,同时实现了RequestFacade
与RequestInterceptor
,它的做用是:
RequestFacade
使用时做为参数传给一个RequestInteceptor
,这个RequestInterceptor
调用它的addHeader
等方法时,它把这些调用及参数记录下来RequestInterceptor
使用时,将以前记录的方法调用及参数从新应用到它的intercept
参数RequestFacade
中在RestHandler.invoke
中,若是判断方法的调用不是同步调用,就经过下面的两行代码将用户设置的interceptor须要添加的参数记录到RequestInterceptorTape
,而后在invokeRequest
中再实际执行参数的添加。
// Apply the interceptor synchronously, recording the interception so we can replay it later. // This way we still defer argument serialization to the background thread. final RequestInterceptorTape interceptorTape = new RequestInterceptorTape(); requestInterceptor.intercept(interceptorTape);
RequestBuilder.setArguments()
解析调用接口时的实际参数。而后经过build()
方法生成一个Request
对象
第三步执行请求,Response response = clientProvider.get().execute(request);
第四步就是解析并分发请求结果了,成功请求时返回结果,解析失败调用ErrorHandler
给用户一个自定义异常的机会,但最终都是经过异常抛出到invoke()
中的,若是是同步调用,直接抛异常,若是是Callback调用,会回调Callback.failure
请求类型有同步请求,Callback请求,Observable请求,来看下Callback请求:
Callback<?> callback = (Callback<?>) args[args.length - 1]; httpExecutor.execute(new CallbackRunnable(callback, callbackExecutor, errorHandler) { @Override public ResponseWrapper obtainResponse() { return (ResponseWrapper) invokeRequest(interceptorTape, methodInfo, args); } });
Callback请求中函数最后一个参数是一个Callback的实例,httpExecutor是一个Executor,用于执行Runnable请求,咱们看到,这里new了一个CallbackRunnable执行,并实现了它的obtainResponse方法,看实现:
abstract class CallbackRunnable<T> implements Runnable { private final Callback<T> callback; private final Executor callbackExecutor; private final ErrorHandler errorHandler; CallbackRunnable(Callback<T> callback, Executor callbackExecutor, ErrorHandler errorHandler) { this.callback = callback; this.callbackExecutor = callbackExecutor; this.errorHandler = errorHandler; } @SuppressWarnings("unchecked") @Override public final void run() { try { final ResponseWrapper wrapper = obtainResponse(); callbackExecutor.execute(new Runnable() { @Override public void run() { callback.success((T) wrapper.responseBody, wrapper.response); } }); } catch (RetrofitError e) { Throwable cause = errorHandler.handleError(e); final RetrofitError handled = cause == e ? e : unexpectedError(e.getUrl(), cause); callbackExecutor.execute(new Runnable() { @Override public void run() { callback.failure(handled); } }); } } public abstract ResponseWrapper obtainResponse(); }
就是一个普通的Runnable,在run方法中首先执行obtailResponse,从名字能够看到是执行请求返回Response,这个从前面能够看到执行了invokeRequest,和同步调用中同样执行请求。
紧接着就提交了一个Runnable至callbackExecutor,在看Platform
时看到了callbackExecotor是经过Platform.get().defaultCallbackExecutor()
返回的,Android中是向主线程的一个Handler发消息
值得注意的事,对于同步调用,若是遇到错误是直接抛异常,而对于异步调用,是调用Callback.failure()
执行网络请求,须要向服务端发送请求参数,如表单数据,上传的文件等,一样须要解析服务端返回的数据,在Retrofit中对这些作了封装,位于Mime包中,也只有封装了,才好统一由指定的Converter执行数据的转换
TypedInput
和TypedOutput
表示输入输出的数据,都包含mimeType,并分别支持读入一个InputStream或写到一个OutputStrem
/** * Binary data with an associated mime type. * * @author Jake Wharton (jw@squareup.com) */ public interface TypedInput { /** Returns the mime type. */ String mimeType(); /** Length in bytes. Returns {@code -1} if length is unknown. */ long length(); /** * Read bytes as stream. Unless otherwise specified, this method may only be called once. It is * the responsibility of the caller to close the stream. */ InputStream in() throws IOException; } /** * Binary data with an associated mime type. * * @author Bob Lee (bob@squareup.com) */ public interface TypedOutput { /** Original filename. * * Used only for multipart requests, may be null. */ String fileName(); /** Returns the mime type. */ String mimeType(); /** Length in bytes or -1 if unknown. */ long length(); /** Writes these bytes to the given output stream. */ void writeTo(OutputStream out) throws IOException; }
TypedByteArray
,内部数据是一个Byte数组
private final byte[] bytes; @Override public long length() { return bytes.length; } @Override public void writeTo(OutputStream out) throws IOException { out.write(bytes); } @Override public InputStream in() throws IOException { return new ByteArrayInputStream(bytes); }
TypedString
,继承自TypedByteArray
,内部表示是同样的
public TypedString(String string) { super("text/plain; charset=UTF-8", convertToBytes(string)); } private static byte[] convertToBytes(String string) { try { return string.getBytes("UTF-8"); } catch (UnsupportedEncodingException e) { throw new RuntimeException(e); } }
其余的也同样,从名字很好理解:TypedFile
,MultipartTypedOutput
,FormEncodedTypedOutput
。
Retrofit对输入和输出作了封装,经过TypedOutput
向服务器发送数据,经过TypedInput
读取服务器返回的数据。
经过MultipartTypedOutput
支持文件上传,读取服务器数据时,若是要求直接返回未解析的Response,Restonse会被转换为TypedByteArray,因此不能是大文件类的
Retrofit支持不一样的Log等级,当为LogLevel.Full时会把Request及Response的Body打印出来,因此若是包含文件就不行了。
Retrofit默认使用GsonConverter,因此要想获取原始数据不要Retrofit解析,要么自定义Conveter,要么直接返回Response了,返回Response也比较麻烦
整体来讲Retrofit看起来很好用,不过要求服务端返回数据最好要规范,否则若是请求成功返回一种数据结构,请求失败返回另外一种数据结构,很差用Converter解析,接口的定义也很差定义,除非都返回Response,或自定义Converter全部接口都返回String
在Twitter上JakeWharton这么说:
Gearing up towards a Retrofit 1.6.0 release and then branching 1.x so we can push master towards a 2.0 and fix long-standing design issues.
要出2.0了,内部API会改,接口应该不怎么变