Android 網(wǎng)絡(luò)(四) Retrofit學(xué)習(xí)筆記

參考
Android開源項目推薦之「網(wǎng)絡(luò)請求哪家強」
野生西瓜--[Android] Retrofit 初步使用
野生西瓜--[Android] Retrofit 源碼分析之執(zhí)行流程
野生西瓜--[Android] Retrofit 源碼分析之 Retrofit 對象
Retrofit使用教程(一)
Retrofit使用教程(二)
Retrofit使用教程(三):Retrofit與RxJava初相逢
深入淺出 Retrofit
快速Android開發(fā)系列網(wǎng)絡(luò)篇之Retrofit
Retrofit2 完全解析 探索與okhttp之間的關(guān)系
拆輪子系列:拆 Retrofit
'Retrofit源碼1: 為什么寫一個interface就可以實現(xiàn)http請求'

一、概念

Retrofit 是 Square 公司出品的默認基于 OkHttp 封裝的一套 RESTful 網(wǎng)絡(luò)請求框架隘谣,不了解 RESTful 概念的不妨去搜索學(xué)習(xí)下腋颠,RESTful 可以說是目前流行的一套 api 設(shè)計的風格烂斋,并不是標準袱巨。Retrofit 的封裝可以說是很強大深滚,里面涉及到一堆的設(shè)計模式吓坚,你可以通過注解直接配置請求瞧栗,你可以使用不同的 http 客戶端,雖然默認是用 http ,可以使用不同 Json Converter 來序列化數(shù)據(jù)恰画,同時提供對 RxJava 的支持逗载,使用** Retrofit + OkHttp + RxJava + Dagger2 **可以說是目前比較潮的一套框架,但是需要有比較高的門檻糠涛。

Retrofit與picasso一樣都是在okhttp基礎(chǔ)之上做的封裝,項目中可以直接用了兼犯。Retrofit因為也是Square出的忍捡,所以大家可能對它更崇拜些。Retrofit跟Volley是一個套路切黔,但解耦的更徹底:比方說通過注解來配置請求參數(shù)砸脊,通過工廠來生成CallAdapter,Converter纬霞,你可以使用不同的請求適配器(CallAdapter), 比方說RxJava凌埂,Java8, Guava。你可以使用不同的反序列化工具(Converter)诗芜,比方說json, protobuff, xml, moshi等等瞳抓。炒雞解耦,里面涉及到超多設(shè)計模式伏恐,個人覺得是很經(jīng)典的學(xué)習(xí)案例孩哑。雖然支持Java8, Guava你可能也不需要用到脐湾。xml臭笆,protobuff等數(shù)據(jù)格式你也可能不需要解析。but秤掌,萬一遇到鬼了呢愁铺。至于性能上,個人覺得這完全取決于請求client闻鉴,也就是okhttp的性能茵乱,跟這些封裝工具沒太大關(guān)系。

Retrofit 是一個 RESTful 的 HTTP 網(wǎng)絡(luò)請求框架的封裝孟岛。注意這里并沒有說它是網(wǎng)絡(luò)請求框架瓶竭,主要原因在于網(wǎng)絡(luò)請求的工作并不是 Retrofit 來完成的督勺。Retrofit 2.0 開始內(nèi)置 OkHttp,前者專注于接口的封裝斤贰,后者專注于網(wǎng)絡(luò)請求的高效智哀,二者分工協(xié)作,宛如古人的『你耕地來我織布』荧恍,小日子別提多幸福了瓷叫。

我們的應(yīng)用程序通過 Retrofit 請求網(wǎng)絡(luò),實際上是使用 Retrofit 接口層封裝請求參數(shù)送巡、Header摹菠、Url 等信息,之后由 OkHttp 完成后續(xù)的請求操作骗爆,在服務(wù)端返回數(shù)據(jù)之后次氨,OkHttp 將原始的結(jié)果交給 Retrofit,后者根據(jù)用戶的需求對結(jié)果進行解析的過程摘投。講到這里煮寡,你就會發(fā)現(xiàn)所謂 Retrofit,其實就是 Retrofitting OkHttp 了谷朝。

二洲押、例一

參考野生西瓜--[Android] Retrofit 初步使用
1.定義接口

public interface APIInterface {
  @GET("/users/{user}")
  Call<TestModel> repo(@Path("user") String user);
}

GET 的意思是 發(fā)送一個 GET請求武花,請求的地址為:baseUrl + "/users/{user}"圆凰。{user} 類似于占位符的作用,具體類型由 repo(@Path("user") String user) 指定体箕,這里表示 {user} 將是一段字符串专钉。
Call<TestModel> 是一個請求對象,<TestModel>表示返回結(jié)果是一個 TestModel 類型的實例累铅。

public class TestModel {
  private String login;
  public String getLogin() {
    return login;
  }
  public void setLogin(String login) {
    this.login = login;
  }
}

2.構(gòu)造一個 Retrofit 對象:

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

注意這里添加的 baseUrl 和 GsonConverter跃须,前者表示要訪問的網(wǎng)站,后者是添加了一個JSON轉(zhuǎn)換器娃兽。

