OkHttp、rxJava、Retrofit聯合網路請求(二)

筆墨Android發表於2018-09-07

上一篇文章我們講到了OkHttp的一些相應的用法,如果沒有看的那就沒有看吧!這篇文章是不會用到相應內容的,因為Retrofit是基於OkHttp進行封裝的!所以很好的避開的相應的操作!!!就醬紫了。。。

慣例一張電影圖片鎮樓!!!這次就用《一生一世》來鎮樓吧!

OkHttp、rxJava、Retrofit聯合網路請求(二)

本文知識點

  • Retrofit的簡單使用
  • Retrofit註解的含義
  • Retrofit中一些常用的操作

重要的事情說三遍,網路許可權一定要加,一定要加!!!

1. Retrofit的簡單使用

Retrofit的簡單使用記住一下幾個步驟

  1. 建立Retrofit的例項
  2. 定義相應的介面,獲取代理物件
  3. 傳送請求
  4. 建立請求的回撥

其實以上的步驟和OkHttp的差不多,如果你沒看上一篇的話,那麼就記住上面的話就可以了!!!接下來我們一步一步實現,其實還是很簡單的!!!

1.1 建立Retrofit的例項

請求的地址是這個樣子滴:

http://api.jisuapi.com/news/get?channel=頭條&start=0&num=10&appkey=274ff62c9225cfa9
複製程式碼

我反手就是一串程式碼:

    Retrofit retrofit = new Retrofit.Builder()
            .baseUrl("http://api.jisuapi.com/news/")
            .addConverterFactory(GsonConverterFactory.create())
            .build();
複製程式碼

其實Retrofit是封裝了相應的OkHttp,所以寫法當然類似了但是有一點值得注意的,這裡有個baseUrl的概念,比如說你們公司所有的API的域名都是以http://api.XXX.xxx/開頭的,後面有指定的內容進行匹配。那麼這個baseUrl就可以這麼寫!拿上面這個地址為例:baseUrl就是http://api.jisuapi.com/news/注意:這個baseUrl的結尾一定是"\"結尾的! 如果沒有這個就會丟擲一個IllegalArgumentException異常!切記,切記。。。再說後面的addConverterFactory(GsonConverterFactory.create())這句,其實就是返回json的結構,因為現在一般的伺服器都是以json進行傳遞的。所以這句話一定要加上,否則也會丟擲IllegalArgumentException異常。記住就好了。

1.2 定義相應的介面,獲取代理物件

因為Retrofit中存在大量的註解,所有的代理物件都在相應的介面中完成,所以這個回事後面的講解重點,多以可以先不用理解相應的含義,後面會詳細講解。

public interface NewsService {
    @GET("get?channel=頭條&start=0&num=10&appkey=274ff62c9225cfa9")
    Call<News> getNews();
}
複製程式碼

簡單的說明一下:上面這個介面,代表是一個GET請求,請求成功後返回一個News物件。

NewsService newsService = retrofit.create(NewsService.class);
複製程式碼

然後回去相應的代理物件的程式碼。

1.3 傳送請求

其實這裡就是建立一個Call物件,來獲取相應的回撥!僅此而已。。。

Call<News> news = newsService.getNews();
複製程式碼

這裡面的方法,對應的是頂部介面的那個方法,從而獲取一個Call物件。

1.4 建立請求的回撥

這裡面的回撥和OkHttp的回撥基本上是一樣的,但是比OkHttp的更人性化而已!因為這裡直接可以獲取到相應的親求物件,而且不管你調幾次都不會有異常。並且回撥是同步的!是不是很6。。。

    news.enqueue(new Callback<News>() {
        @Override
        public void onResponse(Call<News> call, Response<News> response) {
            Log.e(TAG, "成功的回撥: " + response.body());
            News news = response.body();
            Log.e(TAG, "onResponse: " + news.toString());
        }

        @Override
        public void onFailure(Call<News> call, Throwable t) {
            Log.e(TAG, "失敗的回撥: " + t.toString());
        }
    });
複製程式碼

以上就是最簡單的一個請求了!下面我們看一下整體程式碼吧!

        /*1.建立Retrofit的例項*/
        Retrofit retrofit = new Retrofit.Builder()
                .baseUrl("http://api.jisuapi.com/news/")
                .addConverterFactory(GsonConverterFactory.create())
                .build();
        /*2.定義相應的介面,獲取代理物件*/
        NewsService newsService = retrofit.create(NewsService.class);
        /*3.傳送請求*/
        Call<News> news = newsService.getNews();
        /*4.建立請求的回撥*/
        news.enqueue(new Callback<News>() {
            @Override
            public void onResponse(Call<News> call, Response<News> response) {
                Log.e(TAG, "成功的回撥: " + response.body());
                News news = response.body();
            }

            @Override
            public void onFailure(Call<News> call, Throwable t) {
                Log.e(TAG, "失敗的回撥: " + t.toString());
            }
        });
