說到網絡庫就會想到Google的HttpUrlConnection和Apache的HttpClient。
Google推薦使用HttpUrlConnection,拋棄了HttpClient
HttpClient已在Android6.0被廢棄了(吐槽:明明HttpClient功能更強大啊),
查看為什么拋棄HttpClient,可以看下【Android】Android2.3版本以上谷歌為何推薦使用HttpURLConnection卻棄用 Apache HttpClient
想繼續使用HttpClient的話,可以參考android6.0SDK 刪除HttpClient的相關類的解決方法
除了HttpUrlConnection,還有Google推薦的Volley(沒怎么用過)
,和square的okhttp
,還有就是今天介紹的square的retrofit
這個年頭不會點
retrofit + rxjava + mvp...
的組合拳,都不好意思說是新時代的Android開發
為什么我用的組件都是square公司出品的呢?只能說明他們做的太好了。
回到正題
關于Retrofit的介紹,引用它在github上的一句話
Type-safe HTTP client for Android and Java by Square
用于Android和Java的類型安全的Http框架
現在市面上使用的是retrofit2.0版本,跟以前的1.9版本有部分API不一樣,可以查看Retrofit 1.9 遷移到 Retrofit 2.0,建議使用新的版本
Retrofit是使用RESETful API的。
這里簡單的介紹下關于RESETful的架構:
(1)每一個URI代表一種資源;
(2)客戶端和服務器之間,傳遞這種資源的某種表現層;
(3)客戶端通過四個HTTP動詞(GET用來獲取資源,POST用來新建資源(也可以用于更新資源),PUT用來更新資源,DELETE用來刪除資源),
對服務器端資源進行操作,實現"表現層狀態轉化"。
更多關于RESET API可以看阮一峰的理解RESTful架構
Retrofit的git地址:https://github.com/square/retrofit
Retrofit的官方地址:http://square.github.io/retrofit/
使用姿勢
-
往build.gradle添加依賴,
不需要額外添加okhttp,retrofit內部支持
// retrofit compile 'com.squareup.retrofit2:retrofit:2.2.0' // gson轉化器 compile 'com.squareup.retrofit2:converter-gson:2.2.0'
-
創建接口,聲明API
// 獲取請求API // 請求接口 https://api.github.com public interface GitHub { @GET("/repos/{owner}/{repo}/contributors") Call<List<Contributor>> contributors( @Path("owner") String owner, @Path("repo") String repo); } // 請求對象類 public static class Contributor { public final String login; public final int contributions; public Contributor(String login, int contributions) { this.login = login; this.contributions = contributions; } }
-
方法調用
public void request() throws IOException { // 1.創建Retrofit對象,根據Retrofit模板創建 Retrofit retrofit = new Retrofit.Builder() // 傳入baseurl地址, .baseUrl("https://api.github.com") // 傳入gson轉化器 .addConverterFactory(GsonConverterFactory.create()) .build(); // 2.獲取GitHub的對象 GitHub github = retrofit.create(GitHub.class); // 3.傳入參數 Call<List<Contributor>> call = github.contributors("square", "retrofit"); // 4.異步請求接口 // 請求接口格式為"https://api.github.com/repos/square/retrofit/contributors" call.enqueue(new Callback<List<Contributor>>() { @Override public void onResponse(Call<List<Contributor>> call, Response<List<Contributor>> response) { List<Contributor> contributors = response.body(); for (Contributor contributor : contributors) { Log.e("MainActivity",contributor.login + " (" + contributor.contributions + ")"); } } @Override public void onFailure(Call<List<Contributor>> call, Throwable t) { } }); }
通過上面的例子,應該能夠理解Retrofit的使用了吧
關于使用,可以參考下官網介紹http://square.github.io/retrofit/
對此,作者菌有幾個問題
- Retrofit提供的這么多注解是怎么運用的?
- Retrofit有多少種注解
- Retrofit生成API對象后,怎么發起請求的
源碼解讀
先查看retrofit的代碼構造
.
└── retrofit2
├── BuiltInConverters.java --默認的轉化器,支持流,字符串
├── Call.java -- 請求的發送/取消,并獲取當前請求的狀態
├── CallAdapter.java
├── Callback.java. --回調方法
├── Converter.java -- 轉化器,將okhttp中的RequestBody,ResponseBody進行轉化,支持XML,JSON,ProtocolBuf,如GsonConverterFactory,將返回的內容或者發送Body中的內容轉化,
├── DefaultCallAdapterFactory.java
├── ExecutorCallAdapterFactory.java
├── HttpException.java
├── OkHttpCall.java --實現了Call的接口,使用okhttp3.0的API
├── ParameterHandler.java --參數句柄,處理retrofit頭文件的操作請求
├── Platform.java --判斷當前運行環境是Android還是Java8
├── RequestBuilder.java
├── Response.java 服務端返回內容,可以通過response.body()獲取到返回內容
├── Retrofit.java --`對外方法`
├── ServiceMethod.java
├── Utils.java
├── http --http文件夾中包含的是retrofit所有的注解類,關于Http的操作和上傳字段,以及Header配置
│ ├── Body.java 用于Post,根據轉換方式將實例對象轉化為對應字符串傳遞參數.比如Retrofit添加GsonConverterFactory則是將body轉化為gson字符串進行傳遞
│ ├── DELETE.java Delete請求
│ ├── Field.java 用于Post方式傳遞參數,需要在請求接口方法上添加@FormUrlEncoded,即以表單的方式傳遞參數
│ ├── FieldMap.java
│ ├── FormUrlEncoded.java 同@Field使用
│ ├── GET.java GET請求
│ ├── HEAD.java
│ ├── HTTP.java
│ ├── Header.java 添加http header
│ ├── HeaderMap.java
│ ├── Headers.java 跟@Header作用一樣,只是使用方式不一樣,@Header是作為請求方法的參數傳入,@Headers是以固定方式直接添加到請求方法上
│ ├── Multipart.java 配合@Multipart使用,一般用于文件上傳
│ ├── OPTIONS.java
│ ├── PATCH.java
│ ├── POST.java Post請求
│ ├── PUT.java PUT請求
│ ├── Part.java 一般用于文件上傳
│ ├── PartMap.java
│ ├── Path.java 用于URL上占位符
│ ├── Query.java 用于Http Get請求傳遞參數
│ ├── QueryMap.java
│ ├── QueryName.java 用于Http Get請求傳遞參數
│ ├── Streaming.java 用于Http Get請求傳遞流形式參數
│ ├── Url.java 直接賦值URL
│ └── package-info.java
└── package-info.java
附上一篇別人寫的###Retrofit注解含義###
講解下Retrofit的使用原理
- 首先講解Retrofit初始化
Retrofit retrofit = new Retrofit.Builder()
// 傳入baseurl地址,
.baseUrl("https://api.github.com")
// 傳入gson轉化器
.addConverterFactory(GsonConverterFactory.create())
.build();
此處用了Build機制來初始化,現在很多初始化通過Build機制,如Dialog,OKHttp,Picasso等
Build構建時可以傳入以下配置
Builder(Platform platform) {
this.platform = platform;
// Add the built-in converter factory first. This prevents overriding its behavior but also
// ensures correct behavior when using converters that consume all types.
converterFactories.add(new BuiltInConverters());
}
public Builder() {
// 獲取當前的系統版本
this(Platform.get());
}
/**
* 配置Client,默認使用okhttp3.OkHttpClient
* @param client
* @return
*/
public Builder client(OkHttpClient client) {
return callFactory(checkNotNull(client, "client == null"));
}
/**
* 創建CallFactory,用于生成請求Http的Call
* @param factory
* @return
*/
public Builder callFactory(okhttp3.Call.Factory factory) {
this.callFactory = checkNotNull(factory, "factory == null");
return this;
}
/**
* 配置URL
* @param baseUrl
* @return
*/
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);
}
/**
* 配置URL,下面有幾組實例配置URL
* <p>
* <b>Correct:</b><br>
* Base URL: http://example.com/api/<br>
* Endpoint: foo/bar/<br>
* Result: http://example.com/api/foo/bar/
* <p>
* <b>Incorrect:</b><br>
* Base URL: http://example.com/api<br>
* Endpoint: foo/bar/<br>
* Result: http://example.com/foo/bar/
* <p>
* This method enforces that {@code baseUrl} has a trailing {@code /}.
* <p>
* <b>Endpoint values which contain a leading {@code /} are absolute.</b>
* <p>
* Absolute values retain only the host from {@code baseUrl} and ignore any specified path
* components.
* <p>
* Base URL: http://example.com/api/<br>
* Endpoint: /foo/bar/<br>
* Result: http://example.com/foo/bar/
* <p>
* Base URL: http://example.com/<br>
* Endpoint: /foo/bar/<br>
* Result: http://example.com/foo/bar/
* <p>
* <b>Endpoint values may be a full URL.</b>
* <p>
* Values which have a host replace the host of {@code baseUrl} and values also with a scheme
* replace the scheme of {@code baseUrl}.
* <p>
* Base URL: http://example.com/<br>
* Endpoint: https://github.com/square/retrofit/<br>
* Result: https://github.com/square/retrofit/
* <p>
* Base URL: http://example.com<br>
* Endpoint: //github.com/square/retrofit/<br>
* Result: http://github.com/square/retrofit/ (note the scheme stays 'http')
*/
public Builder baseUrl(HttpUrl baseUrl) {
checkNotNull(baseUrl, "baseUrl == null");
List<String> pathSegments = baseUrl.pathSegments();
if (!"".equals(pathSegments.get(pathSegments.size() - 1))) {
throw new IllegalArgumentException("baseUrl must end in /: " + baseUrl);
}
this.baseUrl = baseUrl;
return this;
}
/**
* 添加轉換器
* @param factory
* @return
*/
public Builder addConverterFactory(Converter.Factory factory) {
converterFactories.add(checkNotNull(factory, "factory == null"));
return this;
}
/**
* Add a call adapter factory for supporting service method return types other than {@link
* Call}.
*/
public Builder addCallAdapterFactory(CallAdapter.Factory factory) {
adapterFactories.add(checkNotNull(factory, "factory == null"));
return this;
}
/**
* 回調線程
* @param executor
* @return
*/
public Builder callbackExecutor(Executor executor) {
this.callbackExecutor = checkNotNull(executor, "executor == null");
return this;
}
public Builder validateEagerly(boolean validateEagerly) {
this.validateEagerly = validateEagerly;
return this;
}
/**
* Create the {@link Retrofit} instance using the configured values.
* <p>
* Note: If neither {@link #client} nor {@link #callFactory} is called a default {@link
* OkHttpClient} will be created and used.
*/
public Retrofit build() {
if (baseUrl == null) {
throw new IllegalStateException("Base URL required.");
}
// 默認使用okhttp3.0的OKHttpClient
okhttp3.Call.Factory callFactory = this.callFactory;
if (callFactory == null) {
callFactory = new OkHttpClient();
}
// 若Platform是Android,回調的是主線程
Executor callbackExecutor = this.callbackExecutor;
if (callbackExecutor == null) {
callbackExecutor = platform.defaultCallbackExecutor();
}
// Make a defensive copy of the adapters and add the default Call adapter.
List<CallAdapter.Factory> adapterFactories = new ArrayList<>(this.adapterFactories);
adapterFactories.add(platform.defaultCallAdapterFactory(callbackExecutor));
// Make a defensive copy of the converters.
List<Converter.Factory> converterFactories = new ArrayList<>(this.converterFactories);
return new Retrofit(callFactory, baseUrl, converterFactories, adapterFactories,
callbackExecutor, validateEagerly);
}
此處注意的是baseUrl,正確寫法
是
* Base URL: http://example.com/api/
* Endpoint: foo/bar/
* Result: http://example.com/api/foo/bar/
Platform判斷當前運行環境是Android還是JAVA8
private static final Platform PLATFORM = findPlatform();
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) {
}
try {
Class.forName("java.util.Optional");
return new Java8();
} catch (ClassNotFoundException ignored) {
}
return new Platform();
}
2.獲取GitHub的對象,傳入參數
GitHub github = retrofit.create(GitHub.class);
//傳入參數
Call<List<Contributor>> call = github.contributors("square", "retrofit");
通過代理模式來處理接口
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);
}
// 加載ServiceMethod,進行URL組裝
ServiceMethod<Object, Object> serviceMethod =
(ServiceMethod<Object, Object>) loadServiceMethod(method);
OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);
return serviceMethod.callAdapter.adapt(okHttpCall);
}
});
}
3.調用call.enqueue(new Callback...);
使用的是OKHttpCall,使用了OKHttp的Call,然后進行Http請求。
Call的接口定義
public interface Call<T> extends Cloneable {
/**
* 同步發送請求,返回結果
* @return
* @throws IOException
*/
Response<T> execute() throws IOException;
/**
* 異步發送請求,通過Callback回調返回
* @param callback
*/
void enqueue(Callback<T> callback);
/**
* 是否正在執行發送
* @return
*/
boolean isExecuted();
/**
* 取消發送的請求,若請求還未執行
*/
void cancel();
/**
* 請求是否被取消
* @return
*/
boolean isCanceled();
/**
* Create a new, identical call to this one which can be enqueued or executed even if this call
* has already been.
*/
Call<T> clone();
/** The original HTTP request. */
Request request();
}
4.其他:如何構建Http請求,主要實在ServiceMethod.java這個類中
以@Path為例
else if (annotation instanceof Path) {
if (gotQuery) {
throw parameterError(p, "A @Path parameter must not come after a @Query.");
}
if (gotUrl) {
throw parameterError(p, "@Path parameters may not be used with @Url.");
}
if (relativeUrl == null) {
throw parameterError(p, "@Path can only be used with relative url on @%s", httpMethod);
}
gotPath = true;
Path path = (Path) annotation;
String name = path.value();
validatePathName(p, name);
Converter<?, String> converter = retrofit.stringConverter(type, annotations);
return new ParameterHandler.Path<>(name, converter, path.encoded());
}
通過剛才的代理模式調用ServiceMethod,將接口中的注解生成完整的Http請求
相關資料
Android 網絡框架 Retrofit2.0介紹、使用和封裝