retrofit概念及案例

一、概念

Retrofit 是 Square 公司出品的默認基于 OkHttp 封裝的一套 RESTful 網絡請求框架,不了解 RESTful 概念的不妨去搜索學習下,RESTful 可以說是目前流行的一套 api 設計的風格,并不是標準。Retrofit 的封裝可以說是很強大,里面涉及到一堆的設計模式,你可以通過注解直接配置請求,你可以使用不同的 http 客戶端,雖然默認是用 http ,可以使用不同 Json Converter 來序列化數據,同時提供對 RxJava 的支持,使用** Retrofit + OkHttp + RxJava + Dagger2 **可以說是目前比較潮的一套框架,但是需要有比較高的門檻。

Retrofit與picasso一樣都是在okhttp基礎之上做的封裝,項目中可以直接用了。Retrofit因為也是Square出的,所以大家可能對它更崇拜些。Retrofit跟Volley是一個套路,但解耦的更徹底:比方說通過注解來配置請求參數,通過工廠來生成CallAdapter,Converter,你可以使用不同的請求適配器(CallAdapter), 比方說RxJava,Java8, Guava。你可以使用不同的反序列化工具(Converter),比方說json, protobuff, xml, moshi等等。炒雞解耦,里面涉及到超多設計模式,個人覺得是很經典的學習案例。雖然支持Java8, Guava你可能也不需要用到。xml,protobuff等數據格式你也可能不需要解析。but,萬一遇到鬼了呢。至于性能上,個人覺得這完全取決于請求client,也就是okhttp的性能,跟這些封裝工具沒太大關系。

Retrofit? 是一個 RESTful 的 HTTP 網絡請求框架的封裝。注意這里并沒有說它是網絡請求框架,主要原因在于網絡請求的工作并不是 Retrofit 來完成的。Retrofit 2.0 開始內置 OkHttp,前者專注于接口的封裝,后者專注于網絡請求的高效,二者分工協作,宛如古人的『你耕地來我織布』,小日子別提多幸福了。

我們的應用程序通過 Retrofit 請求網絡,實際上是使用 Retrofit 接口層封裝請求參數、Header、Url 等信息,之后由 OkHttp 完成后續的請求操作,在服務端返回數據之后,OkHttp 將原始的結果交給 Retrofit,后者根據用戶的需求對結果進行解析的過程。講到這里,你就會發現所謂 Retrofit,其實就是 Retrofitting OkHttp 了。

二、例一

參考野生西瓜--[Android] Retrofit 初步使用

1.定義接口

public interface APIInterface {

@GET("/users/{user}")

Call repo(@Path("user") String user);

}

GET 的意思是 發送一個 GET請求,請求的地址為:baseUrl + "/users/{user}"。{user} 類似于占位符的作用,具體類型由 repo(@Path("user") String user) 指定,這里表示 {user} 將是一段字符串。

Call 是一個請求對象,表示返回結果是一個 TestModel 類型的實例。

public class TestModel {

private String login;

public String getLogin() {

return login;

}

public void setLogin(String login) {

this.login = login;

}

}

2.構造一個 Retrofit 對象:

Retrofit retrofit= new Retrofit.Builder()

.baseUrl("https://api.github.com")

.addConverterFactory(GsonConverterFactory.create())

.build();

注意這里添加的 baseUrl 和 GsonConverter,前者表示要訪問的網站,后者是添加了一個JSON轉換器。

3.創建API 接口對象

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

Call model = service.repo("Guolei1130");

注意這里的 .repo("Guolei1130") 取代了前面的 {user}。到這里,我們要訪問的地址就成了:

https://api.github.com/users/Guolei1130

可以看出這樣的方式有利于我們使用不同參數訪問同一個 Web API 接口,比如你可以隨便改成 .repo("ligoudan")

4.最后,就可以發送請求了!

model.enqueue(new Callback() {

@Override

public void onResponse(Call call, Response response) {

// Log.e("Test", response.body().getLogin());

System.out.print(response.body().getLogin());

}

@Override

public void onFailure(Call call, Throwable t) {

System.out.print(t.getMessage());

}

});

5.另外,說說POST 請求參數設置

