Android 快速開發(fā)系列之網(wǎng)絡(luò)篇(Retrofit 2.0)

網(wǎng)絡(luò)請求庫有很多優(yōu)秀的開源項目okhttp,volley都是很不錯的,但是個人比較喜歡Retrofit积蔚。原因有以下幾點:
1.一個類型安全的REST客戶端 可免。
2.通過GsonConverter可以直接把服務器響應的json字符串映射成對象,這一切都是自動化的龙优。當然還有其他的轉(zhuǎn)換器并且支持自定義非常的靈活羊异。
3.支持同步請求和異步請求
4.2.0開始加入對Rxjava的支持,配合Rxjava編程爽爆了彤断,代碼變的很清晰野舶。
5.2.0開始可以很輕松的取消請求,你只需調(diào)用call.cancel()
6.性能和速度都比volley等更好。

重要的事情強調(diào)一下本文針對的是Retrofit 2.0講的宰衙,Retrofit 1.xx的版本和2.0版本有許多改動平道。

retrofit2.0 知識點

步驟:
1.首先導入需要的庫
compile 'com.squareup.retrofit2:retrofit:2.0.2'
compile 'com.squareup.retrofit2:converter-gson:2.0.2'

2.新建一個interface接口,用于存放和服務器交互的接口供炼,針對常用操作下面各寫一個范例

  • Get無參數(shù)請求
      public interface MeizhiApi {
      @GET("data/福利/10/{day}")//()里面的是相對路徑一屋,當然絕對路徑也是可以的
      public Call<BeanMezhi> getMeizhi(@Path("day") int day);//{}里面的是要替換的內(nèi)容 用注解@Path映射
    

}

- Get單個參數(shù)請求
```java
  public interface SmsApi {
  @GET("http://xxx/getSmsCode")
  public Call<JSONObject> getSms(@Query("tel") String tel);//相當于http://xxx/getSmsCode?tel="xx"
}
  • Get多個參數(shù)請求
    public interface SmsApi {
      @GET("http://weixing.wxspider.com:8087/appVoip!getSmsCode")
      public Call<JSONObject> getSms(@QueryMap Map<String, String> options);
    

}

- Post請求
```java
public interface DemoApi {
  @POST("checkupdate")
  public Call<BeanVersion> getVersion(@Body HashMap type);//注意這里用的是@Body 
}
  • 表單請求

public interface DemoApi {
@FormUrlEncoded
@POST("user/edit")
Call<User> updateUser(@Field("first_name") String first, @Field("last_name") String last);
}

- 文件上傳
```java
public interface DemoApi {
  @Multipart
  @PUT("user/photo")
  Call<User> updateUser(@Part("photo") RequestBody photo, @Part("description") RequestBody description);
}

注意:上面所有的接口返回的都是Call類型的這是和1.x版本不同的

3.實例化上面的接口

 public class ApiServiceManager {
 private static ApiServiceManager ourInstance = new ApiServiceManager();

 public static ApiServiceManager getInstance() {
     return ourInstance;
 }

 private DemoApi demoApi;
 private MeizhiApi meizhiApi;
 private SmsApi smsApi;

 private ApiServiceManager() {
     Retrofit.Builder builderWithGson = new Retrofit.Builder()
             .baseUrl(Config.DemoBaseUrl)//設(shè)置基礎(chǔ)url
             .addConverterFactory(GsonConverterFactory.create());//Gson轉(zhuǎn)換器直接返回對象
     Retrofit retrofit = builderWithGson.build();
     demoApi = retrofit.create(DemoApi.class);//拿到和服務器交互的接口實例
     builderWithGson.baseUrl(Config.MeizhiBaseUrl);
     retrofit = builderWithGson.build();
     meizhiApi = retrofit.create(MeizhiApi.class);
     Retrofit.Builder builderWithJson = new Retrofit.Builder()
             .addConverterFactory(JsonConverterFactory.create());//Json轉(zhuǎn)換器返回JSONObject窘疮,因為有些接口返回的數(shù)據(jù)很簡單不想寫個Bean
     retrofit = builderWithJson.build();
     smsApi = retrofit.create(SmsApi.class);
 }

 public DemoApi getDemoApi() {
     return demoApi;
 }

 public MeizhiApi getMeizhiApi() {
     return meizhiApi;
 }

 public SmsApi getSmsApi() {
     return smsApi;
 }
}

4.使用接口

  • 同步請求使用方式
     new Thread(new Runnable() {
              @Override
              public void run() {
                  Call<BeanMezhi> mezhiCall = ApiServiceManager.getInstance().getMeizhiApi().getMeizhi(1);
                  Response<BeanMezhi> response = null;//同步請求會阻塞線程,因此你不能在安卓的主線程中調(diào)用,不然會面臨NetworkOnMainThreadException,想調(diào)用execute方法陆淀,請在后臺線程執(zhí)行
                  try {
                      response = mezhiCall.execute();
                      if (response.body() != null) {//如果不能解析成對應的實體BeanMezhi則response.body()的值是空
                          Log.d(tag, new Gson().toJson(response.body()).toString());
                      }
                  } catch (IOException e) {
                      e.printStackTrace();
                  }
              }
          }).start();
    
  • 異步請求使用方式
    HashMap map = new HashMap();
        map.put("ostype", "android");
        Call<BeanVersion> versionCall = ApiServiceManager.getInstance().getDemoApi().getVersion(map);
        versionCall.enqueue(new Callback<BeanVersion>() {//異步請求
            @Override
            public void onResponse(Call<BeanVersion> call, Response<BeanVersion> response) {//回調(diào)運行在主線程
                if (response.body() != null) {
                    Log.d(tag, new Gson().toJson(response.body()).toString());
                }
            }
    
            @Override
            public void onFailure(Call<BeanVersion> call, Throwable t) {
                Log.d(tag, "onFail");
            }
        });
    

5.取消正在進行中的業(yè)務

 call.cancel();
  ```

