Android網(wǎng)絡(luò)篇(二)—— Retrofit的基本使用

在上一篇我們已經(jīng)學(xué)習(xí)了OkHttp的使用,接下來我們再來看看與OkHttp配套使用的Retrofit窍株。

定義:基于OkHttp的網(wǎng)絡(luò)請求封裝框架

什么意思呢距芬?其實(shí)Retrofit只是對我們傳遞的參數(shù)通過注解的方式來進(jìn)行封裝悬槽,實(shí)際的網(wǎng)絡(luò)請求工作依舊是由OkHttp來完成的。

優(yōu)點(diǎn):

(1)功能強(qiáng)大影斑,不僅支持同步與異步给赞,還支持多種數(shù)據(jù)格式解析。
(2)支持RxJava鸥昏,實(shí)現(xiàn)線程調(diào)度塞俱。
(3)簡潔易用,通過注解的方式配置網(wǎng)絡(luò)請求參數(shù)吏垮。
(4)擴(kuò)展性能好障涯,功能模塊高度解耦罐旗。

在Retrofit的使用過程中,有一個(gè)很重要的知識(shí)點(diǎn)就是關(guān)于注解的使用唯蝶,因?yàn)镽etrofit框架的本質(zhì)就是通過注解的方式對OkHttp的請求參數(shù)進(jìn)行封裝九秀,對返回的結(jié)果進(jìn)行封裝的一個(gè)框架,所以我們很有必要先來了解一下注解粘我。

注解:

首先來看一張圖


1.png

注解的類型分為三類:

第一類:網(wǎng)絡(luò)請求方法

2.png

(1)@GET鼓蜒、@POST、@PUT征字、@DELETE都弹、@HEAD。以上方法分別對應(yīng) HTTP中的網(wǎng)絡(luò)請求方式匙姜。以GET請求方法為例:

fun retrofitGet(view: View) {
    // 1.創(chuàng)建Retrofit的實(shí)例
    val retrofit = Retrofit.Builder()
        .baseUrl("http://www.baidu.com/") // 設(shè)置請求的完整域名
        .build()
    // 2.創(chuàng)建請求接口的實(shí)例
    val apiService = retrofit.create(ApiService::class.java)
    // 3.封裝請求
    val call = apiService.getRequest("more")
    // 4.執(zhí)行請求操作
    call.enqueue(object : Callback<ResponseBody> {
        override fun onResponse(call: Call<ResponseBody>, response: Response<ResponseBody>) {
            Log.i("retrofit", response.body().toString())
            Toast.makeText(this@NetWorkRetrofitActivity,response.body().toString(),Toast.LENGTH_SHORT).show()
        }

        override fun onFailure(call: Call<ResponseBody>, e: Throwable) {
            Log.i("retrofit", "請求失敗${e.printStackTrace()}")
        }
    })
}

public interface ApiService {

    @GET("{path}/")
    Call<ResponseBody> getRequest(@Path("path") String path);


}

Retrofit把網(wǎng)絡(luò)請求的URL 分成了兩部分設(shè)置畅厢,第一個(gè)部分是設(shè)置的baseUrl,即http://www.baidu.com/,而另外一部分則是在此基礎(chǔ)上進(jìn)行拼接組成的氮昧,通過注解的方式進(jìn)行替換框杜,所以上面的例子中最終的URL為:http://www.baidu.com/more

(2)@HTTP

· 作用:替換@GET袖肥、@POST咪辱、@PUT、@DELETE椎组、@HEAD注解的作用 及 更多功能拓展

· 具體使用:通過屬性method油狂、path、hasBody進(jìn)行設(shè)置

@HTTP(method = "GET", path = "{path}/", hasBody = false)
Call<ResponseBody> getHttpRequest(@Path("path") String path);

第二類:標(biāo)記類

3.png

(1)@FormUrlEncoded表示請求體是一個(gè)Form表單庐杨,以鍵值對的形式來傳遞參數(shù)选调。

// Post請求
@FormUrlEncoded
@POST("{user}/")
Observable<ResponseBody> postData(@Path("user") String user,@FieldMap Map<String, String> maps, @Query("meta[]") String... linked);
// 執(zhí)行Post請求(包含數(shù)組)
@Override
public void mHttpPost(Context context,String api, TreeMap map, String[] data, int type, HttpRequestCallback mCallBack) {
    map = HttpTool.getTreeCrc(map);
    Observable<String> observable = apiManager.postData(api,map, data);
    observable.subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribe(new BaseObserver(context, mCallBack, type));
}

