android study: Retrofit

Retrofit 簡(jiǎn)介

Retrofit 是一套由Square所開發(fā)維護(hù)疲眷,將RESTfulAPI 寫法規(guī)范和模塊化的函數(shù)庫虎忌。底層也使用他們的Okhttp瓤球,Retrofit 2默認(rèn)使用OKHttp作為網(wǎng)絡(luò)層,并且在它上面進(jìn)行構(gòu)建微渠。

官方描述:用于Android和Java的一個(gè)類型安全(type-safe)的REST客戶端

你將會(huì)用注解去描述HTTP請(qǐng)求霎苗,同時(shí)Retrofit默認(rèn)集成URL參數(shù)替換和查詢參數(shù).除此之外它還支持 Multipart請(qǐng)求和文件上傳争剿。

添加依賴

compile'com.squareup.retrofit2:converter-gson:2.2.0'

compile'com.squareup.okhttp3:logging-interceptor:3.8.0'

定義接口

在這一步已艰,需要將我們的 API 接口地址轉(zhuǎn)化成一個(gè) Java 接口。
我們的 API 接口地址為:

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

轉(zhuǎn)化寫成 Java 接口為

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

在此處 GET 的意思是 發(fā)送一個(gè) GET請(qǐng)求蚕苇,請(qǐng)求的地址為:baseUrl + "/users/{user}"哩掺。

{user} 類似于占位符的作用,具體類型由 repo(@Path("user") String user) 指定涩笤,這里表示 {user} 將是一段字符串嚼吞。

Call<TestModel> 是一個(gè)請(qǐng)求對(duì)象,<TestModel>表示返回結(jié)果是一個(gè) TestModel 類型的實(shí)例蹬碧。

定義 Model

請(qǐng)求會(huì)將 Json 數(shù)據(jù)轉(zhuǎn)化為 Java 實(shí)體類舱禽,所以我們需要自定義一個(gè) Model:

public class TestModel {
  private String login;

  public String getLogin() {
    return login;
  }

  public void setLogin(String login) {
    this.login = login;
  }
}

進(jìn)行連接通信

現(xiàn)在我們有了『要連接的 Http 接口』和 『要返回的數(shù)據(jù)結(jié)構(gòu)』,就可以開始執(zhí)行請(qǐng)求啦恩沽。

首先誊稚,構(gòu)造一個(gè) Retrofit 對(duì)象:

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

注意這里添加的 baseUrl 和 GsonConverter,前者表示要訪問的網(wǎng)站罗心,后者是添加了一個(gè)轉(zhuǎn)換器里伯。

接著,創(chuàng)建我們的 API 接口對(duì)象渤闷,這里 APIInterface 是我們創(chuàng)建的接口:
APIInterface service = retrofit.create(APIInterface.class);

使用 APIInterface 創(chuàng)建一個(gè)『請(qǐng)求對(duì)象』:
Call<TestModel> model = service.repo("Guolei1130");

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

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

可以看出這樣的方式有利于我們使用不同參數(shù)訪問同一個(gè) Web API 接口飒箭,比如你可以隨便改成 .repo("ligoudan")
最后狼电,就可以發(fā)送請(qǐng)求了!

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

至此弦蹂,我們就利用 Retrofit 完成了一次網(wǎng)絡(luò)請(qǐng)求漫萄。

GET 請(qǐng)求參數(shù)設(shè)置

在我們發(fā)送 GET 請(qǐng)求時(shí),如果需要設(shè)置 GET 時(shí)的參數(shù)盈匾,Retrofit 注解提供兩種方式來進(jìn)行配置腾务。分別是 @Query(一個(gè)鍵值對(duì))和 @QueryMap(多對(duì)鍵值對(duì))。

Call<TestModel> one(@Query("username") String username);
Call<TestModel> many(@QueryMap Map<String, String> params);

POST 請(qǐng)求參數(shù)設(shè)置

POST 的請(qǐng)求與 GET 請(qǐng)求不同削饵,POST 請(qǐng)求的參數(shù)是放在請(qǐng)求體內(nèi)的岩瘦。

所以當(dāng)我們要為 POST 請(qǐng)求配置一個(gè)參數(shù)時(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;
}

最后在獲取請(qǐng)求對(duì)象時(shí):
User user = new User("lgd","123456");
Call<TestModel> model = service.post(user);

就能完成 POST 請(qǐng)求參數(shù)的發(fā)送,注意該請(qǐng)求參數(shù) user 也會(huì)轉(zhuǎn)化成 Json 格式的對(duì)象發(fā)送到服務(wù)器启昧。

以上內(nèi)容摘自: http://www.reibang.com/p/b64a2de066c3 (對(duì)作者表示感謝)

我的總結(jié)