複製程式碼

2. Retrofit註解的含義

其實Retrofit中的註解很多,但是基本上可以分為一下幾類:

  1. GET請求相關的註解
  2. POST請求相關的註解
  3. 公共請求的註解

2.1 GET請求相關的註解

在GET請求中無非就是更改中間的引數或者在後面拼接相應的引數而已無非專案中常用的就這幾種而已!

2.1.1 @Query 和 @QueryMap (用於GET請求新增引數)

這兩個註解用在GET請求中的我就不說了!咦,這不是說了嗎?其實說簡單點就是在後面追加引數的!!!兩個的區別呢?@Query能追加一個,@QueryMap能追加多個!剩下的都一樣了!!!

拿上面的地址舉個例子說明下@Query:

    /**
     * @author : 賀金龍
     * email : 753355530@qq.com
     * create at 2018/9/7  10:50
     * description : 演示@Query的示例
     */
    @GET("get?channel=頭條&start=0&num=10")
    Call<News> getNews(@Query("appkey") String qppKey);
複製程式碼

和上面的區別主要是我把最後一個引數放到getNews方法中傳遞了!呼叫的地方只要修改一個地方就行。

Call<News> news = newsService.getNews("274ff62c9225cfa9");
複製程式碼

其他的內容完全不用動,其實結果和上面一樣,這裡主要是為了演示而已

好再拿上面的地址舉個例子說明下@QueryMap:

    /**
     * @author : 賀金龍
     * email : 753355530@qq.com
     * create at 2018/9/7  10:50
     * description : 演示@QueryMap的示例
     */
    @GET("get?")
    Call<News> getNews(@QueryMap Map<String, String> map);
複製程式碼

和上面的區別在於我把所有的引數都放到Map中進行傳遞了!呼叫的地方修改成這樣就可以了!

Map<String, String> map = new HashMap<>();
        //channel = 頭條 & start = 0 & num = 10 & appkey = 274f f62c9225cfa9
        map.put("channel", "頭條");
        map.put("start", "0");
        map.put("num", "10");
        map.put("appkey", "274ff62c9225cfa9");

Call<News> news = newsService.getNews(map);
複製程式碼

結果還是一樣的,只是所有引數都通過map集合進行傳遞,以上方案選擇那個看你們需求就可以了!!!

2.1.2 @Path URL的預設值補充

其實這個很好理解,就是在URL地址中替換相應的路徑中的某一段名稱,有的URL地址中間有相應“/”分割的內容,path也就是替換這部分的內容;

這回我們拿一段新的URL演示一下@Path註解的用法:

//原始路徑地址:http://api.jisuapi.com/train/station2s?appkey=274ff62c9225cfa9&start=杭州&end=北京&ishigh=0

    /**
     * @author : 賀金龍
     * email : 753355530@qq.com
     * create at 2018/9/7  9:47
     * description : 演示@Path註解的示例
     */
    @GET("{path}/station2s?appkey=274ff62c9225cfa9&start=杭州&end=北京")
    Call<StationBean> getTrainStation(@Path("path") String path, @Query("ishigh") String ishigh);
複製程式碼

請不要看後面的"ishigh"那個傳遞,因為之前寫了一個引數的方法,所以才加上的!主要看path那塊的程式碼。這裡path替換的是"train"這個內容,沒有什麼記住寫法就可以了,呼叫的時候像下面這樣寫就可以了:

Call<StationBean> train = newsService.getTrainStation("train", "0");
複製程式碼

有的人會問這個有什麼用?其實對於上面這個請求真的沒有什麼用,怎麼說呢?有的Get請求中間這塊是一些使用者的引數,比如userId什麼的,是動態的話。上面的@Path註解就有相應的用處了。如果上面的地址改成http://api.jisuapi.com/uesrId/station2s?appkey=274ff62c9225cfa9&start=杭州&end=北京&ishigh=0userId是根據使用者的ID進行更改的,那麼就可以使用@Path這個註解了。好了剩下的場景自己發覺吧!!!

2.2 POST請求相關的註解

  1. @FormUrlEncoded和@Multipart
  2. @Field和@FieldMap 新增請求引數(與@FormUrlEncoded註解配合使用)
  3. @Part和@PartMap 新增請求引數(與@Multipart註解配合使用)
  4. @Body 封裝一個物件