(2)@Multipart表示請求體是一個(gè)支持文件的Form表單夹供。

// 上傳單個(gè)文件
@Multipart
@POST("{user}/")
Observable<String> upload(@Path("user") String user,@PartMap Map<String, RequestBody> maps, @Part MultipartBody.Part file);
@Override
public void mHttpFile(Context context,String api, File file, TreeMap map, int type, HttpRequestCallback mCallBack) {
    // 生成單個(gè)文件
    RequestBody requestFile = RequestBody.create(MediaType.parse("multipart/form-data"), file);
    MultipartBody.Part body = MultipartBody.Part.createFormData("avatar", file.getName(), requestFile);

    // 將所有的字段進(jìn)行轉(zhuǎn)換
    map = HttpTool.getTreeCrc(map);
    Map<String, RequestBody> mapValue = new HashMap<>();
    for (Object key : map.keySet()) {
        mapValue.put(key.toString(), HttpTool.convertToRequestBody(map.get(key).toString()));
    }
    Observable<String> observable = apiManager.upload(api,mapValue, body);
    observable.subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribe(new BaseObserver(context, mCallBack, type));
}

(3)@Streaming表示返回的數(shù)據(jù)以流的形式進(jìn)行返回灵份,比如下載大的文件等等都可以采用這種方式來實(shí)現(xiàn),舉一個(gè)例子:

public interface DownloadService {

  @Streaming
  @GET
  Call<ResponseBody> download(@Url String url);

}

public static void download(String url, final String path, final DownloadListener downloadListener) {

    Retrofit retrofit = new Retrofit.Builder()
        .baseUrl("[<u>http://www.xxx.com</u>](http://www.xxx.com)")
        //通過線程池獲取一個(gè)線程哮洽,指定callback在子線程中運(yùn)行填渠。
        .callbackExecutor(Executors.newSingleThreadExecutor())
        .build();

    DownloadService service = retrofit.create(DownloadService.class);
    Call<ResponseBody> call = service.download(url);
    call.enqueue(new Callback<ResponseBody>() {

      @Override
      public void onResponse(@NonNull Call<ResponseBody> call, @NonNull final Response<ResponseBody> response) {

        //將Response寫入到從磁盤中,詳見下面分析
        //注意鸟辅,這個(gè)方法是運(yùn)行在子線程中的
        writeResponseToDisk(path, response, downloadListener);

      }

      @Override

      public void onFailure(@NonNull Call<ResponseBody> call, @NonNull Throwable throwable) {
        downloadListener.onFail("網(wǎng)絡(luò)錯(cuò)誤~");
      }

    });

}

第三類:網(wǎng)絡(luò)請求參數(shù)

4.png

@Header & @Headers
?作用:添加請求頭 &添加不固定的請求頭
?具體使用如下:

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

// @Headers
@Headers("Authorization: authorization")
@GET("user")
Call<User> getUser()

// 以上的效果是一致的氛什。
// 區(qū)別在于使用場景和使用方式
// 1. 使用場景:@Header用于添加不固定的請求頭,@Headers用于添加固定的請求頭
// 2. 使用方式:@Header作用于方法的參數(shù)匪凉;@Headers作用于方法

當(dāng)然枪眉,我們還可以通過攔截器的方式去添加我們的頭文件,比如:

public class HttpHeaderInterceptor implements Interceptor {
    @Override
    public Response intercept(Chain chain) throws IOException {
        Request original = chain.request();
        int userId = IOFactoryUtil.getIOFactoryUtil().getDefaultHandler().getInt("user_id", 0);
        Log.i("zhoufan",userId+"");
        if (userId > 0) {
            Request request = original.newBuilder()
                    .header("userID", String.valueOf(userId))
                    .build();
            return chain.proceed(request);
        }
        return chain.proceed(original);
    }
}

@Body
作用:以 Post方式 傳遞 自定義數(shù)據(jù)類型 給服務(wù)器
特別注意:如果提交的是一個(gè)Map再层,那么作用相當(dāng)于 @Field
不過Map要經(jīng)過 FormBody.Builder 類處理成為符合 Okhttp 格式的表單贸铜,如:

FormBody.Builder builder = new FormBody.Builder();
builder.add("key","value");

@Field & @FieldMap
作用:發(fā)送 Post請求 時(shí)提交請求的表單字段
具體使用:與 @FormUrlEncoded 注解配合使用