POST 的請求與 GET 請求不同,POST 請求的參數是放在請求體內的。所以當我們要為 POST 請求配置一個參數時,需要用到 @Body 注解:

Call post(@Body User user);

//這里的 User 類型是需要我們去自定義的:

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 model = service.post(user);

三、例二

參考Retrofit使用教程(一)

使用的是百度的API Store提供的API,地址在此:手機號碼歸屬地__API服務_API服務_API Store.

1.設置model

訪問該API返回的數據格式如下:

{

"errNum": 0,

"retMsg": "success",

"retData": {

"phone": "15210011578",

"prefix": "1521001",

"supplier": "移動",

"province": "北京",

"city": "北京",

"suit": "152卡"

}

}

根據返回結果我們創建數據對象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.構造接口

public interface PhoneService {

@GET("/apistore/mobilenumber/mobilenumber")

Call 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.創建Retrofit對象

Retrofit retrofit = new Retrofit.Builder()

.addConverterFactory(GsonConverterFactory.create())//解析方法

.baseUrl(BASE_URL)//主機地址

.build();

//2.創建訪問API的請求

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

Call call = service.getResult(API_KEY, phoneView.getText().toString());

//3.發送請求

call.enqueue(new Callback() {

@Override

public void onResponse(Call call, Response response) {

//4.處理結果

if (response.isSuccess()){

PhoneResult result = response.body();

if (result != null){

PhoneResult.RetDataEntity entity = result.getRetData();

}

}

}

@Override

public void onFailure(Call 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 query(@Query("purpose_codes") String codes, @Query("queryDate") String date,

@Query("from_station") String from, @Query("to_station") String to)

2.head

//比如要更新某個賬戶信息,其接口地址為/info,需要帶的Header有設備信息device,

//系統版本version,還要帶請求參數要更新賬戶的id,代碼如下:

@POST("/info")

Call updateInfo(@Header("device") String device, @Header("version") int version,

@Field("id") String id);

五、Retrofit中使用RxJava

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

//使用RxJava我們則返回一個可被觀測的PhoneResult:Observable,如下:

@GET("/apistore/mobilenumber/mobilenumber")

Observable getPhoneResult(@Header("apikey") String apikey,

@Query("phone") String phone);

//為了能返回此對象,我們需要在創建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())? ? //子線程訪問網絡

.observeOn(AndroidSchedulers.mainThread())? //回調到主線程

.subscribe(new Observer() {

@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());

}

}

});

}

源碼地址

