Retrofit 網(wǎng)絡(luò)請(qǐng)求學(xué)習(xí)筆記

之前在掘金胃珍、簡(jiǎn)書(shū)上看到很多篇介紹retrofit的文章蓝牲,但是一直沒(méi)有靜下心來(lái)研究這到底是什么東西傅蹂。最近時(shí)間比較充裕励稳,仔細(xì)了解了一下retrofit這個(gè)網(wǎng)絡(luò)請(qǐng)求工具的用法及實(shí)現(xiàn)原理∧硝澹現(xiàn)在做下簡(jiǎn)單的記錄犬金,以便日后重溫。

1六剥、Retrofit是什么晚顷?

Retrofit官網(wǎng)的介紹:A type-safe HTTP client for Android and Java ,一個(gè)類型安全的HTTP請(qǐng)求工具(現(xiàn)在對(duì)于類型安全理解不是太到位)疗疟。
Retrofit 是 Square 公司出品的默認(rèn)基于 OkHttp 封裝的一套 RESTful 網(wǎng)絡(luò)請(qǐng)求框架该默。Retrofit 的封裝可以說(shuō)是很強(qiáng)大,里面涉及到一堆的設(shè)計(jì)模式策彤,你可以通過(guò)注解直接配置請(qǐng)求 栓袖,可以使用不同 Json Converter 來(lái)序列化數(shù)據(jù)匣摘,同時(shí)提供對(duì) RxJava 的支持,使用 Retrofit + OkHttp + RxJava + Dagger2 可以說(shuō)是目前比較潮的一套框架裹刮,但是需要有比較高的門(mén)檻音榜。


2、基本用法

既然是網(wǎng)絡(luò)請(qǐng)求工具捧弃,那么最基本的功能就是發(fā)送HTTP請(qǐng)求赠叼,常用的GET、POST請(qǐng)求违霞。再有就是一些請(qǐng)求設(shè)置嘴办,比如超時(shí)時(shí)間、header等买鸽。

2.1 GET請(qǐng)求

先定義一個(gè)接口

 /**
 * Created by dell on 2016/9/7.
 *
 * 定義HTTP API使用接口
 *
 * 接口其實(shí)就是Retrofit對(duì)一個(gè)HTTP請(qǐng)求的描述
 */
public interface GitHubService {

    @FormUrlEncoded //將會(huì)自動(dòng)將請(qǐng)求參數(shù)的類型調(diào)整為application/x-www-form-urlencoded
    @POST("query")  // 請(qǐng)求地址是 baseurl + query
    Call<PostQueryInfo> search(@Field("type") String type, @Field("postid") String postid);//@Field 參數(shù)放到請(qǐng)求體

    @GET("query")
    /**
     * @Query表示請(qǐng)求參數(shù)涧郊,將會(huì)以key=value的方式拼接在url后面
     *
     * 如果參數(shù)很多, 可以使用@QueryMap , getInfo(@QueryMap Map<String,String> map)
     */
    Call<PostQueryInfo> getInfo(@Query("type") String type, @Query("postid") String postid);

     @GET("/users/{user}")
     Call<User> getFeed(@Path("user") String user);  //@Path動(dòng)態(tài)的URL訪問(wèn)癞谒。像上面get請(qǐng)求中的{user}可以把它當(dāng)做一個(gè)占位符底燎,通過(guò)@Path("user")標(biāo)注的參數(shù)進(jìn)行替換
}

