Retrofit入門教程

前言

現在的網絡請求開源項目層出不窮,優秀者也不勝枚舉。常用的Volley,OkHttp等等。層出不窮的優秀開源工具,使得開發者的開發效率大幅提高,並且更加專注於自身的業務。今天介紹一下Retrofit的基本使用方法。

聲明在前

由於retrofit1.x和retrofit2.x升級變化很大,而有些教程在進行知識講解時並未之處針對的retrofit版本,這一點很讓初學者頭痛。現在您將看到的是關於retrofit2.x的講解。另外,關於retrofit1.x和retrofit2.x推薦一個不錯的網站:https://futurestud.io,該網站關於retrofit1.x有詳細的系列講解,並且文章也出了retrofit2.x系列,且沒有刪除retrofit1.x的相關講解。非常的不錯,可惜的是英文的。retrofit2使用了其默認的okhttpClient,當然你也可以自定義client。不過此時你需要關聯OkHttp。

閒聊retrofit

函數式編程使得代碼更易於閱讀,更加直觀、簡潔。retrofit也借鑑了這方面的有點。這是一個比較好的趨勢。與此同時,註解在retrofit的大量應用,使得網絡請求變得更加輕量級。大大減少了工作量,例如GET, POST, PUT, DELETE, 和 HEAD的請求參數都可以通過註解的方式來實現。retrofit涵蓋了主要的網絡請求方式。

另外,如果想要更深入的學習retrofit,對註解,反射的掌握是前提條件,如果想要更深入,還要學習一下java的動態代理技術。

retrofit的使用

首先需要將http api定義在接口中


public interface GitHubService {
  @GET("users/{user}/repos")
  Call<List<Repo>> listRepos(@Path("user") String user);
}

其中Get註解代表這是一個get請求,大括號{}包含了請求api的可變部分。在listRepos中通過註解指定get請求路徑中的可變參數user。這是一個比較簡單的api接口。稍後會列出一些混合的複雜的接口實現。List是請求回的數據的封裝。

直接使用

Retrofit retrofit = new Retrofit.Builder()
    .baseUrl("https://api.github.com/")
    .build();

GitHubService service = retrofit.create(GitHubService.class);

通過被創建的GitHubService可以調用call方法實現一個同步或者異步的網絡請求:

Call<List<Repo>> repos = service.listRepos("octocat");

一些複雜的請求接口

添加請求參數

@GET("group/{id}/users")
Call<List<User>> groupList(@Path("id") int groupId, @Query("sort") String sort);

添加多個請求參數

@GET("group/{id}/users")
Call<List<User>> groupList(@Path("id") int groupId, @QueryMap Map<String, String> options);

指定請求頭

方式一:

@Headers({
    "Accept: application/vnd.github.v3.full+json",
    "User-Agent: Retrofit-Sample-App"
})
@GET("users/{username}")
Call<User> getUser(@Path("username") String username);

方式二:

@GET("user")
Call<User> getUser(@Header("Authorization") String authorization)

以上請求都是同步的;
如果使用異步網絡請求,需要在以上網路接口中增加一個回調例如:

@GET("/user/{id}/photo")
void getUserPhoto(@Path("id") int id, Callback<Photo> cb);

序列化方式

默認的是RequestBody和ResponseBody
另外提供了以下converter

Gson - com.squareup.retrofit2:converter-gson
Jackson - com.squareup.retrofit2:converter-jackson
Moshi - com.squareup.retrofit2:converter-moshi
Protobuf - com.squareup.retrofit2:converter-protobuf
Wire - com.squareup.retrofit2:converter-wire
Simple Framework - com.squareup.retrofit2:converter-simpleframework
Scalars - com.squareup.retrofit2:converter-scalars


附錄:官方教程

到底怎麼使用Retrofit呢?

Retrofit通過註解動態代理來進行網絡請求。所以充分了解Retrofit的註解非常的重要:
這裏寫圖片描述
上圖是Retrofit中關於網絡請求相關的所有註解。