基本使用介紹完了考余,如果需要自定義Converter或者自定義CallAdapter,那么請繼續(xù)往下看轧苫。

---
**1. Converter** 官方提供的轉(zhuǎn)換器有

Gson: com.squareup.retrofit:converter-gson

Jackson: com.squareup.retrofit:converter-jackson

Moshi: com.squareup.retrofit:converter-moshi

Protobuf: com.squareup.retrofit:converter-protobuf

Wire: com.squareup.retrofit:converter-wire

Simple XML: com.squareup.retrofit:converter-simplexml

如何自定義轉(zhuǎn)換器楚堤?
你也可以通過實現(xiàn)Converter.Factory接口來創(chuàng)建一個自定義的converter。以JsonConverter示例:
```java
public class JsonConverterFactory extends Converter.Factory {

  public static JsonConverterFactory create() {
      return new JsonConverterFactory();
  }

  @Override
  public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations, Retrofit retrofit) {
      return new JsonResponseBodyConverter<JSONObject>();
  }

  @Override
  public Converter<?, RequestBody> requestBodyConverter(Type type, Annotation[] parameterAnnotations, Annotation[] methodAnnotations, Retrofit retrofit) {
      return new JsonRequestBodyConverter<JSONObject>();
  }
}
final class JsonRequestBodyConverter<T> implements Converter<T, RequestBody> {
  private static final MediaType MEDIA_TYPE = MediaType.parse("application/json; charset=UTF-8");

  JsonRequestBodyConverter() {

  }

  public RequestBody convert(T value) throws IOException {
      return RequestBody.create(MEDIA_TYPE, value.toString());
  }
}
 final class JsonResponseBodyConverter<T> implements Converter<ResponseBody, T> {

   JsonResponseBodyConverter() {

   }

   @Override
   public T convert(ResponseBody value) throws IOException {
       JSONObject jsonObj;
       try {
           jsonObj = new JSONObject(value.string());
           return (T) jsonObj;
       } catch(JSONException e) {
           return null;
       }
   }
}

2. CallAdapter

在interface接口定義中含懊,retrofit2.0默認都是返回的Call<T>模式的身冬,如果我們想返回其他的類型也是可以,retrofit已經(jīng)為Rxjava粉絲們準備了CallAdapter岔乔,它將作為Observable返回酥筝。使用它必須引入以下兩個庫

compile 'io.reactivex:rxandroid:1.2.0'
compile 'com.squareup.retrofit2:adapter-rxjava:2.0.2'

并在Retrofit Builder鏈表中調(diào)用addCallAdapterFactory

Retrofit.Builder builderWithGson = new Retrofit.Builder()
              .baseUrl(Config.DemoBaseUrl)//設(shè)置基礎(chǔ)url
              .addConverterFactory(GsonConverterFactory.create())//Gson轉(zhuǎn)換器直接返回對象
              .addCallAdapterFactory(RxJavaCallAdapterFactory.create());//增加RxjavaCallAdapter
Retrofit retrofit = builderWithGson.build();

接下來看怎么結(jié)合Rxjava調(diào)用接口

  //定義接口
  @GET("data/福利/10/{day}")
  public Observable<BeanMezhi> getMeizhi2(@Path("day") int day);

  //調(diào)用接口
   Observable<BeanMezhi> observable = ApiServiceManager.getInstance().getMeizhiApi().getMeizhi2(1);
   observable.subscribeOn(Schedulers.io())//獲取數(shù)據(jù)指定運行在io線程
             .observeOn(AndroidSchedulers.mainThread())//發(fā)布到android主線程
             .subscribe(new Action1<BeanMezhi>() {
                  @Override
                  public void call(BeanMezhi beanMezhi) {
                      Log.d(tag, new Gson().toJson(beanMezhi).toString());//處理數(shù)據(jù)這里已經(jīng)是運行在主線程了
                  }
              });

在使用rxjava以后我們不在需要寫new Thread().start 這些“臟”代碼了,rxjava對線程的調(diào)度非常強大雏门,那么有同學會說感覺還不如返回Call<T>模式的然后異步調(diào)用來的簡單嘿歌。好吧,如果要對返回的數(shù)據(jù)先過濾茁影,在排序宙帝,還要存儲數(shù)據(jù)庫等等一系列處理,你該怎么辦募闲?有了rxjava處理這種復雜的數(shù)據(jù)流一切就會變的簡單清晰步脓,如果在配合lambda表達式可以說是如虎添翼。

項目主頁: http://square.github.io/retrofit/
JSONCoverter:https://github.com/brokge/Retrofit2.0-JSONCoverter

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末浩螺,一起剝皮案震驚了整個濱河市靴患,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌要出,老刑警劉巖鸳君,帶你破解...
    沈念sama閱讀 218,858評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異患蹂,居然都是意外死亡相嵌,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,372評論 3 395
  • 文/潘曉璐 我一進店門况脆,熙熙樓的掌柜王于貴愁眉苦臉地迎上來饭宾,“玉大人,你說我怎么就攤上這事格了】疵” “怎么了?”我有些...
    開封第一講書人閱讀 165,282評論 0 356
  • 文/不壞的土叔 我叫張陵盛末,是天一觀的道長弹惦。 經(jīng)常有香客問我否淤,道長,這世上最難降的妖魔是什么棠隐? 我笑而不...
    開封第一講書人閱讀 58,842評論 1 295
  • 正文 為了忘掉前任石抡,我火速辦了婚禮,結(jié)果婚禮上助泽,老公的妹妹穿的比我還像新娘啰扛。我一直安慰自己,他們只是感情好嗡贺,可當我...
    茶點故事閱讀 67,857評論 6 392
  • 文/花漫 我一把揭開白布隐解。 她就那樣靜靜地躺著,像睡著了一般诫睬。 火紅的嫁衣襯著肌膚如雪煞茫。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,679評論 1 305
  • 那天摄凡,我揣著相機與錄音续徽,去河邊找鬼。 笑死亲澡,一個胖子當著我的面吹牛炸宵,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播谷扣,決...
    沈念sama閱讀 40,406評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼捎琐!你這毒婦竟也來了会涎?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,311評論 0 276
  • 序言:老撾萬榮一對情侶失蹤瑞凑,失蹤者是張志新(化名)和其女友劉穎末秃,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體籽御,經(jīng)...
    沈念sama閱讀 45,767評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡练慕,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,945評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了技掏。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片铃将。...
    茶點故事閱讀 40,090評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖哑梳,靈堂內(nèi)的尸體忽然破棺而出劲阎,到底是詐尸還是另有隱情,我是刑警寧澤鸠真,帶...
    沈念sama閱讀 35,785評論 5 346
  • 正文 年R本政府宣布悯仙,位于F島的核電站龄毡,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏锡垄。R本人自食惡果不足惜沦零,卻給世界環(huán)境...
    茶點故事閱讀 41,420評論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望货岭。 院中可真熱鬧路操,春花似錦、人聲如沸茴她。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,988評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽丈牢。三九已至祭钉,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間己沛,已是汗流浹背慌核。 一陣腳步聲響...
    開封第一講書人閱讀 33,101評論 1 271
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留申尼,地道東北人垮卓。 一個月前我還...
    沈念sama閱讀 48,298評論 3 372
  • 正文 我出身青樓,卻偏偏與公主長得像师幕,于是被迫代替她去往敵國和親粟按。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,033評論 2 355

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