發(fā)送請(qǐng)求

 /**
     * 模擬GET 請(qǐng)求
     */
    @OnClick(R.id.btn_get)
    public void onClick() {
        //retrofit是通過(guò)注解將HTTP轉(zhuǎn)化為java接口
        Retrofit retrofit = new Retrofit.Builder()
                .client(getNewClient())//設(shè)置不同的底層網(wǎng)絡(luò)庫(kù)
                .baseUrl("http://www.kuaidi100.com/")
//                .addCallAdapterFactory(RxJavaCallAdapterFactory.create())//添加 RxJava 適配器
                .addConverterFactory(GsonConverterFactory.create())//通過(guò)addConverterFactory來(lái)添加轉(zhuǎn)化器來(lái)完成數(shù)據(jù)轉(zhuǎn)化 ,把json數(shù)據(jù)轉(zhuǎn)成model對(duì)象
                .build();
        GitHubService service = retrofit.create(GitHubService.class);
        Call<PostQueryInfo> call = service.getInfo("ems","1045861721780");
//        try {
//            User user = call.execute().body();//同步請(qǐng)求 刃榨,網(wǎng)絡(luò)請(qǐng)求一定要在子線程中完成弹砚,不能直接在UI線程執(zhí)行,不然會(huì)crash
//        } catch (IOException e) {
//            e.printStackTrace();
//        }
        //異步請(qǐng)求
        call.enqueue(new Callback<PostQueryInfo>() {
            @Override
            public void onResponse(Call<PostQueryInfo> call, Response<PostQueryInfo> response) {
                pb.dismiss();
                if (response.isSuccessful()){
                    PostQueryInfo user = response.body();
                    Toast.makeText(RetrofitActivity.this, user.getMessage(), Toast.LENGTH_LONG).show();
                    Log.i(TAG, "user:" + user.getMessage());
                }else{
                    ResponseBody errorBody = response.errorBody();
                }
            }
            @Override
            public void onFailure(Call<PostQueryInfo> call, Throwable t) {
                pb.dismiss();
                Log.e(TAG, "onFailure: " + t.getMessage());
            }
        });
//        call.cancel();//取消請(qǐng)求
    }

   private OkHttpClient getNewClient() {
        /**
         * HttpLoggingInterceptor提供了4中控制打印信息類型的等級(jí)枢希,分別是NONE桌吃,BASIC,HEADERS苞轿,BODY茅诱,接下來(lái)分別來(lái)說(shuō)一下相應(yīng)的打印信息類型。
         *
         * NONE  沒(méi)有任何日志信息
         * Basic  打印請(qǐng)求類型搬卒,URL瑟俭,請(qǐng)求體大小,返回值狀態(tài)以及返回值的大小
         * Headers 打印返回請(qǐng)求和返回值的頭部信息契邀,請(qǐng)求類型摆寄,URL以及返回值狀態(tài)碼
         * Body  打印請(qǐng)求和返回值的頭部和body信息
         *
         */
        HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
        logging.setLevel(HttpLoggingInterceptor.Level.BODY);
        int cacheSize = 10 * 1024 * 1024; // 10 MiB
        Cache cache = new Cache(this.getCacheDir(), cacheSize);
        return new OkHttpClient.Builder()
//                .addInterceptor(new CustomInterceptor())
                .addInterceptor(logging)
                .cache(cache)
//                .readTimeout()
//                .writeTimeout()
                .connectTimeout(10, TimeUnit.SECONDS)
                .build();
    }

2.2 POST請(qǐng)求

在上一步驟中定義的接口,已經(jīng)包含了POST請(qǐng)求坯门,即用@POST這個(gè)注解修飾的方法微饥。

定義完接口之后,發(fā)送請(qǐng)求的方法和GET請(qǐng)求的方法類似

 Retrofit retrofit = new Retrofit.Builder()
                .baseUrl("http://www.kuaidi100.com/")
                .client(getNewClient())
                .addConverterFactory(GsonConverterFactory.create())//添加轉(zhuǎn)換器古戴,gson數(shù)據(jù)轉(zhuǎn)成 model對(duì)象
                .build();
        /**
         *
         * Retrofit對(duì)象傳了一個(gè)GitHubService接口的Class對(duì)象欠橘,怎么又返回一個(gè)GitHubService對(duì)象呢?
         *
         * create方法重要就是返回了一個(gè)動(dòng)態(tài)代理對(duì)象
         *
         * 動(dòng)態(tài)代理對(duì)象 作用:當(dāng)你要調(diào)用某個(gè)類的方法前现恼,插入你想要執(zhí)行的代碼肃续。
         */
        GitHubService service = retrofit.create(GitHubService.class);
        /**
         * service 是一個(gè)動(dòng)態(tài)代理對(duì)象黍檩,并不是GitHubService 的 implements對(duì)象。當(dāng)service對(duì)象調(diào)用search方法時(shí)始锚,執(zhí)行的是動(dòng)態(tài)代理方法建炫。
         *
         * 動(dòng)態(tài)代理發(fā)揮了它的作用,你看上去是調(diào)用了contributors方法疼蛾,其實(shí)此時(shí)Retrofit把Github接口翻譯成一個(gè)HTTP請(qǐng)求
         *
         * 后為你生成了一個(gè)Http請(qǐng)求肛跌,然后,你調(diào)用call.enqueue方法時(shí)就發(fā)送了這個(gè)請(qǐng)求察郁,然后你就可以處理Response的數(shù)據(jù)了
         */
        Call<PostQueryInfo> call = service.search("ems", "1045861721780");