例如想要添加請求頭

如果你想添加請求頭:
retrofit提供了HEAD和Header和Headers三個註解,供你使用。
關於HEAD:

@Documented
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface HEAD {
    String value() default "";
}

請注意HEAD是用在方法的註解的。但這是請求頭參數較少的請求。此外還可以使用Header

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.PARAMETER})
public @interface Header {
    String value();
}

Header是用在參數中的。

再說說Headers上面已經提供了一個service實例。
看看源碼:

@Documented
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Headers {
    String[] value();
}

所以無論你的請求想要添加什麼參數,先查看一下retrofit關於註解的源碼:

關於@Target的類型

  • CONSTRUCTOR:用於描述構造器
  • FIELD:用於描述域
  • LOCAL_VARIABLE:用於描述局部變量
  • METHOD:用於描述方法
  • PACKAGE:用於描述包
  • PARAMETER:用於描述參數
  • TYPE:用於描述類、接口(包括註解類型) 或enum聲明

瞭解到這兒,至少你應該具有獨立使用retrofit的能力了。

作爲擴展知識,還是希望能研究一下retrofit的源碼。另外推薦一下Rxjava。二者結合使用,效率更高啊!

優秀文章:

Retrofit2.0使用總結及注意事項

關於retrofit的一個坑爹的提示

這個提示可能稱不上是bug,先來描述一下這個提示:

can't resolve host在github的issues裏面找了好久也沒找到合適的答案,由於我的手機設置了代理,當我取消代理時,這個問題就解決了。真是好坑啊!!!!!!!!!!!!!!!!!

使用案例

案例1:爲請求添加頭

可以通過添加註解@Headers在endpoint上,也可以通過okhttp的插入器,來進行添加頭。

通過@Headers來添加頭

這個例子很好找,在retrofit的官方介紹中:


@Headers("Cache-Control: max-age=640000")
@GET("widget/list")
Call<List<Widget>> widgetList();

通過okhttp的插入器實現

okHttpClient.interceptors().add(new Interceptor() {  
    @Override
    public Response intercept(Interceptor.Chain chain) throws IOException {
        Request original = chain.request();

        // Request customization: add request headers
        Request.Builder requestBuilder = original.newBuilder()
                .header("Authorization", "auth-value"); // <-- this is the important line

        Request request = requestBuilder.build();
        return chain.proceed(request);
    }
});

不過此時要注意Request.Builder的header(key,value)和addHeader(key,value)的區別。

.header(key, val): will override preexisting headers identified by key//將會覆蓋已存在的header
.addHeader(key, val): will add the header and don’t override preexisting ones//將會添加到header而不是覆蓋

retrofit2的轉換器轉換爲字符串

這是一個字符串轉換器,有來做測試比較方便

import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;

import okhttp3.MediaType;
import okhttp3.RequestBody;
import okhttp3.ResponseBody;
import retrofit2.Converter;
import retrofit2.Retrofit;
public class ToStringConverterFactory extends Converter.Factory {
    private static final MediaType MEDIA_TYPE = MediaType.parse("text/plain");


    @Override
    public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations, Retrofit retrofit) {
        if (String.class.equals(type)) {
            return new Converter<ResponseBody, String>() {
                @Override
                public String convert(ResponseBody value) throws IOException {
                    return value.string();
                }
            };
        }
        return null;
    }

    public Converter<?, RequestBody> requestBodyConverter(Type type, Annotation[] annotations, Retrofit retrofit) {

        if (String.class.equals(type)) {
            return new Converter<String, RequestBody>() {
                @Override
                public RequestBody convert(String value) throws IOException {
                    return RequestBody.create(MEDIA_TYPE, value);
                }
            };
        }
        return null;
    }
}

一些未知的小事項

最好不要通過插入器修改鏈接,會造成一些錯誤的結果,例如:重複修改鏈接,這個問題有待查明!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章