編輯:關於Android編程
就目前來說Retrofit2使用的已相當的廣泛,那麼我們先來了解下兩個問題:
1 . 什麼是Retrofit?
Retrofit是針對於Android/Java的、基於okHttp的、一種輕量級且安全的、並使用注解方式的網絡請求框架。
2 . 我們為什麼要使用Retrofit,它有哪些優勢?
首先,Retrofit使用注解方式,大大簡化了我們的URL拼寫形式,而且注解含義一目了然,簡單易懂;
其次,Retrofit使用簡單,結構層次分明,每一步都能清晰的表達出之所以要使用的寓意;
再者,Retrofit支持同步和異步執行,使得請求變得異常簡單,只要調用enqueue/execute即可完成;
最後,Retrofit更大自由度的支持我們自定義的業務邏輯,如自定義Converters。
好,知道了Retrofit是什麼,有了哪些優勢,現在我們來學習下怎麼使用。
在使用之前,你必須先導入必要的jar包,以androidStudio為例:
添加依賴:
compile 'com.squareup.retrofit2:retrofit:2.0.2'
compile 'com.squareup.retrofit2:converter-gson:2.0.2'
因為Retrofit2是依賴okHttp請求的,而且請查看它的META-INF->META-INF\maven\com.squareup.retrofit2\retrofit->pom.xml文件,
com.squareup.okhttp3
okhttp
...
由此可見,它確實是依賴okHttp,okHttp有會依賴okio所以它會制動的把這兩個包也導入進來。
添加權限:
既然要請求網絡,在我們android手機上是必須要有訪問網絡的權限的,下面把權限添加進來
好了,下面開始介紹怎麼使用Retrofit,既然它是使用注解的請求方式來完成請求URL的拼接,那麼我們就按注解的不同來分別學習:
首先,我們需要創建一個java接口,用於存放請求方法的:
public interface GitHubService {
}
然後逐步在該方法中添加我們所需要的方法(按照請求方式):
1 :Get : 是我們最常見的請求方法,它是用來獲取數據請求的。
①:直接通過URL獲取網絡內容:
public interface GitHubService {
@GET("users/octocat/repos")
Call> listRepos();
}
在這裡我們定義了一個listRepos()的方法,通過@GET注解標識為get請求,請求的URL為“users/octocat/repos”。
然後看看Retrofit是怎麼調用的,代碼如下:
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://api.github.com/")
.build();
GitHubService service = retrofit.create(GitHubService.class);
Call> repos = service.listRepos();
repos.enqueue(new Callback>(){
@Override
public void onResponse(Call> call, Response> response){
}
@Override
public void onFailure(Call> call, Throwable t){
}
});
代碼解釋:首先獲取Retrofit對象,然後通過動態代理獲取到所定義的接口,通過調用接口裡面的方法獲取到Call類型返回值,最後進行網絡請求操作(這裡不詳細說明Retrofit 實現原理,後面會對它進行源碼解析),這裡必須要說的是請求URL的拼接:在構建Retrofit對象時調用baseUrl所傳入一個String類型的地址,這個地址在調用service.listRepos()時會把@GET(“users/octocat/repos”)的URL拼接在尾部。
ok,這樣就完成了,我們這次的請求,但是我們不能每次請求都要創建一個方法呀?這時我們就會想起動態的構建URL了
②:動態獲取URL地址:@Path
我們再上面的基礎上進行修改,如下:
public interface GitHubService {
@GET("users/{user}/repos")
Call> listRepos(@Path("user") String user);
這裡在Get注解中包含{user},它所對應的是@Path注解中的“user”,它所標示的正是String user,而我們再使用Retrofit對象動態代理的獲取到GitHubService,當調用listRepos時,我們就必須傳入一個String類型的User,如:
...
Call> repos = service.listRepos("octocat");
...
如上代碼,其他的代碼都是不變的,而我們只需要使用@Path注解就完全的實現了動態的URL地址了,是不是很方便呢,這還不算什麼,通常情況下,我們去獲取一些網絡信息,因為信息量太大,我們會分類去獲取,也就是攜帶一些必要的元素進行過濾,那我們該怎麼實現呢?其實也很簡單,因為Retrofit已經為我們封裝好了注解,請看下面(官網實例):
③:動態指定條件獲取信息:@Query
@GET("group/{id}/users")
Call> groupList(@Path("id") int groupId, @Query("sort") String sort);
我們只需要使用@Query注解即可完成我們的需求,在@Query(“sort”)中,short就好比是URL請求地址中的鍵,而它說對應的String sort中的sort則是它的值。
但是我們想,在網絡請求中一般為了更精確的查找到我們所需要的數據,過濾更多不需要的無關的東西,我們往往需要攜帶多個請求參數,當然可以使用@Query注解,但是太麻煩,很長,容易遺漏和出錯,那有沒有更簡單的方法呢,有,當然後,我們可以直接放到一個map鍵值對中:
④:動態指定條件組獲取信息:@QueryMap
@GET("group/{id}/users")
Call> groupList(@Path("id") int groupId, @QueryMap Map options);
使用@QueryMap注解可以分別地從Map集合中獲取到元素,然後進行逐個的拼接在一起。
ok,到這裡,我們使用@Get注解已經可以完成絕大部分的查詢任務了,下面我們再來看看另一種常用的請求方式–post
2 POST : 一種用於攜帶傳輸數據的請求方式
稍微了解點Http的同學們,可能都會知道:相對於get請求方式把數據存放在uri地址欄中,post請求傳輸的數據時存放在請求體中,所以post才能做到對數據的大小無限制。而在Retrofit中,它又是怎麼使用的呢?請看下面:
①:攜帶數據類型為對象時:@Body
@POST("users/new")
Call createUser(@Body User user);
當我們的請求數據為某對象時Retrofit是這麼處理使用的:
首先,Retrofit用@POST注解,標明這個是post的請求方式,裡面是請求的url;
其次,Retrofit仿照http直接提供了@Body注解,也就類似於直接把我們要傳輸的數據放在了body請求體中,這樣應用可以更好的方便我們理解。
來看下應用:
Call> repos = service.createUser(new User(1, "管滿滿", "28", "http://write.blog.csdn.net/postlist"));
這樣我們直接把一個新的User對象利用注解@Body存放在body請求體,並隨著請求的執行傳輸過去了。
但是有同學在這該有疑問了,Retrofit就只能傳輸的數據為對象嗎?當然不是,下面請看
②:攜帶數據類型為表單鍵值對時:@Field
@FormUrlEncoded
@POST("user/edit")
Call updateUser(@Field("first_name") String first, @Field("last_name") String last);
當我們要攜帶的請求數據為表單時,通常會以鍵值對的方式呈現,那麼Retrofit也為我們考慮了這種情況,它首先用到@FormUrlEncoded注解來標明這是一個表單請求,然後在我們的請求方法中使用@Field注解來標示所對應的String類型數據的鍵,從而組成一組鍵值對進行傳遞。
那你是不是有該有疑問了,假如我是要上傳一個文件呢?
③:單文件上傳時:@Part
@Multipart
@PUT("user/photo")
Call updateUser(@Part("photo") RequestBody photo, @Part("description") RequestBody description);
此時在上傳文件時,我們需要用@Multipart注解注明,它表示允許多個@Part,@Part則對應的一個RequestBody 對象,RequestBody 則是一個多類型的,當然也是包括文件的。下面看看使用
File file = new File(Environment.getExternalStorageDirectory(), "ic_launcher.png");
RequestBody photoRequestBody = RequestBody.create(MediaType.parse("image/png"), file);
RequestBody descriptionRequestBody = RequestBody.create(null, "this is photo.");
Call call = service.updateUser(photoRequestBody, descriptionRequestBody);
這裡我們創建了兩個RequestBody 對象,然後調用我們定義的updateUser方法,並把RequestBody傳遞進入,這樣就實現了文件的上傳。是不是很簡單呢?
相比單文件上傳,Retrofit還進一步提供了多文件上傳的方式:
④:多文件上傳時:@PartMap
@Multipart
@PUT("user/photo")
Call updateUser(@PartMap Map photos, @Part("description") RequestBody description);
這裡其實和單文件上傳是差不多的,只是使用一個集合類型的Map封裝了文件,並用@PartMap注解來標示起來。其他的都一樣,這裡就不多講了。
3 Header : 一種用於攜帶消息頭的請求方式
Http請求中,為了防止攻擊或是過濾掉不安全的訪問或是為添加特殊加密的訪問等等以減輕服務器的壓力和保證請求的安全,通常都會在消息頭中攜帶一些特殊的消息頭處理。Retrofit也為我們提供了該請求方式:
@Headers("Cache-Control: max-age=640000")
@GET("widget/list")
Call> widgetList();
-----------------------------------------------------------
@Headers({
"Accept: application/vnd.github.v3.full+json",
"User-Agent: Retrofit-Sample-App"
})
@GET("users/{username}")
Call getUser(@Path("username") String username);
以上兩種是靜態的為Http請求添加消息頭,只需要使用@Headers注解,以鍵值對的方式存放即可,如果需要添加多個消息頭,則使用{}包含起來,如上所示。但要注意,即使有相同名字得消息頭也不會被覆蓋,並共同的存放在消息頭中。
當然有靜態添加那相對的也就有動態的添加消息頭了,方法如下:
@GET("user")
Call getUser(@Header("Authorization") String authorization)
使用@Header注解可以為一個請求動態的添加消息頭,假如@Header對應的消息頭為空的話,則會被忽略,否則會以它的.toString()方式輸出。
ok,到這裡已基本講解完Retrofit的使用,還有兩個重要但簡單的方法也必須在這裡提一下:
1 call.cancel();它可以終止正在進行的請求,程序只要一旦調用到它,不管請求是否在終止都會被停止掉。
2 call.clone();當你想要多次請求一個接口的時候,直接用 clone 的方法來生產一個新的,否則將會報錯,因為當你得到一個call實例,我們調用它的 execute 方法,但是這個方法只能調用一次。多次調用則發生異常。
好了,關於Retrofit的使用我們就講這麼多,接下來我們從源碼的角度簡單的解析下它的實現原理。
首先先看一下Retrofit2標准示例
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build();
GitHubService service = retrofit.create(GitHubService.class);
service.enqueue();
service.execute();
由上面我們基本可以看出,Retrofit是通過構造者模式創建出來的,那麼我們就來看看Builder這個構造器的源碼:
public static final class Builder {
...
Builder(Platform platform) {
this.platform = platform;
converterFactories.add(new BuiltInConverters());
}
public Builder() {
this(Platform.get());
}
public Builder client(OkHttpClient client) {
return callFactory(checkNotNull(client, "client == null"));
}
public Builder callFactory(okhttp3.Call.Factory factory) {
this.callFactory = checkNotNull(factory, "factory == null");
return this;
}
public Builder baseUrl(String baseUrl) {
checkNotNull(baseUrl, "baseUrl == null");
HttpUrl httpUrl = HttpUrl.parse(baseUrl);
if (httpUrl == null) {
throw new IllegalArgumentException("Illegal URL: " + baseUrl);
}
return baseUrl(httpUrl);
}
public Builder baseUrl(HttpUrl baseUrl) {
checkNotNull(baseUrl, "baseUrl == null");
List pathSegments = baseUrl.pathSegments();
if (!"".equals(pathSegments.get(pathSegments.size() - 1))) {
throw new IllegalArgumentException("baseUrl must end in /: " + baseUrl);
}
this.baseUrl = baseUrl;
return this;
}
public Builder addConverterFactory(Converter.Factory factory) {
converterFactories.add(checkNotNull(factory, "factory == null"));
return this;
}
public Builder addCallAdapterFactory(CallAdapter.Factory factory) {
adapterFactories.add(checkNotNull(factory, "factory == null"));
return this;
}
public Builder callbackExecutor(Executor executor) {
this.callbackExecutor = checkNotNull(executor, "executor == null");
return this;
}
public Builder validateEagerly(boolean validateEagerly) {
this.validateEagerly = validateEagerly;
return this;
}
public Retrofit build() {
if (baseUrl == null) {
throw new IllegalStateException("Base URL required.");
}
okhttp3.Call.Factory callFactory = this.callFactory;
if (callFactory == null) {
callFactory = new OkHttpClient();
}
Executor callbackExecutor = this.callbackExecutor;
if (callbackExecutor == null) {
callbackExecutor = platform.defaultCallbackExecutor();
}
List adapterFactories = new ArrayList<>(this.adapterFactories);
adapterFactories.add(platform.defaultCallAdapterFactory(callbackExecutor));
List converterFactories = new ArrayList<>(this.converterFactories);
return new Retrofit(callFactory, baseUrl, converterFactories, adapterFactories,
callbackExecutor, validateEagerly);
}
}
源碼講解:
1:當我們使用new Retrofit.Builder()來創建時,在Builder構造器中,首先就獲得當前的設備平台信息,並且把內置的轉換器工廠(BuiltInConverters)加添到工廠集合中,它的主要作用就是當使用多種Converters的時候能夠正確的引導並找到可以消耗該類型的轉化器。
2:從我們的基本示例中看到有調用到.baseUrl(BASE_URL)這個方法,實際上沒當使用Retrofit時,該方法都是必須傳入的,並且還不能為空,從源碼中可以看出,當baseUrl方法傳進的參數來看,如果為空的話將會拋出NullPointerException空指針異常。
3:addConverterFactory該方法是傳入一個轉換器工廠,它主要是對數據轉化用的,請網絡請求獲取的數據,將會在這裡被轉化成我們所需要的數據類型,比如通過Gson將json數據轉化成對象類型。
4 : 從源碼中,我們看到還有一個client方法,這個是可選的,如果沒有傳入則就默認為OkHttpClient,在這裡可以對OkHttpClient做一些操作,比如添加攔截器打印log等
5:callbackExecutor該方法從名字上看可以得知應該是回調執行者,也就是Call對象從網絡服務獲取數據之後轉換到UI主線程中。
6:addCallAdapterFactory該方法主要是針對Call轉換了,比如對Rxjava的支持,從返回的call對象轉化為Observable對象。
7:最後調用build()方法,通過new Retrofit(callFactory, baseUrl, converterFactories, adapterFactories,
callbackExecutor, validateEagerly);構造方法把所需要的對象傳遞到Retrofit對象中。
ok,當我們通過Builder構造器構造出Retrofit對象時,然後通過Retrofit.create()方法是怎麼把我們所定義的接口轉化成接口實例的呢?來看下create()源碼:
public T create(final Class 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);
}
ServiceMethod serviceMethod = loadServiceMethod(method);
OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args);
return serviceMethod.callAdapter.adapt(okHttpCall);
}
});
}
當看到Proxy時,是不是多少有點明悟了呢?沒錯就是動態代理,動態代理其實已經封裝的很簡單了,主要使用newProxyInstance()方法來返回一個類的代理實例,其中它內部需要傳遞一個類的加載器,類本身以及一個InvocationHandler處理器,主要的動作都是在InvocationHandler中進行的,它裡面只有一個方法invoke()方法,每當我們調用代理類裡面的方法時invoke()都會被執行,並且我們可以從該方法的參數中獲取到所需要的一切信息,比如從method中獲取到方法名,從args中獲取到方法名中的參數信息等。
而Retrofit在這裡使用到動態代理也不會例外:
首先,通過method把它轉換成ServiceMethod ;
然後,通過serviceMethod, args獲取到okHttpCall 對象;
最後,再把okHttpCall進一步封裝並返回Call對象。
下面來逐步詳解。
1:將method把它轉換成ServiceMethod
ServiceMethod serviceMethod = loadServiceMethod(method);
ServiceMethod loadServiceMethod(Method method) {
ServiceMethod result;
synchronized (serviceMethodCache) {
result = serviceMethodCache.get(method);
if (result == null) {
result = new ServiceMethod.Builder(this, method).build();
serviceMethodCache.put(method, result);
}
}
return result;
}
loadServiceMethod源碼方法中非常的好理解,主要就是通過ServiceMethod.Builder()方法來構建ServiceMethod,並把它給緩存取來,以便下次可以直接回去ServiceMethod。那下面我們再來看看它是怎麼構建ServiceMethod方法的:
public Builder(Retrofit retrofit, Method method) {
this.retrofit = retrofit;
this.method = method;
this.methodAnnotations = method.getAnnotations();
this.parameterTypes = method.getGenericParameterTypes();
this.parameterAnnotationsArray = method.getParameterAnnotations();
}
public ServiceMethod build() {
callAdapter = createCallAdapter();
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?");
}
responseConverter = createResponseConverter();
...
return new ServiceMethod<>(this);
}
首先在Builder()中初始化一些參數,然後在build()中返回一個new ServiceMethod<>(this)對象。
下面來詳細的解釋下build()方法,完全理解了該方法則便於理解下面的所有執行流程。
①:構建CallAdapter對象,該對象將會在第三步中起著至關重要的作用。
現在我們先看看它是怎麼構建CallAdapter對象的:createCallAdapter()方法源碼如下:
private CallAdapter 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 {
return 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);
}
}
在createCallAdapter方法中主要做的是事情就是獲取到method的類型和注解,然後調用retrofit.callAdapter(returnType, annotations);方法:
public CallAdapter callAdapter(Type returnType, Annotation[] annotations) {
return nextCallAdapter(null, returnType, annotations);
}
public CallAdapter nextCallAdapter(CallAdapter.Factory skipPast, Type returnType,
Annotation[] annotations) {
checkNotNull(returnType, "returnType == null");
checkNotNull(annotations, "annotations == null");
int start = adapterFactories.indexOf(skipPast) + 1;
for (int i = start, count = adapterFactories.size(); i < count; i++) {
CallAdapter adapter = adapterFactories.get(i).get(returnType, annotations, this);
if (adapter != null) {
return adapter;
}
}
...
}
輾轉到Retrofit中nextCallAdapter()中,在for 循環中分別從adapterFactories中來獲取CallAdapter對象,但是adapterFactories中有哪些CallAdapter對象呢,這就需要返回到構建Retrofit對象中的Builder 構造器中查看了
public static final class Builder {
...
public Builder addCallAdapterFactory(CallAdapter.Factory factory) {
adapterFactories.add(checkNotNull(factory, "factory == null"));
return this;
}
...
public Retrofit build() {
List adapterFactories = new ArrayList<>(this.adapterFactories);
adapterFactories.add(platform.defaultCallAdapterFactory(callbackExecutor));
...
}
}
CallAdapter.Factory defaultCallAdapterFactory(Executor callbackExecutor) {
if (callbackExecutor != null) {
return new ExecutorCallAdapterFactory(callbackExecutor);
}
return DefaultCallAdapterFactory.INSTANCE;
}
從上面的代碼中可以看到,不管有沒有通過addCallAdapterFactory添加CallAdapter,adapterFactories集合至少都會有一個ExecutorCallAdapterFactory對象。當我們從adapterFactories集合中回去CallAdapter對象時,那我們都會獲得ExecutorCallAdapterFactory這個對象。而這個對象將在第三步中和後面執行同步或異步請求時起著至關重要的作用。
②:構建responseConverter轉換器對象,它的作用是尋找適合的數據類型轉化
該對象的構建和構建CallAdapter對象的流程基本是一致的,這裡就不在贅述。同學們可自行查看源碼。
2:通過serviceMethod, args獲取到okHttpCall 對象
第二步相對比較簡單,就是對象傳遞:
OkHttpCall(ServiceMethod serviceMethod, Object[] args) {
this.serviceMethod = serviceMethod;
this.args = args;
}
3:把okHttpCall進一步封裝並返回Call對象
這一步也是一句話 return serviceMethod.callAdapter.adapt(okHttpCall);但是想理解清楚必須先把第一步理解透徹,通過第一步我們找得到serviceMethod.callAdapter就是ExecutorCallAdapterFactory對象,那麼調用.adapt(okHttpCall)把okHttpCall怎麼進行封裝呢?看看源碼:
T adapt(Call call);
一看,嚇死寶寶了,就這麼一句,這是嘛呀,但是經過第一步的分析,我們已知道serviceMethod.callAdapter就是ExecutorCallAdapterFactory,那麼我們可以看看在ExecutorCallAdapterFactory類中有沒有發現CallAdapter的另類應用呢,一看,果不其然在重寫父類的get()方法中我們找到了答案:
@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>() {
@Override public Type responseType() {
return responseType;
}
@Override public Call adapt(Call call) {
return new ExecutorCallbackCall<>(callbackExecutor, call);
}
};
}
當看到return new CallAdapter中的adapt(Call call)我們就完全知其所以然了,至於ExecutorCallbackCall怎麼應用的我們在發起網絡請求的時候講解。
ok,當我們得到接口的代理實例之後,通過代理接口調用裡面的方法,就會觸發InvocationHandler對象中的invoke方法,從而完成上面的三個步驟並且返回一個Call對象,通過Call對象就可以去完成我們的請求了,Retrofit為我們提供兩種請求方式,一種是同步,一種是異步。我們這裡就以異步方式來講解:
service.enqueue(new Callback>() {
@Override
public void onResponse(Call> call, Response> response) {
Log.d("response body",response.body());
}
@Override
public void onFailure(Call call, Throwable t) {
Log.i("response Throwable",t.getMessage().toString());
}
});
從上面我們可以看到enqueue方法中有一個回調函數,回調函數裡面重寫了兩個方法分別代表請求成功和失敗的方法,但是我們想知道它是怎麼實現的原理呢?那麼請往下面看:
在上面獲取接口的代理實例時,通過代理接口調用裡面的方法獲取一個Call對象,我們上面也分析了其實這個Call對象就是ExecutorCallbackCall,那麼我們來看看它裡面是怎麼實現的?
static final class ExecutorCallbackCall implements Call {
final Executor callbackExecutor;
final Call delegate;
ExecutorCallbackCall(Executor callbackExecutor, Call delegate) {
this.callbackExecutor = callbackExecutor;
this.delegate = delegate;
}
@Override public void enqueue(final Callback callback) {
if (callback == null) throw new NullPointerException("callback == null");
delegate.enqueue(new Callback() {
@Override public void onResponse(Call call, final Response 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 call, final Throwable t) {
callbackExecutor.execute(new Runnable() {
@Override public void run() {
callback.onFailure(ExecutorCallbackCall.this, t);
}
});
}
});
}
在ExecutorCallbackCall類中,封裝了兩個對象一個是callbackExecutor,它主要是把現在運行的線程切換到主線程中去,一個是delegate對象,這個對象就是真真正正的執行網絡操作的對象,那麼它的真身到底是什麼呢?還記得我們在獲取代理接口第三步執行的serviceMethod.callAdapter.adapt(okHttpCall)的分析吧,經過輾轉幾步終於把okHttpCall傳遞到了new ExecutorCallbackCall<>(callbackExecutor, call);中,然後看看ExecutorCallbackCall的構造方法:
ExecutorCallbackCall(Executor callbackExecutor, Call delegate) {
this.callbackExecutor = callbackExecutor;
this.delegate = delegate;
}
由此可以明白delegate 就是okHttpCall對象,那麼我們在看看okHttpCall是怎麼執行異步網絡請求的:
@Override
public void enqueue(final Callback callback) {
if (callback == null) throw new NullPointerException("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 {
call = rawCall = createRawCall();
} catch (Throwable t) {
failure = creationFailure = t;
}
}
}
if (failure != null) {
callback.onFailure(this, failure);
return;
}
if (canceled) {
call.cancel();
}
call.enqueue(new okhttp3.Callback() {
@Override
public void onResponse(okhttp3.Call call, okhttp3.Response rawResponse)
throws IOException {
Response response;
try {
response = parseResponse(rawResponse);
} catch (Throwable e) {
callFailure(e);
return;
}
callSuccess(response);
}
@Override
public void onFailure(okhttp3.Call call, IOException e) {
try {
callback.onFailure(OkHttpCall.this, e);
} catch (Throwable t) {
t.printStackTrace();
}
}
private void callFailure(Throwable e) {
try {
callback.onFailure(OkHttpCall.this, e);
} catch (Throwable t) {
t.printStackTrace();
}
}
private void callSuccess(Response response) {
try {
callback.onResponse(OkHttpCall.this, response);
} catch (Throwable t) {
t.printStackTrace();
}
}
});
}
從上面代碼中,我們很容易就看出,其實它就是這裡面封裝了一個okhttp3.Call,直接利用okhttp進行網絡的異步操作,至於okhttp是怎麼進行網絡請求的我們就不再這裡講解了,感興趣的朋友可以自己去查看源碼。
好了,到這裡整個Retrofit的實現原理基本已解析完畢,相信大家學習過都能夠很好的掌握了。ok,今天就講到這裡吧,本來還打算再寫個實例放上來的,看看篇幅也就放棄了,實戰部分會在下一篇和Rxjava一起放出來,Rxjava我自己學習的都心花怒放了。看完記住關注微信平台。
在項目開發中,我們經常需要進行動態添加組件,其中可添加的部分有兩項:布局和組件 其中,添加的布局主要有RelativeLayout型(相對布局)的和Linear
關於Android中launchMode的文章介紹的真心不少,廣為流傳而且介紹的最詳細的莫過於這篇文章http://blog.csdn.net/android_tutor
這就是開源的好處,通過Github、各大論壇和技術博客,你會發現很多對你有用的資源。對於做技術的同學來說,深入研究一門技術很重要,但是適當的擴展自己的視野,了解他人的一些
前言:監於5.0之後Google用的是Camera2相關API取代之前的Camera,過時的Camera雖然精典,但不再進行介紹,可自行查閱相關資料。今天本文是在正式深入