//        PostBodyService service = retrofit.create(PostBodyService.class);
//        Call<PostQueryInfo> call = service.getPostInfo(new PostInfo("ems","1045861721780"));
        /**
         *  同步請(qǐng)求 衍慎, 不能在main線程中執(zhí)行
         */
//        try {
//            PostQueryInfo info = call.execute().body();
//        } catch (IOException e) {
//            e.printStackTrace();
//        }
        /**
         * 異步請(qǐng)求
         */
        call.enqueue(new Callback<PostQueryInfo>() {
            @Override
            public void onResponse(Call<PostQueryInfo> call, Response<PostQueryInfo> response) {
                pb.dismiss();
                if (response.isSuccessful()){
                    String msg = response.body().getMessage();
                    Log.i(TAG, "msg:" + msg);
                    Toast.makeText(RetrofitActivity.this, msg, Toast.LENGTH_LONG).show();
                }else{
                    ResponseBody requestBody = response.errorBody();
                }
            }
            @Override
            public void onFailure(Call<PostQueryInfo> call, Throwable t) {
                pb.dismiss();
                String msg = t.getMessage();
                Log.e(TAG, "msg:" + msg);
            }
        });

2.3 自定義client : 可以設(shè)置超時(shí)時(shí)間、緩存等

 private OkHttpClient getNewClient() {
        /**
         * HttpLoggingInterceptor提供了4中控制打印信息類型的等級(jí)皮钠,分別是NONE稳捆,BASIC,HEADERS麦轰,BODY乔夯,接下來(lái)分別來(lái)說(shuō)一下相應(yīng)的打印信息類型。
         *
         * NONE  沒(méi)有任何日志信息
         * Basic  打印請(qǐng)求類型款侵,URL末荐,請(qǐng)求體大小,返回值狀態(tài)以及返回值的大小
         * Headers 打印返回請(qǐng)求和返回值的頭部信息新锈,請(qǐng)求類型甲脏,URL以及返回值狀態(tài)碼
         * Body  打印請(qǐng)求和返回值的頭部和body信息
         *
         */
        HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
        logging.setLevel(HttpLoggingInterceptor.Level.BODY);
        int cacheSize = 10 * 1024 * 1024; // 10 MiB
        Cache cache = new Cache(this.getCacheDir(), cacheSize);
        return new OkHttpClient.Builder()
//                .addInterceptor(new CustomInterceptor())
                .addInterceptor(logging)
                .cache(cache)
//                .readTimeout()
//                .writeTimeout()
                .connectTimeout(10, TimeUnit.SECONDS)
                .build();
    }

Retrofit retrofit = new Retrofit.Builder()
                .client(getNewClient())//設(shè)置不同的底層網(wǎng)絡(luò)庫(kù)
                .baseUrl("http://www.kuaidi100.com/")
//                .addCallAdapterFactory(RxJavaCallAdapterFactory.create())//添加 RxJava 適配器
                .addConverterFactory(GsonConverterFactory.create())//通過(guò)addConverterFactory來(lái)添加轉(zhuǎn)化器來(lái)完成數(shù)據(jù)轉(zhuǎn)化 ,把json數(shù)據(jù)轉(zhuǎn)成model對(duì)象
                .build();

2.4 轉(zhuǎn)換器 : 用什么方式解析服務(wù)端返回的數(shù)據(jù),支持gson妹笆、jackson块请、xml等

Gson: com.squareup.retrofit2:converter-gson:2.1.0
Moshi: com.squareup.retrofit2:converter-moshi:2.1.0
Jackson: com.squareup.retrofit2:converter-jackson:2.1.0
SimpleXML: com.squareup.retrofit2:converter-simplexml:2.1.0
ProtoBuf: com.squareup.retrofit2:converter-protobuf:2.1.0
Wire: com.squareup.retrofit2:converter-wire:2.1.0           

如果上面的轉(zhuǎn)換器還不夠你使用的話,你可以通過(guò)自己實(shí)現(xiàn) Converter.Factory 來(lái)自定義轉(zhuǎn)換器拳缠。