3.創(chuàng)建API 接口對象

APIInterface service = retrofit.create(APIInterface.class);
Call<TestModel> model = service.repo("Guolei1130");

注意這里的 .repo("Guolei1130") 取代了前面的 {user}菇民。到這里,我們要訪問的地址就成了:
https://api.github.com/users/Guolei1130
可以看出這樣的方式有利于我們使用不同參數(shù)訪問同一個 Web API 接口投储,比如你可以隨便改成 .repo("ligoudan")

4.最后第练,就可以發(fā)送請求了!

model.enqueue(new Callback<TestModel>() {
  @Override
  public void onResponse(Call<TestModel> call, Response<TestModel> response) {
    // Log.e("Test", response.body().getLogin());
    System.out.print(response.body().getLogin());
  }

  @Override
  public void onFailure(Call<TestModel> call, Throwable t) {
    System.out.print(t.getMessage());
  }
  });

5.另外玛荞,說說POST 請求參數(shù)設(shè)置
POST 的請求與 GET 請求不同娇掏,POST 請求的參數(shù)是放在請求體內(nèi)的。所以當我們要為 POST 請求配置一個參數(shù)時勋眯,需要用到 @Body 注解:

Call<TestModel> post(@Body User user);
//這里的 User 類型是需要我們?nèi)プ远x的:
public class User {
  public String username;
  public String password;

  public User(String username,String password){
    this.username = username;
    this.password = password;
}
//最后在獲取請求對象時:
User user = new User("lgd","123456");
Call<TestModel> model = service.post(user);
三婴梧、例二

參考Retrofit使用教程(一)
使用的是百度的API Store提供的API,地址在此:手機號碼歸屬地__API服務(wù)_API服務(wù)_API Store.
1.設(shè)置model
訪問該API返回的數(shù)據(jù)格式如下:

{
    "errNum": 0,
    "retMsg": "success",
    "retData": {
        "phone": "15210011578",
        "prefix": "1521001",
        "supplier": "移動",
        "province": "北京",
        "city": "北京",
        "suit": "152卡"
    }
}

根據(jù)返回結(jié)果我們創(chuàng)建數(shù)據(jù)對象PhoneResult,如下:

public class PhoneResult {
    /**
     * errNum : 0
     * retMsg : success
     * retData : {"phone":"15210011578","prefix":"1521001","supplier":"移動","province":"北京","city":"北京","suit":"152卡"}
     */
    private int errNum;
    private String retMsg;
    /**
     * phone : 15210011578
     * prefix : 1521001
     * supplier : 移動
     * province : 北京
     * city : 北京
     * suit : 152卡
     */
    private RetDataEntity retData;

    public void setErrNum(int errNum) {
        this.errNum = errNum;
    }

    public void setRetMsg(String retMsg) {
        this.retMsg = retMsg;
    }

    public void setRetData(RetDataEntity retData) {
        this.retData = retData;
    }

    public int getErrNum() {
        return errNum;
    }

    public String getRetMsg() {
        return retMsg;
    }

    public RetDataEntity getRetData() {
        return retData;
    }

    public static class RetDataEntity {
        private String phone;
        private String prefix;
        private String supplier;
        private String province;
        private String city;
        private String suit;

        public void setPhone(String phone) {
            this.phone = phone;
        }

        public void setPrefix(String prefix) {
            this.prefix = prefix;
        }

        public void setSupplier(String supplier) {
            this.supplier = supplier;
        }

        public void setProvince(String province) {
            this.province = province;
        }

        public void setCity(String city) {
            this.city = city;
        }

        public void setSuit(String suit) {
            this.suit = suit;
        }

        public String getPhone() {
            return phone;
        }

        public String getPrefix() {
            return prefix;
        }

        public String getSupplier() {
            return supplier;
        }

        public String getProvince() {
            return province;
        }

        public String getCity() {
            return city;
        }

