Android app開(kāi)發(fā)中Retrofit框架的初步上手使用
Retrofit 2.0
先來(lái)說(shuō)一下Retrofit 2.0版本中一些引人注意的地方。
在Retrofit 2.0中,最大的改動(dòng)莫過(guò)于減小庫(kù)的體積,首先,Retrofit 2.0去掉了對(duì)所有的HTTP客戶端的兼容,而鐘情于OkHttpClient一個(gè),極大地減少了各種適配代碼,原因一會(huì)兒說(shuō);其次,拆庫(kù),比如將對(duì)RxJava的支持設(shè)置為可選(需要額外引入庫(kù));再比如將各個(gè)序列化反序列化轉(zhuǎn)換器支持設(shè)置為可選(需要額外引入庫(kù))。于2.0拋棄HttpClient和HttpURLConnection,為了減小庫(kù)體積是一方面,另外一個(gè)重要的原因作為一個(gè)專門(mén)為Android&Java 應(yīng)用量身打造的Http棧,OkHttpClient越來(lái)越受到各個(gè)大的開(kāi)源項(xiàng)目的青睞,畢竟它本身就是開(kāi)源的。
此外,OkHttpClient與HttpClient相比,它最大的改進(jìn)是自帶工作線程池,所以上層應(yīng)用無(wú)需自己去維護(hù)復(fù)雜的并發(fā)模型,而HttpClient僅僅提供了一個(gè)線程安全的類,所以還需要上層應(yīng)用去處理并發(fā)的邏輯(實(shí)際上Volley一部分工作就是干這個(gè))。從這個(gè)角度來(lái)說(shuō),Retrofit得益于OkHttpClient的優(yōu)勢(shì),較之于Volley是一種更加先進(jìn)的網(wǎng)絡(luò)框架。
由于Retrofit不需要去關(guān)心并發(fā)工作線程的維護(hù),所以它可以全力關(guān)注于如何精簡(jiǎn)發(fā)送一個(gè)請(qǐng)求的代價(jià),實(shí)際上使用Retrofit發(fā)送一個(gè)請(qǐng)求實(shí)在是太easy了!
簡(jiǎn)單示例
首先定義請(qǐng)求接口,即程序中都需要什么請(qǐng)求操作
public interface GitHubService { @GET("/users/{user}/repos") List<Repo> listRepos(@Path("user") String user); }
然后通過(guò)RestAdapter生成一個(gè)剛才定義的接口的實(shí)現(xiàn)類,使用的是動(dòng)態(tài)代理。
RestAdapter restAdapter = new RestAdapter.Builder() .setEndpoint("https://api.github.com") .build(); GitHubService service = restAdapter.create(GitHubService.class);
現(xiàn)在就可以調(diào)用接口進(jìn)行請(qǐng)求了
List<Repo> repos = service.listRepos("octocat");
使用就是這么簡(jiǎn)單,請(qǐng)求時(shí)直接調(diào)用接口就行了,甚至不用封裝參數(shù),因?yàn)閰?shù)的信息已經(jīng)在定義接口時(shí)通過(guò)Annotation定義好了。
從上面的例子可以看到接口直接返回了需要的Java類型,而不是byte[]或String,解析數(shù)據(jù)的地方就是Converter,這個(gè)是可以自定義的,默認(rèn)是用Gson解析,也就是說(shuō)默認(rèn)認(rèn)為服務(wù)器返回的是Json數(shù)據(jù),可以通過(guò)指定不同的Convert使用不同的解析方法,如用Jackson解析Json,或自定義XmlConvert解析xml數(shù)據(jù)。
Retrofit的使用就是以下幾步:
- 定義接口,參數(shù)聲明,Url都通過(guò)Annotation指定
- 通過(guò)RestAdapter生成一個(gè)接口的實(shí)現(xiàn)類(動(dòng)態(tài)代理)
- 調(diào)用接口請(qǐng)求數(shù)據(jù)
- 接口的定義要用用Rtrofit定義的一些Annotation,所以先看一下Annotation的。
Annotation
以上面的示例中的接口來(lái)看
@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本身也被幾個(gè)Anotation注解,@Target表示@GET注解是用于方法的,value方法就返回這個(gè)注解的value值,在上例中就是/group/{id}/users,然后就是@RestMethod
@Documented @Target(ANNOTATION_TYPE) @Retention(RUNTIME) public @interface RestMethod { String value(); boolean hasBody() default false; }
RestMethod是一個(gè)用于Annotation的Annotation,比如上面的例子中用來(lái)注解的@GET,value方法就返回GET,hasBody表示是否有Body,對(duì)于POST這個(gè)方法就返回true
@Documented @Target(METHOD) @Retention(RUNTIME) @RestMethod(value = "POST", hasBody = true) public @interface POST { String value(); }
Retrofit的Annotation包含請(qǐng)求方法相關(guān)的@GET、@POST、@HEAD、@PUT、@DELETA、@PATCH,和參數(shù)相關(guān)的@Path、@Field、@Multipart等。
定義了Annotation要就有解析它的方法,在Retrofit中解析的位置就是RestMethodInfo,但在這之前需要先看哪里使用了RestMethodInfo,前面說(shuō)了Retrofit使用了動(dòng)態(tài)代理生成了我們定義的接口的實(shí)現(xiàn)類,而這個(gè)實(shí)現(xiàn)類是通過(guò)RestAdapter.create返回的,所以使用動(dòng)態(tài)代理的位置就是RestAdapter,接下來(lái)就看一下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就不說(shuō)了,接口中定義的都是相對(duì)Url,EndPoint就是域名,build方法調(diào)用ensureSaneDefaults()方法,然后就構(gòu)造了一個(gè)RestAdapter對(duì)象,構(gòu)造函數(shù)的參數(shù)中傳入了EndPoint外的幾個(gè)對(duì)象,這幾個(gè)對(duì)象就是在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都是通過(guò)Platform對(duì)象獲得的,所以要先看下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,通過(guò)findPlatform()初始化實(shí)例,如果是Android平臺(tái)就使用Platform.Android,如果是Google AppEngine就使用Platform.AppEngine,否則使用Platform.Base,這些都是Platform的子類,其中AppEngine又是Base的子類。
Platform是一個(gè)抽象類,定義了以下幾個(gè)抽象方法,這幾個(gè)方法的作用就是返回一些RestAdapter中需要要用到成員的默認(rèn)實(shí)現(xiàn)
abstract Converter defaultConverter(); // 默認(rèn)的Converter,用于將請(qǐng)求結(jié)果轉(zhuǎn)化成需要的數(shù)據(jù),如GsonConverter將JSON請(qǐng)求結(jié)果用Gson解析成Java對(duì)象 abstract Client.Provider defaultClient(); // Http請(qǐng)求類,如果是AppEngine就使用`UrlFetchClient`,否則如果有OKHttp就使用OKHttp,如果是Android,2.3以后使用HttpURLConnection,2.3以前使用HttpClient abstract Executor defaultHttpExecutor(); // 用于執(zhí)行Http請(qǐng)求的Executor abstract Executor defaultCallbackExecutor(); // Callback調(diào)用中用于執(zhí)行Callback的Executor(可能是同步的) abstract RestAdapter.Log defaultLog(); // Log接口,用于輸出Log
看完P(guān)latform的接口再看ensureSaneDefaults就清楚了,初始化轉(zhuǎn)化數(shù)據(jù)的Converter、執(zhí)行請(qǐng)求的Client、執(zhí)行請(qǐng)求的Executor、執(zhí)行Callback的Executor、Log輸出類、錯(cuò)誤處理類和用于在請(qǐng)求前添加額外處理的攔截請(qǐng)求的Interceptor。
Converter默認(rèn)都是用的GsonConverter,就不看了,defaultClient返回執(zhí)行網(wǎng)絡(luò)請(qǐng)求的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; } }; }
Platform.Base
@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; } }; }
對(duì)于Android,優(yōu)先使用OKHttp,否則2.3以后使用HttpUrlConnection,2.3以前使用HttpClient
defaultHttpExecutor就是返回一個(gè)Executor,執(zhí)行請(qǐng)求的線程在這個(gè)Executor中執(zhí)行,就做了一件事,把線程設(shè)置為后臺(tái)線程
defaultCallbackExecutor用于執(zhí)行Callback類型的請(qǐng)求時(shí),提供一個(gè)Executor執(zhí)行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(); } }
MainThreadExecutor
public final class MainThreadExecutor implements Executor { private final Handler handler = new Handler(Looper.getMainLooper()); @Override public void execute(Runnable r) { handler.post(r); } }
如果是Android,通過(guò)Handler將回調(diào)發(fā)送到主線程執(zhí)行,如果非Android,直接同步執(zhí)行。
Platform看完了,RestAdapter的成員初始化完成,就要看怎么通過(guò)RestAdapter.create生成我們定義的接口的實(shí)現(xiàn)類了
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; } }
使用了動(dòng)態(tài)代理,InvocationHandler是RestHandler,RestHandler有一個(gè)參數(shù),是Method->RestMethodInfo的映射,初始化時(shí)這個(gè)映射是空的。重點(diǎn)就是這兩個(gè)了: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. }
執(zhí)行請(qǐng)求時(shí)會(huì)調(diào)用RestHandler的invoke方法,如上所示,主要是上面代碼中標(biāo)注有6點(diǎn)
如果調(diào)用的是Object的方法,不做處理直接調(diào)用。
通過(guò)getMethodInfo獲取調(diào)用的Method對(duì)應(yīng)的RestMethodInfo,前面說(shuō)了,構(gòu)造RestHandler對(duì)象時(shí)傳進(jìn)來(lái)了一個(gè)Method->RestMethodInfo的映射,初始時(shí)是空的。
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中判斷如果相應(yīng)的映射不存在,就建立這個(gè)映射,并如名字所示緩存起來(lái)
- 如果是同步調(diào)用(接口中直接返回?cái)?shù)據(jù),不通過(guò)Callback或Observe),直接調(diào)用invokeRequest
- 如果是非同步調(diào)用,先通過(guò)RequestInterceptorTape記錄攔截請(qǐng)求,記錄后在后臺(tái)線程做實(shí)際攔截,后面會(huì)提到。
- 如果是Observe請(qǐng)求(RxJava),執(zhí)行第5步,對(duì)RxJava不了解,略過(guò)
- 如果是Callback形式,交由線程池執(zhí)行
接口中的每一個(gè)Method有一個(gè)對(duì)應(yīng)的RestMethodInfo,關(guān)于接口中Annotation信息的處理就都在這里了
RestMethodInfo
private enum ResponseType { VOID, OBSERVABLE, OBJECT } RestMethodInfo(Method method) { this.method = method; responseType = parseResponseType(); isSynchronous = (responseType == ResponseType.OBJECT); isObservable = (responseType == ResponseType.OBSERVABLE); }
在構(gòu)造函數(shù)中調(diào)用了parseResponseType,parseResponseType解析了方法簽名,根據(jù)方法的返回值類型及最后一個(gè)參數(shù)的類型判斷方法的類型是哪種ResponseType
無(wú)論是哪種ResponseType,最終都是調(diào)用invokeRequest執(zhí)行實(shí)際的請(qǐng)求,接下來(lái)依次看下invokeRequest的執(zhí)行步驟
RestAdapter.invokeRequest
第一步是調(diào)用methodInfo.init()解析調(diào)用的方法,方法里有做判斷,只在第一次調(diào)用時(shí)解析,因?yàn)樘幰淮谓馕龊筮@個(gè)對(duì)象就被緩存起來(lái)了,下次調(diào)同一個(gè)方法時(shí)可以直接使用
synchronized void init() { if (loaded) return; parseMethodAnnotations(); parseParameters(); loaded = true; }
在RestMethodInfo.init中分別調(diào)用
- parseMethodAnnotations():解析所有方法的Annotation
- parseParameters():解析所有參數(shù)的Annotation
for (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中,會(huì)獲取方法所有的Annotation并遍歷:
對(duì)于每一個(gè)Annotation,也會(huì)獲取它的Annotation,看它是否是被RestMethod注解的Annotation,如果是,說(shuō)明是@GET,@POST類型的注解,就調(diào)用parsePath解析請(qǐng)求的Url,requestParam(URL中問(wèn)號(hào)后的內(nèi)容)及Url中需要替換的參數(shù)名(Url中大括號(hào)括起來(lái)的部分)
尋找Headers Annotation解析Header參數(shù)
解析RequestType:SIMPLE,MULTIPART,F(xiàn)ORM_URL_ENCODED
parseParameters解析請(qǐng)求參數(shù),即參數(shù)的Annotation,@PATH、@HEADER、@FIELD等
第二步是RequestBuilder和Interceptor,這兩個(gè)是有關(guān)聯(lián)的,所以一起看。
RequestBuilder requestBuilder = new RequestBuilder(serverUrl, methodInfo, converter); requestBuilder.setArguments(args); requestInterceptor.intercept(requestBuilder); Request request = requestBuilder.build();
先說(shuō)RequestInterceptor,作用很明顯,當(dāng)執(zhí)行請(qǐng)求時(shí)攔截請(qǐng)求以做一些特殊處理,比如添加一些額外的請(qǐng)求參數(shù)。
/** 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只有一個(gè)方法intercept,接收一個(gè)RequestFacade參數(shù),RequestFacade是RequestInterceptor內(nèi)部的一個(gè)接口,這個(gè)接口的方法就是添加請(qǐng)求參數(shù),Query、Header什么的。大概可以看出RequestInterceptor的作用了,如果RequestFacade表示一個(gè)請(qǐng)求相關(guān)的數(shù)據(jù),RequestInteceptor.intercept的作用就是向這個(gè)RequestFacade中添加額外Header,Param等參數(shù)。
RequestFacade的一個(gè)子類叫RequestBuilder,用來(lái)處理Request請(qǐng)求參數(shù),在invokeRequest中會(huì)對(duì)RequestBuilder調(diào)用intercept方法向RequestBuilder添加額外的參數(shù)。
有一個(gè)叫RequestInterceptorTape的類,同時(shí)實(shí)現(xiàn)了RequestFacade與RequestInterceptor,它的作用是:
當(dāng)作為RequestFacade使用時(shí)作為參數(shù)傳給一個(gè)RequestInteceptor,這個(gè)RequestInterceptor調(diào)用它的addHeader等方法時(shí),它把這些調(diào)用及參數(shù)記錄下來(lái)
然后作為RequestInterceptor使用時(shí),將之前記錄的方法調(diào)用及參數(shù)重新應(yīng)用到它的intercept參數(shù)RequestFacade中
在RestHandler.invoke中,如果判斷方法的調(diào)用不是同步調(diào)用,就通過(guò)下面的兩行代碼將用戶設(shè)置的interceptor需要添加的參數(shù)記錄到RequestInterceptorTape,然后在invokeRequest中再實(shí)際執(zhí)行參數(shù)的添加。
// 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()解析調(diào)用接口時(shí)的實(shí)際參數(shù)。然后通過(guò)build()方法生成一個(gè)Request對(duì)象
第三步執(zhí)行請(qǐng)求,Response response = clientProvider.get().execute(request);
第四步就是解析并分發(fā)請(qǐng)求結(jié)果了,成功請(qǐng)求時(shí)返回結(jié)果,解析失敗調(diào)用ErrorHandler給用戶一個(gè)自定義異常的機(jī)會(huì),但最終都是通過(guò)異常拋出到invoke()中的,如果是同步調(diào)用,直接拋異常,如果是Callback調(diào)用,會(huì)回調(diào)Callback.failure
CallbackRunnable
請(qǐng)求類型有同步請(qǐng)求,Callback請(qǐng)求,Observable請(qǐng)求,來(lái)看下Callback請(qǐng)求:
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請(qǐng)求中函數(shù)最后一個(gè)參數(shù)是一個(gè)Callback的實(shí)例,httpExecutor是一個(gè)Executor,用于執(zhí)行Runnable請(qǐng)求,我們看到,這里new了一個(gè)CallbackRunnable執(zhí)行,并實(shí)現(xiàn)了它的obtainResponse方法,看實(shí)現(xiàn):
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(); }
就是一個(gè)普通的Runnable,在run方法中首先執(zhí)行obtailResponse,從名字可以看到是執(zhí)行請(qǐng)求返回Response,這個(gè)從前面可以看到執(zhí)行了invokeRequest,和同步調(diào)用中一樣執(zhí)行請(qǐng)求。
緊接著就提交了一個(gè)Runnable至callbackExecutor,在看Platform時(shí)看到了callbackExecotor是通過(guò)Platform.get().defaultCallbackExecutor()返回的,Android中是向主線程的一個(gè)Handler發(fā)消息
值得注意的事,對(duì)于同步調(diào)用,如果遇到錯(cuò)誤是直接拋異常,而對(duì)于異步調(diào)用,是調(diào)用Callback.failure()
Mime
執(zhí)行網(wǎng)絡(luò)請(qǐng)求,需要向服務(wù)端發(fā)送請(qǐng)求參數(shù),如表單數(shù)據(jù),上傳的文件等,同樣需要解析服務(wù)端返回的數(shù)據(jù),在Retrofit中對(duì)這些做了封裝,位于Mime包中,也只有封裝了,才好統(tǒng)一由指定的Converter執(zhí)行數(shù)據(jù)的轉(zhuǎn)換
TypedInput和TypedOutput表示輸入輸出的數(shù)據(jù),都包含mimeType,并分別支持讀入一個(gè)InputStream或?qū)懙揭粋€(gè)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,內(nèi)部數(shù)據(jù)是一個(gè)Byte數(shù)組 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,內(nèi)部表示是一樣的 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,F(xiàn)ormEncodedTypedOutput。
其他
Retrofit對(duì)輸入和輸出做了封裝,通過(guò)TypedOutput向服務(wù)器發(fā)送數(shù)據(jù),通過(guò)TypedInput讀取服務(wù)器返回的數(shù)據(jù)。
通過(guò)MultipartTypedOutput支持文件上傳,讀取服務(wù)器數(shù)據(jù)時(shí),如果要求直接返回未解析的Response,Restonse會(huì)被轉(zhuǎn)換為T(mén)ypedByteArray,所以不能是大文件類的
Retrofit支持不同的Log等級(jí),當(dāng)為L(zhǎng)ogLevel.Full時(shí)會(huì)把Request及Response的Body打印出來(lái),所以如果包含文件就不行了。
Retrofit默認(rèn)使用GsonConverter,所以要想獲取原始數(shù)據(jù)不要Retrofit解析,要么自定義Conveter,要么直接返回Response了,返回Response也比較麻煩
總體來(lái)說(shuō)Retrofit看起來(lái)很好用,不過(guò)要求服務(wù)端返回?cái)?shù)據(jù)最好要規(guī)范,不然如果請(qǐng)求成功返回一種數(shù)據(jù)結(jié)構(gòu),請(qǐng)求失敗返回另一種數(shù)據(jù)結(jié)構(gòu),不好用Converter解析,接口的定義也不好定義,除非都返回Response,或自定義Converter所有接口都返回String
- Android網(wǎng)絡(luò)請(qǐng)求框架Retrofit詳解
- Android Retrofit 2.0框架上傳圖片解決方案
- 簡(jiǎn)略分析Android的Retrofit應(yīng)用開(kāi)發(fā)框架源碼
- Retrofit和OkHttp如何實(shí)現(xiàn)Android網(wǎng)絡(luò)緩存
- Android Retrofit2網(wǎng)路編程實(shí)現(xiàn)方法詳解
- Android Retrofit2數(shù)據(jù)解析代碼解析
- Android中Retrofit的簡(jiǎn)要介紹
- 基于Retrofit2+RxJava2實(shí)現(xiàn)Android App自動(dòng)更新
- Android retrofit上傳文件實(shí)例(包含頭像)
- Android 封裝Okhttp+Retrofit+RxJava,外加攔截器實(shí)例
- Android Retrofit 中文亂碼問(wèn)題的解決辦法
- Android使用 Retrofit 2.X 上傳多文件和多表單示例
- Android中Retrofit 2.0直接使用JSON進(jìn)行數(shù)據(jù)交互
- Android Retrofit框架的使用
相關(guān)文章
android獲取當(dāng)前接入點(diǎn)信息判斷是ctwap還是ctnet實(shí)例代碼
這篇文章主要介紹了android獲取當(dāng)前接入點(diǎn)信息判斷是ctwap還是ctnet的方法,大家參考使用吧2013-11-11Android 將view 轉(zhuǎn)換為Bitmap出現(xiàn)空指針問(wèn)題解決辦法
這篇文章主要介紹了Android 將view 轉(zhuǎn)換為Bitmap出現(xiàn)空指針問(wèn)題解決辦法的相關(guān)資料,這里提供實(shí)例并提供解決辦法,需要的朋友可以參考下2017-07-07Android中比較兩個(gè)圖片是否一致的問(wèn)題
這篇文章主要介紹了Android中比較兩個(gè)圖片是否一致的問(wèn)題,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-10-10Android Studio gradle 編譯提示‘default not found’ 解決辦法
這篇文章主要介紹了Android Studio gradle 編譯提示‘default not found’ 解決辦法的相關(guān)資料,需要的朋友可以參考下2016-12-12Android中Gallery和ImageSwitcher的使用實(shí)例
今天小編就為大家分享一篇關(guān)于Android中Gallery和ImageSwitcher的使用實(shí)例,小編覺(jué)得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來(lái)看看吧2019-03-03Android實(shí)現(xiàn)短信驗(yàn)證碼自動(dòng)攔截讀取功能
這篇文章主要為大家詳細(xì)介紹了Android實(shí)現(xiàn)短信驗(yàn)證碼自動(dòng)攔截讀取功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-08-08使用ViewPager實(shí)現(xiàn)左右循環(huán)滑動(dòng)及滑動(dòng)跳轉(zhuǎn)
今天實(shí)現(xiàn)了左右滑動(dòng),至于在最后一頁(yè)滑動(dòng)跳轉(zhuǎn),這個(gè)也做了但是效果不是太好,也希望有實(shí)現(xiàn)的朋友能夠分享下2013-01-01深入Android HandlerThread 使用及其源碼完全解析
這篇文章主要介紹了深入Android HandlerThread 使用及其源碼完全解析,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-08-08Android ScrollView滑動(dòng)實(shí)現(xiàn)仿QQ空間標(biāo)題欄漸變
這篇文章主要為大家詳細(xì)介紹了Android ScrollView滑動(dòng)實(shí)現(xiàn)仿QQ空間標(biāo)題欄漸變,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-08-08