@FormUrlEncoded
@POST("{user}/")
Observable<ResponseBody> postData(@Path("user") String user,@FieldMap Map<String, String> maps, @Query("meta[]") String... linked);

@Part & @PartMap
作用:發(fā)送 Post請求 時(shí)提交請求的表單字段
與@Field的區(qū)別:功能相同堡纬,但攜帶的參數(shù)類型更加豐富,包括數(shù)據(jù)流蒿秦,所以適用于 有文件上傳 的場景
具體使用:與 @Multipart 注解配合使用

@Multipart
@POST("{user}/")
Observable<String> upload(@Path("user") String user,@PartMap Map<String, RequestBody> maps, @Part MultipartBody.Part file);

@Query和@QueryMap
作用:用于 @GET 方法的查詢參數(shù)(Query = Url 中 ‘?’ 后面的 key-value)

@GET("{user}/")
Observable<String> getData(@Path("user") String user,@QueryMap TreeMap<String, Object> map);

@Path
作用:URL地址的缺省值
@Url
作用:直接傳入一個(gè)請求的 URL變量 用于URL設(shè)置
具體使用:

@GET
Observable<String> testUrlAndQuery(@Url String url, @Query("showAll") boolean showAll);
// 當(dāng)有URL注解時(shí)烤镐,@GET傳入的URL就可以省略
最后總結(jié)一下:
5.png

創(chuàng)建Retrofit的實(shí)例:

val retrofit = Retrofit.Builder()
    .baseUrl("http://www.baidu.com/") // 設(shè)置請求的完整域名
    .addConverterFactory(GsonConverterFactory.create()) // 設(shè)置數(shù)據(jù)解析器
    .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) // 支持RxJava平臺(tái)
    .build()
a. 關(guān)于數(shù)據(jù)解析器(Converter)

Retrofit支持多種數(shù)據(jù)解析方式
使用時(shí)需要在Gradle添加依賴
Scalars com.squareup.retrofit2:converter-scalars:2.0.2
Gson com.squareup.retrofit2:converter-gson:2.0.2
通常情況下我們選擇的是這兩種,其中Scalars代表的是基礎(chǔ)數(shù)據(jù)類型的解析棍鳖,而Gson代表的是Gson格式的解析炮叶。

b. 關(guān)于網(wǎng)絡(luò)請求適配器(CallAdapter)

Retrofit支持多種網(wǎng)絡(luò)請求適配器方式:guava、Java8和rxjava渡处,使用時(shí)如使用的是 Android 默認(rèn)的 CallAdapter镜悉,則不需要添加網(wǎng)絡(luò)請求適配器的依賴,否則則需要按照需求進(jìn)行添加Retrofit 提供的 CallAdapter医瘫,一般情況下我們選擇RxJava的適配器积瞒。

完整的請求例子:
(1)添加依賴
(2)添加網(wǎng)絡(luò)權(quán)限
(3)創(chuàng)建Retrofit實(shí)例
(4)創(chuàng)建用于描述網(wǎng)絡(luò)請求的接口
(5)對發(fā)送請求進(jìn)行封裝
(6)發(fā)送請求
(7)對返回?cái)?shù)據(jù)進(jìn)行解析

第一步:添加依賴
// okHttp網(wǎng)絡(luò)請求框架
// define a BOM and its version
implementation(platform("com.squareup.okhttp3:okhttp-bom:4.9.1"))

// define any required OkHttp artifacts without version
implementation("com.squareup.okhttp3:okhttp")
implementation("com.squareup.okhttp3:logging-interceptor")

//retrofit網(wǎng)絡(luò)請求框架
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
//retrofit添加Json解析返回?cái)?shù)據(jù)
implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
//retrofit添加RxJava支持
implementation 'com.jakewharton.retrofit:retrofit2-rxjava2-adapter:1.0.0'
第二步:添加網(wǎng)絡(luò)權(quán)限
<uses-permission android:name="android.permission.INTERNET"/>
剩余步驟:
fun retrofitGet(view: View) {
    // 3.創(chuàng)建Retrofit的實(shí)例
    val retrofit = Retrofit.Builder()
        .baseUrl("http://www.baidu.com/") // 設(shè)置請求的完整域名
        .addConverterFactory(GsonConverterFactory.create()) // 設(shè)置數(shù)據(jù)解析器
        .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) // 支持RxJava平臺(tái)
        .build()
    // 4.創(chuàng)建請求接口的實(shí)例
    val apiService = retrofit.create(ApiService::class.java)
    // 5.封裝請求
    val call = apiService.getHttpRequest("more")
    // 6.執(zhí)行請求操作
    call.enqueue(object : Callback<ResponseBody> {
        override fun onResponse(call: Call<ResponseBody>, response: Response<ResponseBody>) {
            // 7.解析返回?cái)?shù)據(jù)
            Log.i("retrofit", response.body().toString())
            Toast.makeText(this@NetWorkRetrofitActivity,response.body().toString(),Toast.LENGTH_SHORT).show()
        }

        override fun onFailure(call: Call<ResponseBody>, e: Throwable) {
            Log.i("retrofit", "請求失敗${e.printStackTrace()}")
        }
    })
}