// 在鏈接時(shí)會(huì)替換掉URL中{}中的內(nèi)容叙凡,例如:[http://your.api-base.url/group/123/users](http://your.api-base.url/group/123/users)
@GET("/group/{id}/users")      //注意 字符串id
List<User> groupList(@Path("id") int groupId);  //注意 Path注解的參數(shù)要和前面的字符串一樣 id

// 例如:(http://your.api-base.url/group/123/users)
@GET("/group/{id}/{name}") 
List<User> groupList(@Path("id") int groupId, @Path("name")  String name); 

// 還支持查詢參數(shù),@Query相當(dāng)于在URL后加上問號(hào)和后面的參數(shù)密末,例如:(http://your.api-base.url/group/123/users?sort=1)
@GET("/group/{id}/users")
List<User> groupList(@Path("id")  int groupId, @Query("sort") String sort);
 
// 多個(gè)Query之間通過&連接握爷,例如 (http://your.api-base.url/group/123/users?newsId=123&sort=1)
@GET("/group/{id}/users")
List<User> groupList(@Path("id")  int groupId, @Query("newsId") String newsId, @Query("sort") String sort);

// 假如需要添加相同Key值,但是value卻有多個(gè)的情況严里,一種方式是添加多個(gè)@Query參數(shù)新啼,還有一種簡(jiǎn)便的方式是將所有的value放置在列表中,然后在同一個(gè)@Query下完成添加
// 例如:[http://your.api-base.url/group/123/users?newsId=123&newsId=345]
@GET("/group/{id}/users")
List<User> groupList(@Path("id") int groupId, @Query
("newsId") List<String> newsId);

 // 也可以多個(gè)參數(shù)在URL問號(hào)之后刹碾,且個(gè)數(shù)不確定燥撞,例如(http://your.api-base.url/group/123/users?newsId=123&sort=1)
@GET("/group/{id}/users")
List<User> groupList(@Path("id") int groupId, @QueryMap
 Map<String, String> map);

// 也可以為固定參數(shù)與動(dòng)態(tài)參數(shù)的混用
@GET("/group/{id}/users")
List<User> groupList(@Path("id") int groupId, @Query
("newsId") String newsId, @QueryMap Map<String, String> map);

  // Query非必填,也就是說即使不傳該參數(shù)迷帜,服務(wù)端也可以正常解析物舒,但請(qǐng)求方法定義處還是需要完整的Query注解,某次請(qǐng)求如果不需要傳該參數(shù)的話戏锹,只需填充null即可
List<User> repos = service.groupList(123, null);

// 若需要重新定義接口地址冠胯,可以使用@Url,將地址以參數(shù)的形式傳入即可
@GET
Call<List<Activity>> getActivityList(@Url String url, @QueryMap Map<String, String> map);

對(duì)請(qǐng)求進(jìn)行統(tǒng)一封裝

public abstract class BaseRequest<M> {

    protected abstract Call<M> getCall();

    private int tag;

    private WeakReference<IRequestCallback> callbackWeakReference;

    public void exe(final IRequestCallback requestCallback, final int tag) {

        final IRequestCallback callback = checkCallback();

        if (callback == null) return;
        callback.onRequestStart(tag);

        getCall().enqueue(new Callback<M>() {

            @Override
            public void onResponse(Call<M> call, Response<M> response) {

                if (response.isSuccessful()) {
                    if (callback == null) return;
                    callback.onRequestSucc(tag, response.body());
                } else {
                    String error = response.message();
                    int code = response.code();
                    String message = code + ":" + error;
                    ServerException exception = new ServerException(message);
                    callback.onRequestException(tag, exception);
                    LogUtil.log("base response 錯(cuò)誤:" + message);
                }

            }

            @Override
            public void onFailure(Call<M> call, Throwable e) {
                Throwable exception = e.getCause();
                IRequestCallback callback = checkCallback();
                if (callback == null) return;

                callback.onRequestException(tag, (Exception) exception);
            }
        });

        if (callback == null) return;
        callback.onRequestFinal(tag);
    }

    public void cancel() {
        getCall().cancel();
    }

編寫service

public interface TestService {

    @GET("jxc/merchant_info/query_merchant_info")
    Call<UserInfoGson> getUserInfoJSON(@Query("merchantNo") String merchantNo);

    /**
     *
     * 如果不需要轉(zhuǎn)換成Json數(shù)據(jù),可以用了ResponseBody;
     * @param merchantNo
     * @return
     */
    @GET("jxc/merchant_info/query_merchant_info")
    Call<ResponseBody> getUserInfoString(@Query("merchantNo") String merchantNo);

    @GET("jxc/merchant_info/query_merchant_info")
    Call<UserInfoGson> getUserInfoJSON(@QueryMap HashMap<String ,String> map);