        public String getSuit() {
            return suit;
        }
    }
}

2.構(gòu)造接口

public interface PhoneService {
    @GET("/apistore/mobilenumber/mobilenumber")
    Call<PhoneResult> getResult(@Header("apikey") String apikey, @Query("phone") String phone);
}

3.使用

private static final String BASE_URL = "http://apis.baidu.com";
private static final String API_KEY = "8e13586b86e4b7f3758ba3bd6c9c9135";

private void query(){
    //1.創(chuàng)建Retrofit對象
    Retrofit retrofit = new Retrofit.Builder()
            .addConverterFactory(GsonConverterFactory.create())//解析方法
            .baseUrl(BASE_URL)//主機地址
            .build();
            
    //2.創(chuàng)建訪問API的請求
    PhoneService service = retrofit.create(PhoneService.class);
    Call<PhoneResult> call = service.getResult(API_KEY, phoneView.getText().toString());
    
    //3.發(fā)送請求
    call.enqueue(new Callback<PhoneResult>() {
        @Override
        public void onResponse(Call<PhoneResult> call, Response<PhoneResult> response) {
            //4.處理結(jié)果
            if (response.isSuccess()){
                PhoneResult result = response.body();
                if (result != null){
                    PhoneResult.RetDataEntity entity = result.getRetData();
                }
            }
        }

        @Override
        public void onFailure(Call<PhoneResult> call, Throwable t) {

        }
    });
}
四下梢、query,head

參考Retrofit使用教程(二)
1.query

//如12306的查詢接口https://kyfw.12306.cn/otn/lcxxcx/query?
//purpose_codes=ADULT&queryDate=2016-03-18&from_station=BJP&to_station=CDW,寫法如下:
@GET("/otn/lcxxcx/query")
Call<Result> query(@Query("purpose_codes") String codes, @Query("queryDate") String date,
    @Query("from_station") String from, @Query("to_station") String to)

2.head

//比如要更新某個賬戶信息,其接口地址為/info,需要帶的Header有設(shè)備信息device,
//系統(tǒng)版本version,還要帶請求參數(shù)要更新賬戶的id,代碼如下:
@POST("/info")
Call<Object> updateInfo(@Header("device") String device, @Header("version") int version,
                        @Field("id") String id);
五、Retrofit中使用RxJava

參考Retrofit使用教程(三):Retrofit與RxJava初相逢

//使用RxJava我們則返回一個可被觀測的PhoneResult:Observable<PhoneResult>,如下:
@GET("/apistore/mobilenumber/mobilenumber")
Observable<PhoneResult> getPhoneResult(@Header("apikey") String apikey,
                                       @Query("phone") String phone);
//為了能返回此對象,我們需要在創(chuàng)建Retrofit對象時添加一個RxJava對象的Adapter來自動完成:
Retrofit retrofit = new Retrofit.Builder()
        .baseUrl(BASE_URL)
        .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
        .addConverterFactory(GsonConverterFactory.create())
        .build();
//獲取手機的歸屬地
phoneService.getPhoneResult(PhoneApi.API_KEY, number)
        .subscribeOn(Schedulers.newThread())    //子線程訪問網(wǎng)絡(luò)
        .observeOn(AndroidSchedulers.mainThread())  //回調(diào)到主線程
        .subscribe(new Observer<PhoneResult>() {
            @Override
            public void onCompleted() {}

            @Override
            public void onError(Throwable e) {}

            @Override
            public void onNext(PhoneResult result) {
                if (result != null && result.getErrNum() == 0) {
                    PhoneResult.RetDataEntity entity = result.getRetData();
                    resultView.append("地址:" + entity.getCity());
                }
            }
        });
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末塞蹭,一起剝皮案震驚了整個濱河市孽江,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌番电,老刑警劉巖竟坛,帶你破解...
    沈念sama閱讀 217,826評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異钧舌,居然都是意外死亡担汤,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,968評論 3 395
  • 文/潘曉璐 我一進店門洼冻,熙熙樓的掌柜王于貴愁眉苦臉地迎上來崭歧,“玉大人,你說我怎么就攤上這事撞牢÷誓耄” “怎么了?”我有些...
    開封第一講書人閱讀 164,234評論 0 354
  • 文/不壞的土叔 我叫張陵屋彪,是天一觀的道長所宰。 經(jīng)常有香客問我,道長畜挥,這世上最難降的妖魔是什么仔粥? 我笑而不...
    開封第一講書人閱讀 58,562評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮蟹但,結(jié)果婚禮上躯泰,老公的妹妹穿的比我還像新娘。我一直安慰自己华糖,他們只是感情好麦向,可當我...
    茶點故事閱讀 67,611評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著客叉,像睡著了一般诵竭。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上兼搏,一...
    開封第一講書人閱讀 51,482評論 1 302
  • 那天卵慰,我揣著相機與錄音,去河邊找鬼向族。 笑死呵燕,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的件相。 我是一名探鬼主播再扭,決...
    沈念sama閱讀 40,271評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼氧苍,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了泛范?” 一聲冷哼從身側(cè)響起让虐,我...
    開封第一講書人閱讀 39,166評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎罢荡,沒想到半個月后赡突,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,608評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡区赵,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,814評論 3 336
  • 正文 我和宋清朗相戀三年惭缰,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片笼才。...
    茶點故事閱讀 39,926評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡漱受,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出骡送,到底是詐尸還是另有隱情昂羡,我是刑警寧澤,帶...
    沈念sama閱讀 35,644評論 5 346
  • 正文 年R本政府宣布摔踱,位于F島的核電站虐先,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏派敷。R本人自食惡果不足惜蛹批,卻給世界環(huán)境...
    茶點故事閱讀 41,249評論 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望膀息。 院中可真熱鬧般眉,春花似錦、人聲如沸潜支。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,866評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽冗酿。三九已至,卻和暖如春络断,著一層夾襖步出監(jiān)牢的瞬間裁替,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,991評論 1 269
  • 我被黑心中介騙來泰國打工貌笨, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留弱判,地道東北人。 一個月前我還...
    沈念sama閱讀 48,063評論 3 370
  • 正文 我出身青樓锥惋,卻偏偏與公主長得像昌腰,于是被迫代替她去往敵國和親开伏。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,871評論 2 354

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