上面最開始演示的是GET請求,對於POST請求小夥伴們還是不瞭解,所以這裡先給大家演示一下正常的POST請求是怎麼弄的!其實其他的程式碼都不用動,只需要更改相應的介面就可以了,像如下這樣:

    /**
     * @author : 賀金龍
     * email : 753355530@qq.com
     * create at 2018/9/7  11:51
     * description : 演示POST的請求內容
     */
    @FormUrlEncoded
    @POST("train/station2s?")
    Call<StationBean> getPostTrainStation(@FieldMap Map<String, String> map);
複製程式碼

使用的時候和上面一樣傳遞相應的內容就可以了!!!

2.2.1 @FormUrlEncoded和@Multipart註解

其實這個是和POST請求一起作用才有效的註解,怎麼理解呢?記得請求的時候有相應的檔案上傳吧?其實這個東西就是標記你是上傳檔案,還是上傳表單的;

  • @FormUrlEncoded 純表單形式(不包含上傳檔案)
  • @Multipart 表單形式(包含檔案上傳)

看上面那個案例,POST請求都應該新增@FormUrlEncoded或者@Multipart註解,否則會丟擲java.lang.IllegalArgumentException: @FieldMap parameters can only be used with form encoding. 的異常!

記住上面這些東西,下面用到的時候我會詳細講解的

2.2.2 @Field和@FieldMap 新增請求引數

千萬要注意上面兩個註解要與@FormUrlEncoded註解配合使用!!!傳送POST的表單請求。這裡注意@Field和@FieldMap只能攜帶String型別的引數!

@Field的使用和上面@Query的用法是類似的!只是更改一下相應的註解而已但是@FormUrlEncoded別忘記新增!!!

@FieldMap的案例參考上面getPostTrainStation的內容就可以了!!!所以這裡就不貼相應的程式碼了,請原諒我這種懶癌患者。。。

2.2.3 @Part和@PartMap 新增請求引數

千萬要注意上面兩個註解要與@Multipart註解配合使用!!!傳送POST的表單請求。這裡要注意於@Field和@FieldMap的區別在於攜帶的引數型別更加豐富,包括資料流,所以適用於“有檔案上傳”的場景!上面都說到了@Part和@PartMap有檔案上傳的功能,所以這裡就直接演示一下相應的檔案上傳,程式碼如下:

    @Multipart
    @POST("/form")
    Call<ResponseBody> testLoadFile(@Part("name") String name, @Part("age") String age, @Part MultipartBody.Part file);
複製程式碼

使用的時候程式碼如下:

    MediaType textType = MediaType.parse("text/plain");
    RequestBody name = RequestBody.create(textType, "Carson");
    RequestBody age = RequestBody.create(textType, "24");
    RequestBody file = RequestBody.create(MediaType.parse("application/octet-stream"), "這裡是模擬檔案的路徑");
    MultipartBody.Part body = MultipartBody.Part.createFormData("picture", "檔名稱", file);
複製程式碼

這裡最值得說明的就是MediaType.parse("application/octet-stream")這個代表上傳檔案的型別!

引數 說明
text/html HTML格式
text/plain 純文字格式
text/xml XML格式
image/gif gif圖片格式
image/jpeg jpg圖片格式
image/png png圖片格式
application/xhtml+xml XHTML格式
application/xml XML資料格式
application/atom+xml Atom XML聚合格式
application/json JSON資料格式
application/pdf pdf格式
application/msword Word文件格式
application/octet-stream 二進位制流資料

參照以上的型別就可以了!!!因為這裡不能用公司的地址去嘗試,找了半天沒有相應上傳的地址,所以我放棄了,不過你按照上面的方式處理就可以了,應該不會錯的。如果錯了你找我,我給你看看!!!

有人會說,我如果要傳一堆引數怎麼弄?也就是多個key/value的形式,其實你可以這麼弄!

    @Multipart
    @POST("/form")
    Call<ResponseBody> testLoadFileMore(@FieldMap Map<String, String> map, @Part MultipartBody.Part file);
複製程式碼

或者

    @Multipart
    @POST("/form")
    Call<ResponseBody> testLoadFileMore(@Part Map<String, RequestBody> map, @Part MultipartBody.Part file);
複製程式碼

怎麼選擇就看你專案裡面的需求了!!!

2.2.4 @Body 封裝一個物件

這個註解是用在POST請求的,這一點千萬要注意,其實就是把Body中每一個屬性取出來,以表單的形式傳給伺服器的!這裡傳遞的物件伺服器傳遞的引數不同是這樣的{"appkey":"274ff62c9225cfa9","end":"北京","ishigh":"0","start":"杭州"}首先你要明確一點,這個@Body是把內容放在body總傳遞地的!不是通過header傳遞的!!!