    /**
     *
     * POST JSON 請(qǐng)求, 結(jié)果不轉(zhuǎn)成GSON 使用String
     *
     * @param route
     * @return
     */
    @Headers({"Content-type:application/json;charset=UTF-8"})
    @POST("/jxc/customer/add")
    Call<ResponseBody> addMember(@Body RequestBody route);

    @FormUrlEncoded
    @POST("/jxc/customer/add")
    Call<ResponseBody> addMember2(@Field("loginName") String username,
                                  @Field("password") String password);
}

編寫request

public class UserInfoRequest extends BaseRequest<TestService> {

    private HashMap<String , String> param;

    public UserInfoRequest(HashMap<String, String> param) {
        this.param = param;
    }

    //.getUserInfoJSON("66283")
    @Override
    protected Call<UserInfoGson> getCall() {
          return   NetFactory.getRetrofit().
                              create(getServiceClass()).
                                     getUserInfoJSON(param);
    }

    @Override
    protected Class<TestService> getServiceClass() {
        return TestService.class;
    }
}

發(fā)起請(qǐng)求
new UserInfoRequest(param).exe(callback, tag)

public class CustomConverterFactory extends Converter.Factory{

private Gson gson;

public CustomConverterFactory(Gson gson) {
    this.gson = gson;
}

@Override
public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations, Retrofit retrofit) {
    //return super.responseBodyConverter(type, annotations, retrofit);

    TypeAdapter<?> adapter = gson.getAdapter(TypeToken.get(type));
    return new CustomResponseConverter<>(gson, adapter);
}

@Override
public Converter<?, RequestBody> requestBodyConverter(Type type, Annotation[] parameterAnnotations, Annotation[] methodAnnotations, Retrofit retrofit) {
    return super.requestBodyConverter(type, parameterAnnotations, methodAnnotations, retrofit);
}

}

/**

public class CustomResponseConverter <T> implements Converter<ResponseBody, T> {

private final Gson gson;
private final TypeAdapter<T> adapter;

public CustomResponseConverter(Gson gson, TypeAdapter<T> adapter) {
    this.gson = gson;
    this.adapter = adapter;
}

@Override
public T convert(ResponseBody value) throws IOException {
    try {
        String body = value.string();

        LogUtil.log("converter response: " + body);

        return adapter.fromJson(body)

    } catch (Exception e) {
        throw new RuntimeException(e.getMessage());
    } finally {
        value.close();
    }
}

}

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末锦针,一起剝皮案震驚了整個(gè)濱河市涵叮,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌伞插,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,695評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件盾碗,死亡現(xiàn)場(chǎng)離奇詭異媚污,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)廷雅,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,569評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門耗美,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人航缀,你說我怎么就攤上這事商架。” “怎么了芥玉?”我有些...
    開封第一講書人閱讀 168,130評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵蛇摸,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我灿巧,道長(zhǎng)赶袄,這世上最難降的妖魔是什么揽涮? 我笑而不...
    開封第一講書人閱讀 59,648評(píng)論 1 297
  • 正文 為了忘掉前任,我火速辦了婚禮饿肺,結(jié)果婚禮上蒋困,老公的妹妹穿的比我還像新娘。我一直安慰自己敬辣,他們只是感情好雪标,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,655評(píng)論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著溉跃,像睡著了一般村刨。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上喊积,一...
    開封第一講書人閱讀 52,268評(píng)論 1 309
  • 那天烹困,我揣著相機(jī)與錄音,去河邊找鬼乾吻。 笑死髓梅,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的绎签。 我是一名探鬼主播枯饿,決...
    沈念sama閱讀 40,835評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼诡必!你這毒婦竟也來了奢方?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,740評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤爸舒,失蹤者是張志新(化名)和其女友劉穎蟋字,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體扭勉,經(jīng)...
    沈念sama閱讀 46,286評(píng)論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡鹊奖,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,375評(píng)論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了涂炎。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片忠聚。...
    茶點(diǎn)故事閱讀 40,505評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖唱捣,靈堂內(nèi)的尸體忽然破棺而出两蟀,到底是詐尸還是另有隱情,我是刑警寧澤震缭,帶...
    沈念sama閱讀 36,185評(píng)論 5 350
  • 正文 年R本政府宣布赂毯,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏欢瞪。R本人自食惡果不足惜活烙,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,873評(píng)論 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望遣鼓。 院中可真熱鬧啸盏,春花似錦、人聲如沸骑祟。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,357評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽次企。三九已至怯晕,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間缸棵,已是汗流浹背舟茶。 一陣腳步聲響...
    開封第一講書人閱讀 33,466評(píng)論 1 272
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留堵第,地道東北人吧凉。 一個(gè)月前我還...
    沈念sama閱讀 48,921評(píng)論 3 376
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像踏志,于是被迫代替她去往敵國(guó)和親阀捅。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,515評(píng)論 2 359

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