注意:在設(shè)置數(shù)據(jù)轉(zhuǎn)換器的時(shí)候,通常情況下不能設(shè)置多個(gè)登下,因?yàn)闊o法保證后臺(tái)返回的數(shù)據(jù)類型同時(shí)滿足所有的轉(zhuǎn)換茫孔,一般情況下我們會(huì)使用Gson作為數(shù)據(jù)轉(zhuǎn)換的類型或者使用Scalars作為數(shù)據(jù)轉(zhuǎn)換的類型,如果我們需要對返回?cái)?shù)據(jù)進(jìn)行特殊處理被芳,那么可以考慮自定義數(shù)據(jù)轉(zhuǎn)換器缰贝。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市畔濒,隨后出現(xiàn)的幾起案子剩晴,更是在濱河造成了極大的恐慌,老刑警劉巖侵状,帶你破解...
    沈念sama閱讀 211,265評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件赞弥,死亡現(xiàn)場離奇詭異,居然都是意外死亡趣兄,警方通過查閱死者的電腦和手機(jī)绽左,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,078評論 2 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來艇潭,“玉大人拼窥,你說我怎么就攤上這事√D” “怎么了鲁纠?”我有些...
    開封第一講書人閱讀 156,852評論 0 347
  • 文/不壞的土叔 我叫張陵,是天一觀的道長鳍寂。 經(jīng)常有香客問我改含,道長,這世上最難降的妖魔是什么迄汛? 我笑而不...
    開封第一講書人閱讀 56,408評論 1 283
  • 正文 為了忘掉前任捍壤,我火速辦了婚禮刃唤,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘白群。我一直安慰自己尚胞,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,445評論 5 384
  • 文/花漫 我一把揭開白布帜慢。 她就那樣靜靜地躺著笼裳,像睡著了一般。 火紅的嫁衣襯著肌膚如雪粱玲。 梳的紋絲不亂的頭發(fā)上躬柬,一...
    開封第一講書人閱讀 49,772評論 1 290
  • 那天,我揣著相機(jī)與錄音抽减,去河邊找鬼允青。 笑死,一個(gè)胖子當(dāng)著我的面吹牛卵沉,可吹牛的內(nèi)容都是我干的颠锉。 我是一名探鬼主播,決...
    沈念sama閱讀 38,921評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼史汗,長吁一口氣:“原來是場噩夢啊……” “哼琼掠!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起停撞,我...
    開封第一講書人閱讀 37,688評論 0 266
  • 序言:老撾萬榮一對情侶失蹤瓷蛙,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后戈毒,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體艰猬,經(jīng)...
    沈念sama閱讀 44,130評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,467評論 2 325
  • 正文 我和宋清朗相戀三年埋市,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了冠桃。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,617評論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡恐疲,死狀恐怖腊满,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情培己,我是刑警寧澤,帶...
    沈念sama閱讀 34,276評論 4 329
  • 正文 年R本政府宣布胚泌,位于F島的核電站省咨,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏玷室。R本人自食惡果不足惜零蓉,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,882評論 3 312
  • 文/蒙蒙 一笤受、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧敌蜂,春花似錦箩兽、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,740評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至秸脱,卻和暖如春落包,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背摊唇。 一陣腳步聲響...
    開封第一講書人閱讀 31,967評論 1 265
  • 我被黑心中介騙來泰國打工咐蝇, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人巷查。 一個(gè)月前我還...
    沈念sama閱讀 46,315評論 2 360
  • 正文 我出身青樓有序,卻偏偏與公主長得像,于是被迫代替她去往敵國和親岛请。 傳聞我的和親對象是個(gè)殘疾皇子笔呀,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,486評論 2 348

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