程式碼是這樣的:

    /**
     * @author : 賀金龍
     * email : 753355530@qq.com
     * create at 2018/9/7  14:57
     * description : 演示@Body註解的使用
     */
    @POST("train/station2s?")
    Call<StationBean> getPostTrainStationBody(@Body BodyBean bodyBean);
複製程式碼

傳遞的時候,正常傳遞就好了!!!

像下面這樣:

    BodyBean bodyBean = new BodyBean("274ff62c9225cfa9", "杭州", "北京", "0");
    Call<StationBean> postTrainStationBody = newsService.getPostTrainStationBody(bodyBean);
複製程式碼

注意點:

  1. 不要像POST請求那樣設定@FormUrlEncoded否則會丟擲@Body parameters cannot be used with form or multi-part encoding. (parameter #1)異常!
  2. 是傳遞到相應Request的body中,不是通過header傳遞的,這個一定要明確。不懂的話,問問後臺的人員!!!

2.3 一些公共的請求引數

2.3.1 @Header和@Headers

關於這兩個註解很好理解,就是新增請求頭的,但是@Header新增固定的請求頭,@Headers新增的是不固定的請求頭

這裡就直接貼一段程式碼就好了,看了你就能懂了

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

// @Headers
@Headers("Authorization: authorization")
@GET("user")
Call<User> getUser()
複製程式碼

如果你問我請求頭是什麼的話,你可以在Google瀏覽器中按F12然後給你一張圖!

OkHttp、rxJava、Retrofit聯合網路請求(二)

上面這個你要是不理解,找你們後臺的去問問,要麼就自己學習一下!我怕我講不懂,因為我不是伺服器的。。。

2.3.2 @Url 允許我們傳入一個URL地址(可以實現相應的重定向)

我是這麼理解的,其實我不知道這麼理解對不對,但是效果是能看見的!!!也就是我可以不管你baseUrl中設定的內容,這裡直接更改相應的URL地址!像下面這樣:

首先看一下baseUrl的設定

    Retrofit retrofit = new Retrofit.Builder()
            .baseUrl("http://api.jisuapi.com/news/")
            .addConverterFactory(GsonConverterFactory.create())
            .build();
複製程式碼

然後我們在看我們怎麼重新設定相應的地址:

    /**
     * @author : 賀金龍
     * email : 753355530@qq.com
     * create at 2018/9/7  9:47
     * description : 獲取鐵路資訊的介面
     */
    @GET
    Call<StationBean> getTrainStation(@Url String url);
複製程式碼

對就是通過@Url這個註解來重新定製相應的註解,所以這裡不管你之前的baseUrl什麼樣都會從這個url中獲取地址,來看一下完整的程式碼:

    /**
     * @author : 賀金龍
     * email : 753355530@qq.com
     * create at 2018/9/7  9:48
     * description : Retrofit更改請求網址的演示
     */
    public void retrofitChange(View view) {
        Retrofit retrofit = new Retrofit.Builder()
                .baseUrl("http://api.jisuapi.com/news/")
                .addConverterFactory(GsonConverterFactory.create())
                .build();
        NewsService newsService = retrofit.create(NewsService.class);
        Call<StationBean> trainStation = newsService.getTrainStation("http://api.jisuapi.com/train/station2s?appkey=274ff62c9225cfa9&start=杭州&end=北京&ishigh=0");
        trainStation.enqueue(new Callback<StationBean>() {
            @Override
            public void onResponse(Call<StationBean> call, Response<StationBean> response) {
                Log.e(TAG, "請求成功的話證明URL地址已經修改了" + response.body().toString());
            }

            @Override
            public void onFailure(Call<StationBean> call, Throwable t) {
                Request request = call.request();
                HttpUrl url = request.url();

                Log.e(TAG, "請求失敗的話列印一下URL地址" + url);
                Log.e(TAG, "onFailure: " + t);
            }
        });
    }
複製程式碼

其實這裡不管你baseUrl設定的地址怎麼樣?最後訪問的都是http://api.jisuapi.com/train/station2s?appkey=274ff62c9225cfa9&start=杭州&end=北京&ishigh=0 這個地址!

那麼很多人會問這個有什麼作用,想想你們公司正常的網路請求和修改頭像的這兩個地址一樣嗎?反正我們不一樣,所以才需要這個東西的!!!


基本上使用的時候就這麼多問題,可能有些講解不到的,如果有什麼不到位的,及時補充!!!我理解的也有限。有問題留言,我們一期討論!!!

github地址奉上

相關文章