2.5 添加header :有兩種方式墩新,靜態(tài)和動(dòng)態(tài)

2.5.1 靜態(tài)方式

通過(guò)@Headers注解。如:下面這段是官方文檔上的窟坐。
@Headers({
    "Accept: application/vnd.github.v3.full+json",
    "User-Agent: Retrofit-Sample-App"
})                              
@GET("query")
Call<PostQueryInfo> getInfo(@Query("type") String type, @Query("postid") String postid);

或者也可以通過(guò)Interceptor來(lái)定義靜態(tài)請(qǐng)求頭

public class RequestInterceptor implements Interceptor {
        @Override
             public Response intercept(Chain chain) throws IOException {
                Request original = chain.request();
                Request request = original.newBuilder()
                        .header("User-Agent", "Your-App-Name")
                        .header("Accept", "application/vnd.yourapi.v1.full+json")
                        .method(original.method(), original.body())
                        .build();
                    return chain.proceed(request);
                }
         }

2.5.2 動(dòng)態(tài)添加 : 在定義的接口 方法中海渊,添加參數(shù)

@GET("/user") 
Call<User> getUser(@Header("Authorization") String authorization , @Query("q") String name)

2.6 開(kāi)啟log日志

Retrofit官方提供了一個(gè)很方便查看日志的Interceptor,你可以控制你需要的打印信息類型狸涌,使用方法也很簡(jiǎn)單切省。

添加依賴:compile 'com.squareup.okhttp3:logging-interceptor:3.4.1'

HttpLoggingInterceptor提供了4中控制打印信息類型的等級(jí),分別是NONE帕胆,BASIC朝捆,HEADERS,BODY懒豹,接下來(lái)分別來(lái)說(shuō)一下相應(yīng)的打印信息類型芙盘。
* NONE 沒(méi)有任何日志信息
* Basic 打印請(qǐng)求類型驯用,URL,請(qǐng)求體大小儒老,返回值狀態(tài)以及返回值的大小
* Headers 打印返回請(qǐng)求和返回值的頭部信息蝴乔,請(qǐng)求類型,URL以及返回值狀態(tài)碼
* Body 打印請(qǐng)求和返回值的頭部和body信息
也是在自定義client中設(shè)置:
HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
logging.setLevel(HttpLoggingInterceptor.Level.BODY);
client.addInterceptor(logging);
此后步驟參考2.3 中的getNewClient();

2.7 常用注解解釋:

@GET和@POST分別是get和post請(qǐng)求驮樊。括號(hào)里面的value值與上面.baseUrl組成完整的路徑
@Path動(dòng)態(tài)的URL訪問(wèn)薇正。像上面get請(qǐng)求中的{user}可以把它當(dāng)做一個(gè)占位符,通過(guò)@Path("user")標(biāo)注的參數(shù)進(jìn)行替換
@Query請(qǐng)求參數(shù)囚衔。無(wú)論是GET或POST的參數(shù)都可以用它來(lái)實(shí)現(xiàn)
@QueryMap請(qǐng)求參數(shù)使用Map集合挖腰。可以傳遞一個(gè)map集合對(duì)象
@FormUrlEncoded將會(huì)自動(dòng)將請(qǐng)求參數(shù)的類型調(diào)整為application/x-www-form-urlencoded 练湿,不能用于Get請(qǐng)求
@Body實(shí)體請(qǐng)求參數(shù)猴仑。顧名思義可以傳遞一個(gè)實(shí)體對(duì)象來(lái)作為請(qǐng)求的參數(shù),不過(guò)實(shí)體屬性要與參數(shù)名一一致(在測(cè)試代碼中肥哎,用這種方式傳遞參數(shù)辽俗,會(huì)報(bào)錯(cuò),就是@Body標(biāo)簽不能同時(shí)和@FormUrlEncoded篡诽、@Multipart標(biāo)簽同時(shí)使用)
@field :注解將每一個(gè)請(qǐng)求參數(shù)都存放至請(qǐng)求體中崖飘,還可以添加encoded參數(shù)
@fieldMap :POST請(qǐng)求參數(shù)有多個(gè)時(shí), 可以使用這個(gè)
@Multipart 文件上傳
@Url"對(duì)一個(gè)方法的參數(shù)進(jìn)行注解霞捡,表明這是個(gè)url ,可以傳遞一個(gè)完整的url地址