[https://github.com/WuXiaolong/AndroidMVPSample]

關于MVP、Retrofit、RxJava,之前已經分別做了分享,如果您還沒有閱讀過,可以猛戳:

1、Android MVP 實例

2、Android Retrofit 2.0使用

3、RxJava

4、RxBus

假設,您對MVP、Retrofit、RxJava已經有了一點了解,那么我們開始本文:

Android MVP優化

1、MVP綁定Activity(Fragment)生命周期按照之前的文章,每個Presenter都得初始化和銷毀,我新加MvpActivity(MvpFragment),加了抽象方法protected abstract P createPresenter();

這樣做的目的在需要使用MVP的地方,可以繼承MvpActivity(MvpFragment),然后初始化和銷毀就不用手動一個個去加了。

2、接口請求等還是放到MVP的P中

這個圖片,在當時寫MVP文章時給出的,實際開發中,我發現每個都這樣寫,實在是增加了不少代碼,然接口請求放到P中,還有個好處,就是MVP綁定Activity(Fragment)生命周期,當onDestroy時取消RXJava注冊,以避免內存泄露。

代碼

目錄結構

如圖,有個大致了解:

Paste_Image.png

mvp:所有的mvp都放在這個包下retrofit:Retrofit接口和配置文件rxjava:RxJava一些回調設置ui:Activity或fragment,建議按功能再細分包

核心代碼

還是就貼出核心代碼吧,源碼在我的github上(https://github.com/WuXiaolong/AndroidMVPSample)。

MainActivity入口,還是演示的之前的MVP的天氣的接口,接口請求方法放在Presenter。

MvpActivity

Presenter綁定Activity(Fragment)生命周期

public abstract class MvpActivity

extends BaseActivity { protected P mvpPresenter; @Override protected void onCreate(Bundle savedInstanceState) { mvpPresenter = createPresenter(); super.onCreate(savedInstanceState); } protected abstract P createPresenter(); @Override protected void onDestroy() { super.onDestroy(); if (mvpPresenter != null) { mvpPresenter.detachView(); } }}

MainPresenter

apiStores.loadData方法是Retrofit做的網絡請求,回調是RxJava完成的。

public class MainPresenter extends BasePresenter { public MainPresenter(MainView view) { attachView(view); }? public void loadData(String cityId) { mvpView.showLoading(); addSubscription(apiStores.loadData(cityId), new SubscriberCallBack<>(new ApiCallback() { @Override public void onSuccess(MainModel model) { mvpView.getDataSuccess(model); } @Override public void onFailure(int code, String msg) { mvpView.getDataFail(msg); } @Override public void onCompleted() { mvpView.hideLoading(); } })); }}

apiStores.loadData

是不是很簡單,關于Retrofit配置,詳見源碼AppClient。

public interface ApiStores { //baseUrl String API_SERVER_URL = "http://www.weather.com.cn/"; //加載天氣 @GET("adat/sk/{cityId}.html") Observable loadData(@Path("cityId") String cityId);}

RxJava回調方法

這里onError,寫了如果網絡請求用httpcode來判斷。當然可以不要。\

private ApiCallback apiCallback; public SubscriberCallBack(ApiCallback apiCallback) { this.apiCallback = apiCallback; } @Override public void onCompleted() { apiCallback.onCompleted(); } @Override public void onError(Throwable e) { e.printStackTrace(); if (e instanceof HttpException) { HttpException httpException = (HttpException) e; //httpException.response().errorBody().string() int code = httpException.code(); String msg = httpException.getMessage(); if (code == 504) { msg = "網絡不給力"; } apiCallback.onFailure(code, msg); } else { apiCallback.onFailure(0, e.getMessage()); } apiCallback.onCompleted(); } @Override public void onNext(T t) { apiCallback.onSuccess(t); }}

BasePresenter

再來看看BasePresenter,這里做了Presenter初始化和銷毀(包括RXjava取消注冊),調用在MvpActivity。

public class BasePresenter implements Presenter { public V mvpView; public ApiStores apiStores = AppClient.retrofit().create(ApiStores.class); private CompositeSubscription mCompositeSubscription; @Override public void attachView(V mvpView) { this.mvpView = mvpView; } @Override public void detachView() { this.mvpView = null; onUnsubscribe(); } //RXjava取消注冊,以避免內存泄露 public void onUnsubscribe() { if (mCompositeSubscription != null && mCompositeSubscription.hasSubscriptions()) { mCompositeSubscription.unsubscribe(); } } public void addSubscription(Observable observable, Subscriber subscriber) { if (mCompositeSubscription == null) { mCompositeSubscription = new CompositeSubscription(); } mCompositeSubscription.add(observable .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(subscriber)); }}

總結

三者結合使用,重點還是對MVP的優化,Retrofit只貼出最簡單的(后續會寫Retrofit詳情使用),Rxjava可能我是對它認識尚淺,實際運用最多還是RxBus。

作者:Android_lml

鏈接:http://www.lxweimin.com/p/9a01b8807818

來源:簡書

著作權歸作者所有。商業轉載請聯系作者獲得授權,非商業轉載請注明出處。

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 173,285評論 25 708
  • Spring Cloud為開發人員提供了快速構建分布式系統中一些常見模式的工具(例如配置管理,服務發現,斷路器,智...
    卡卡羅2017閱讀 134,923評論 18 139
  • 楚江微雨0707閱讀 165評論 0 2
  • day15/21 今天讀書時間:22:00-23:00 今日讀書分享: 昨天因為趕夜路三點多才睡,今天看的內容有點...
    兜兜與狗蛋閱讀 451評論 0 0
  • 人類擁有三大硬性需求:求生本能、性沖動和渴望偉大。 如果你愿意去回顧自己的人生,你會發現你的幾乎所有行為都是圍繞著...
    市場小白雪俠俠閱讀 267評論 0 0