取消請(qǐng)求

在頁(yè)面退出時(shí)坐漏,要把正在進(jìn)行的請(qǐng)求取消,否則再回調(diào)后容易發(fā)生異常碧信。
單個(gè)請(qǐng)求可以調(diào)用call.cancel()

3、實(shí)現(xiàn)原理

動(dòng)態(tài)代理機(jī)制

參考文檔

1街夭、Android網(wǎng)絡(luò)訪問(wèn)庫(kù) - Retrofit學(xué)習(xí)(1)基礎(chǔ)
2砰碴、Android 網(wǎng)絡(luò)開(kāi)源庫(kù)-Retrofit(一)簡(jiǎn)單介紹
3、Retrofit 2.0:迄今為止最大的更新最好的Android HTTP客戶端庫(kù)
4板丽、Retrofit用法詳解(有各種常見(jiàn)用法樣例)
5呈枉、Retrofit源碼1: 為什么寫(xiě)一個(gè)interface就可以實(shí)現(xiàn)http請(qǐng)求
6、關(guān)于Retrofit的用法:Retrofit 是什么埃碱,1猖辫、Retrofit 怎么用,2砚殿、Retrofit的原理是什么
7啃憎、使用Retrofit2.0+OkHttp3.0實(shí)現(xiàn)緩存處理
8、 Android網(wǎng)絡(luò)請(qǐng)求庫(kù) - Say hello to retrofit
9似炎、Retrofit 官網(wǎng)
10辛萍、我們真的需要使用RxJava+Retrofit嗎悯姊?
11、拆輪子系列:拆 Retrofit
12贩毕、「Android技術(shù)匯」Retrofit2 源碼解析和案例說(shuō)明

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末悯许,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子辉阶,更是在濱河造成了極大的恐慌先壕,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,919評(píng)論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件谆甜,死亡現(xiàn)場(chǎng)離奇詭異启上,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)店印,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,567評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門(mén)冈在,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人按摘,你說(shuō)我怎么就攤上這事包券。” “怎么了炫贤?”我有些...
    開(kāi)封第一講書(shū)人閱讀 163,316評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵溅固,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我兰珍,道長(zhǎng)侍郭,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,294評(píng)論 1 292
  • 正文 為了忘掉前任掠河,我火速辦了婚禮亮元,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘唠摹。我一直安慰自己爆捞,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,318評(píng)論 6 390
  • 文/花漫 我一把揭開(kāi)白布勾拉。 她就那樣靜靜地躺著煮甥,像睡著了一般。 火紅的嫁衣襯著肌膚如雪藕赞。 梳的紋絲不亂的頭發(fā)上成肘,一...
    開(kāi)封第一講書(shū)人閱讀 51,245評(píng)論 1 299
  • 那天,我揣著相機(jī)與錄音斧蜕,去河邊找鬼双霍。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的店煞。 我是一名探鬼主播蟹演,決...
    沈念sama閱讀 40,120評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼顷蟀!你這毒婦竟也來(lái)了酒请?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 38,964評(píng)論 0 275
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤鸣个,失蹤者是張志新(化名)和其女友劉穎羞反,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體囤萤,經(jīng)...
    沈念sama閱讀 45,376評(píng)論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡昼窗,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,592評(píng)論 2 333
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了涛舍。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片澄惊。...
    茶點(diǎn)故事閱讀 39,764評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖富雅,靈堂內(nèi)的尸體忽然破棺而出掸驱,到底是詐尸還是另有隱情,我是刑警寧澤没佑,帶...
    沈念sama閱讀 35,460評(píng)論 5 344
  • 正文 年R本政府宣布毕贼,位于F島的核電站,受9級(jí)特大地震影響蛤奢,放射性物質(zhì)發(fā)生泄漏鬼癣。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,070評(píng)論 3 327
  • 文/蒙蒙 一啤贩、第九天 我趴在偏房一處隱蔽的房頂上張望待秃。 院中可真熱鬧,春花似錦瓜晤、人聲如沸锥余。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,697評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至嘲恍,卻和暖如春足画,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背佃牛。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,846評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工淹辞, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人俘侠。 一個(gè)月前我還...
    沈念sama閱讀 47,819評(píng)論 2 370
  • 正文 我出身青樓象缀,卻偏偏與公主長(zhǎng)得像蔬将,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子央星,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,665評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容