Retrofit使用指南

Retrofit is a type-safe HTTP client for Android and Java.

Retrofit是面向Android和Java平臺(tái)的一個(gè)類(lèi)型安全的HTTP客戶(hù)端毯盈。

<br />
本文將圍繞Retrofit的注解、CallAdapter(配合RxJava)恨胚、Converter進(jìn)行介紹撞羽;

首先在你的工程添加以下依賴(lài):

final OKHTTP_VERSION = '3.4.1'
final RETROFIT_VERSION = '2.1.0'
compile "com.squareup.okhttp3:okhttp:$OKHTTP_VERSION"
compile "com.squareup.okhttp3:logging-interceptor:$OKHTTP_VERSION"
compile "com.squareup.retrofit2:retrofit:$RETROFIT_VERSION"
compile "com.squareup.retrofit2:converter-gson:$RETROFIT_VERSION"

版本號(hào)如有更新的可以更改到最新

接下來(lái)演示如何請(qǐng)求接口:
假設(shè)我們現(xiàn)在需要請(qǐng)求一個(gè)這樣的接口: https://api.github.com/users/yuhengye
首先創(chuàng)建一個(gè)TestService接口

import retrofit2.Call;
import retrofit2.http.GET;

public interface TestService {

    //定義一條接口請(qǐng)求
    @GET("users/yuhengye")
    Call<String> getUserInfo();
}

先不關(guān)心為何創(chuàng)建一個(gè)這樣的接口
接下來(lái)創(chuàng)建一個(gè)RetrofitManager.class類(lèi)

import okhttp3.OkHttpClient;
import okhttp3.logging.HttpLoggingInterceptor;
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;

public class RetrofitManager {

    private static Retrofit mRetrofit;

    private static TestService mTestService;

    private static OkHttpClient mOkHttpClient;

    public static OkHttpClient getOkHttpClient(){
        if(mOkHttpClient == null) {
            HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
            logging.setLevel(HttpLoggingInterceptor.Level.BASIC);
            mOkHttpClient = new OkHttpClient.Builder()
                    .addInterceptor(logging)
                    .build();
        }
        return mOkHttpClient;
    }

    public static Retrofit getRetrofit(){
        if(mRetrofit == null) {
            mRetrofit = new Retrofit.Builder()
                    .baseUrl("https://api.github.com/")
                    .addConverterFactory(GsonConverterFactory.create())
                    .client(getOkHttpClient())
                    .build();

        }
        return mRetrofit;
    }

    public static TestService getTestService(){
        if(mTestService == null) {
            mTestService = getRetrofit().create(TestService.class);
        }
        return mTestService;
    }
}

接下來(lái)看下如何在代碼里請(qǐng)求接口:

1.異步執(zhí)行:

 Call<String> call = RetrofitManager.getTestService().getUserInfo();

 call.enqueue(new Callback<String>() {
     @Override
     public void onResponse(Call<String> call, Response<String> response) {

         //獲得結(jié)果
         String result = response.body();

     }

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

     }
 });

2.同步執(zhí)行

 Call<String> call = RetrofitManager.getTestService().getUserInfo();

 try {
     Response<String> response = call.execute();
     //獲得結(jié)果
     String result = response.body();
 }catch (IOException ioException){
     ioException.printStackTrace();
 }

上面例子中的Call<String>里申明的call是用于操控網(wǎng)絡(luò)請(qǐng)求的李皇,而申明的泛型String類(lèi)型這是我們想要獲得請(qǐng)求結(jié)果時(shí)轉(zhuǎn)換成的類(lèi)型伺糠,下文會(huì)解釋是怎么轉(zhuǎn)換的枕荞;

在Android里不能在主線程執(zhí)行請(qǐng)求網(wǎng)絡(luò)的操作造烁,所以使用同步請(qǐng)求是需要另起線程,所以一般都是異步請(qǐng)求汽摹;

如果你像以上所述操作后李丰,那么你已經(jīng)成功使用Retrofit請(qǐng)求網(wǎng)絡(luò)。

接下來(lái)看下如何定義一條接口請(qǐng)求:

//定義一條接口請(qǐng)求
//等價(jià)于https://api.github.com/users/yuhengye
@GET("users/yuhengye")
Call<String> getUserInfo();

可以看到方法頭部使用了注解逼泣,官方文檔是這么解釋的:

Every method must have an HTTP annotation that provides the request method and relative URL. There are five built-in annotations: GET, POST, PUT, DELETE, and HEAD. The relative URL of the resource is specified in the annotation.

<br />

每個(gè)方法都必須有一個(gè)HTTP注釋提供的請(qǐng)求方法和相對(duì)URL趴泌。有五個(gè)內(nèi)置注釋?zhuān)篏ET,POST拉庶,PUT嗜憔,DELETE和HEAD。資源的相對(duì)URL在注釋中指定氏仗。

<br />
也就是說(shuō)如果你想用GET請(qǐng)求就可以在方法頭部加@GET注釋?zhuān)褂肞OST請(qǐng)求就使用@POST即可吉捶,在注釋里申明的相對(duì)URL會(huì)和在生成Retrofit實(shí)例時(shí)使用的baseUrl()方法里傳的值結(jié)合;

Retrofit提供不同HTTP請(qǐng)求方法的注解:
GET,POST,PUT,DELETE,HEAD,OPTIONS,PATCH,HTTP;

Retrofit還提供了以下注解:
Path,Header,HeaderMap,Headers,Query,QueryMap,FormUrlEncoded,Field,FieldMap,Multipart,Part,PartMap,Body,Streaming,Url;

值得一提的是,Retrofit里提供的注解命名規(guī)范是這樣的皆尔,如果是HTTP請(qǐng)求的方法呐舔,則一律是大寫(xiě),其他注解才使用駝峰式慷蠕;

接下來(lái)解釋下這些注解如何使用珊拼,為了縮短篇幅,以下在調(diào)用接口時(shí)流炕,就不寫(xiě)獲取接口實(shí)例的方法了澎现,比如調(diào)用getUserInfo()相當(dāng)于RetrofitManager.getTestService().getUserInfo(),并且申明的接口方法都是在TestService接口下浪感;


Path
@GET("users/{username}")
Call<String> getUserInfo(@Path("username") String name);

//等價(jià)于https://api.github.com/users/yuhengye
getUserInfo("yuhengye");

可以看出Path是用于替換相對(duì)路徑url里的值昔头;


Header,HeaderMap,Headers

//在方法里聲明多個(gè)參數(shù)時(shí),可以多種注解組合使用
@GET("users/{username}")
Call<String> getUserInfo(@Header("Accept-Language") String lang, @Path("username") String name);

//Request Header 
//Accept-Language:en-US
getUserInfo("en-US", "yuhengye");

---------------divider---------------    

@GET("users/yuhengye")
Call<String> getUserInfo(@Header("Accept-Language") List<String> langs);

List<String> langs = new ArrayList<>();
langs.add("en-US");
langs.add("zh-CN");

//Request Header 
//Accept-Language:en-US,zh-CN
getUserInfo(langs);

---------------divider---------------    

@GET("users/yuhengye")
Call<String> getUserInfo(@HeaderMap<String,String> headers);
     
Map<String,String> headers = new HashMap<>();
header.put("Accept", "text/plain");
header.put("Accept-Charset", "utf-8");
     
//Request Header 
//Accept:text/plain
//Accept-Charset:utf-8
getUserInfo(headers);

---------------divider---------------

@Headers("Accept-Language: en-US")
@GET("users/yuhengye")
Call<String> getUserInfo();

@Headers({
"Accept-Language: en-US,zh-CN",
"Cache-Control: max-age=640000"
})
@GET("users/yuhengye")
Call<String> getUserInfo2();

Header是用于給HTTP的請(qǐng)求頭增加信息的影兽,HeaderMap則是當(dāng)有多個(gè)Header和數(shù)量不固定的Header準(zhǔn)備的揭斧;下面介紹QueryQueryMapFieldFieldMap時(shí)也是一樣的道理峻堰;而Headers是當(dāng)你的接口的Header是固定的時(shí)候才用到讹开,并且支持多個(gè)(數(shù)組形式);


Query,QueryMap

@GET("search/repositories")
Call<String> searchRepo(@Query("q") String keywords, @Query("sort") String sort); 

//等價(jià)于https://api.github.com/search/repositories?q=retrofit&sort=stars
searchRepo("retrofit", "stars");

---------------divider---------------

@GET("search/repositories?order=desc")
Call<String> searchRepoOrderByDesc(@QueryMap Map<String,String> params); 

Map<String,String> params = new HashMap<>();
header.put("q", "retrofit");

//等價(jià)于https://api.github.com/search/repositories?order=desc&q=retrofit
searchRepo(params);

QueryQueryMap主要是在GET請(qǐng)求時(shí)捐名,給你的URL傳參旦万,當(dāng)然URL的參數(shù)也可以固定在相對(duì)路徑里面;


FormUrlEncoded,Field,FieldMap

@FormUrlEncoded
@POST(“https://api.weibo.com/oauth2/access_token”)
Call<String> oauthToken(@Field("access_token") String code, @Field("client_secret") String client_secret);

//POST表單數(shù)據(jù)
oauthToken("codeValue", "secretValue");

這里沒(méi)有使用到FieldMap镶蹋,相信你也知道它的用處是什么了成艘,當(dāng)你需要使用到表單的方式請(qǐng)求時(shí)赏半,只需使用@FormUrlEncoded注解就可以了,實(shí)際上跟你替換成@Headers("Content-Type: application/x-www-form-urlencoded")同理淆两,只是框架已經(jīng)幫我們實(shí)現(xiàn)了断箫,FieldFieldMapQueryQueryMap類(lèi)似,只不過(guò)前者是在表單提交時(shí)使用的注解秋冰,后者是URL傳參時(shí)使用的注解仲义。

在上面例子中,@Post中的URL不再是相對(duì)路徑剑勾,而是絕對(duì)路徑埃撵,當(dāng)你使用絕對(duì)路徑時(shí),Retrofit就會(huì)忽略掉你實(shí)例化它時(shí)通過(guò)baseUrl()方法傳給它的host(接口前綴地址)虽另;因?yàn)閷?shí)際開(kāi)發(fā)中暂刘,有可能只有幾個(gè)接口的host是跟你設(shè)置的host不一樣的,為了避免實(shí)例化多個(gè)Retrofit捂刺,你只需傳入絕對(duì)路徑即可鸳惯;


Body

@POST("users/new")
Call<String> createUser(@Body RequestBody user);

---------------divider---------------

@POST("users/new")
Call<String> createUser(@Body User user);

當(dāng)發(fā)起POST請(qǐng)求時(shí),使用@Body把參數(shù)直接放到你的request的body里面叠萍,如果在實(shí)例化Retrofit時(shí),沒(méi)有指定converter(下文會(huì)解釋這是什么), 那@Body注解后面的參數(shù)類(lèi)型必須是RequestBody類(lèi)型绪商;


Multipart,Part,PartMap

@Multipart
@POST(“https://api.weibo.com/2/statuses/upload_pic.json”)
Call<String> uploadPicture(
@Part("access_token") RequestBody token,
@Part MultipartBody.Part file);

RequestBody requestFile = RequestBody.create(MediaType.parse("application/otcet-stream"), new File(s));

MultipartBody.Part body = MultipartBody.Part.createFormData("pic", "share.png", requestFile);

uploadPicture(RequestBody.create(MediaType.parse("multipart/form-data"), "tokenValue"), body);

以下是POST請(qǐng)求時(shí)苛谷,multipart的body內(nèi)容

//部分頭信息
Content-Type: multipart/form-data; boundary=d1547544-7ffb-4ebd-913e-8cb21d2ea8f9
Content-Length: 32461

//body內(nèi)容開(kāi)始
--d1547544-7ffb-4ebd-913e-8cb21d2ea8f9
Content-Disposition: form-data; name="access_token"
Content-Transfer-Encoding: binary
Content-Type: multipart/form-data; charset=utf-8
Content-Length: 32
2.00tJ6X3GiGSdcC5f7433b5a40iAPJA
--d1547544-7ffb-4ebd-913e-8cb21d2ea8f9
Content-Disposition: form-data; name="pic"; filename="share.png"
Content-Type: application/otcet-stream
Content-Length: 31813
file bytes
--d1547544-7ffb-4ebd-913e-8cb21d2ea8f9--
//body內(nèi)容結(jié)束

通常我們上傳文件時(shí),大多數(shù)使用multipart格郁,下面簡(jiǎn)單描述下multipart的傳輸過(guò)程:
每一個(gè)部分都是以--加boundary(分隔符)開(kāi)始腹殿,然后是該部分內(nèi)容的描述信息,然后一個(gè)回車(chē)例书,然后是描述信息的具體內(nèi)容锣尉;如果傳送的內(nèi)容是一個(gè)文件的話,那么還會(huì)包含文件名信息决采,以及文件內(nèi)容的類(lèi)型自沧。上面請(qǐng)求的第二個(gè)部分就是是一個(gè)文件體的結(jié)構(gòu),最后會(huì)以--boundary符--結(jié)尾树瞭,表示請(qǐng)求體結(jié)束拇厢。

當(dāng)使用@Multipart時(shí),相當(dāng)于@Headers("Content-Type: multipart/form-data; boundary=${bound}")晒喷,并且框架幫你把boundary(分隔符)的值設(shè)定好了;

@Part@PartMap是配合@Multipart使用的孝偎,使用@PartMap參數(shù)類(lèi)型必須是Map,key就相當(dāng)于partName, value所接受的參數(shù)類(lèi)型跟@Part一樣凉敲,但建議不要是MultipartBody.Part(因?yàn)镸ultipartBody.Part本身已經(jīng)包含partName)衣盾,以下是@Part接受不同參數(shù)時(shí)的情況:
1.當(dāng)方法里傳參類(lèi)型是MultipartBody.Part時(shí)寺旺,不要這樣(@Part("partName") MultipartBody.Part part申明,partName會(huì)被忽略势决,因?yàn)樵跇?gòu)建MultipartBody.Part時(shí)阻塑,已經(jīng)包含了part的name在里面;

2.當(dāng)方法里傳參類(lèi)型是RequestBody時(shí)徽龟,會(huì)把RequestBody的ContentType的值加到body里面去叮姑,比如像上面例子這樣申明Call<String> uploadPicture(@Part("access_token") RequestBody token);那么調(diào)用該方法uploadPicture(RequestBody.create(MediaType.parse("multipart/form-data"), "tokenValue"))時(shí)插入body里面的內(nèi)容是這樣的:

--d1547544-7ffb-4ebd-913e-8cb21d2ea8f9
Content-Disposition: form-data; name="access_token"
Content-Transfer-Encoding: binary
Content-Type: multipart/form-data; charset=utf-8
Content-Length: 32
2.00tJ6X3GiGSdcC5f7433b5a40iAPJA
--d1547544-7ffb-4ebd-913e-8cb21d2ea8f9--

3.當(dāng)方法里傳參類(lèi)型是其他對(duì)象類(lèi)型時(shí),將會(huì)通過(guò)實(shí)例化Retrofit時(shí)指定的conveter(下文會(huì)解釋這是什么)來(lái)轉(zhuǎn)換成RequestBody据悔,然后跟上面第二點(diǎn)所述處理;


Streaming

@Streaming
@GET("video/test.mp4")
Response getVideo();

Response response = getVideo(); 
InputStream videoDataStream = response.getBody().in();

使用@Streaming時(shí)返回值必須是Response传透,包含了HTTP請(qǐng)求最初的body內(nèi)容,未經(jīng)任何轉(zhuǎn)換极颓;一般是獲取比較大的數(shù)據(jù)流時(shí)使用朱盐,或者下載文件時(shí)使用;


Url

//等價(jià)于https://api.github.com/users/yuhengye
@GET("users/yuhengye")
Call<String> getUserInfo();

---------------divider---------------    

@GET
Call<String> getUserInfo(@Url String url);

//等價(jià)于https://api.github.com/users/yuhengye
getUserInfo("users/yuhengye);

從上面可以看出,如果方法里申明帶@Url的參數(shù)菠隆,則在方法頭部申明HTTP請(qǐng)求方式時(shí)兵琳,可以不再申明相對(duì)路徑地址;

實(shí)例化Retrofit時(shí)調(diào)用baseUrl(api)方法傳進(jìn)去的api地址應(yīng)該以/結(jié)尾骇径,如果傳的是https://api.github.com/search躯肌,Retrofit會(huì)以/最后出現(xiàn)的位置作為結(jié)尾當(dāng)成https://api.github.com/處理,值得一提的是破衔,在申明請(qǐng)求接口頭部定義url的時(shí)候清女,建議統(tǒng)一用相對(duì)路徑(前面不包括/),類(lèi)似@GET("users/yuhengye")而不是@GET("/users/yuhengye")晰筛,因?yàn)檫@兩者處理上也有所不同:

BaseUrl:https://api.github.com/search/
Endpoint:/users/yuhengye
Result:https://api.github.com/users/yuhengye

BaseUrl:https://api.github.com/search/
Endpoint:users/yuhengye
Result:https://api.github.com/search/users/yuhengye


CallAdapter

上文在申明接口的方法時(shí)嫡丙,返回值統(tǒng)一都是Call<String>類(lèi)型,因?yàn)槟J(rèn)情況下返回值是只支持Call類(lèi)型的读第,這個(gè)類(lèi)包含了最基本的網(wǎng)絡(luò)請(qǐng)求操作曙博,相信你看過(guò)很多文章是XXX App,使用了以下框架:Retrofit + RxJava + ...怜瞒,如果你沒(méi)用過(guò)RxJava父泳,可以跳過(guò)此段介紹,下面簡(jiǎn)單介紹RxJava結(jié)合Retrofit請(qǐng)求網(wǎng)絡(luò)盼砍。

首先加入以下依賴(lài):

compile "com.squareup.retrofit2:adapter-rxjava:$RETROFIT_VERSION"

//以下是下面例子中使用RxJava的版本   
compile 'io.reactivex:rxandroid:1.1.0'
compile 'io.reactivex:rxjava:1.1.0'

在實(shí)例化Retrofit時(shí)尘吗,改成這樣:

public static Retrofit getRetrofit(){
  if(mRetrofit == null) {
      mRetrofit = new Retrofit.Builder()
              .baseUrl("https://api.github.com/")
              .addConverterFactory(GsonConverterFactory.create())
              .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
              .client(getOkHttpClient())
              .build();
    
  }
  return mRetrofit;
}

addCallAdapterFactory(RxJavaCallAdapterFactory.create())這個(gè)是我們?cè)黾拥恼{(diào)用,以后在申明接口的方法返回值時(shí)浇坐,就可以這樣:

@GET("users/yuhengye")
Observable<String> getUserInfo();

RetrofitManager
            .getTestService()
            .getUserInfo()//返回一個(gè)Observable<String>對(duì)象
            .subscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe(new Subscriber<String>() {
                @Override
                public void onCompleted() {
                }
                @Override
                public void onError(Throwable e) {
                }
                @Override
                public void onNext(String result) {
                }
            });

Converter(轉(zhuǎn)換器)

上文中多次提到過(guò)converter的概念睬捶,現(xiàn)在簡(jiǎn)單介紹下:

Retrofit框架默認(rèn)是使用OKHTTP去請(qǐng)求網(wǎng)絡(luò)的,而在OKHTTP中網(wǎng)絡(luò)請(qǐng)求默認(rèn)都是返回一個(gè)ResponseBody類(lèi)型的值近刘,為了免去網(wǎng)絡(luò)請(qǐng)求中重復(fù)的轉(zhuǎn)換操作擒贸,在實(shí)例化Retrofit時(shí)可以通過(guò)addConverterFactory(GsonConverterFactory.create())方法增加一個(gè)轉(zhuǎn)換工廠臀晃,Retrofit內(nèi)置的轉(zhuǎn)換工廠是只支持ResponseBodyVoid類(lèi)型介劫,值得一提的是徽惋,在接口申明的返回值里如果不關(guān)心返回結(jié)果,必須這樣申明Call<Void>座韵,不能沒(méi)有泛型Call這樣申明险绘;

文章開(kāi)始介紹要引入的依賴(lài)時(shí),有包括這個(gè)compile "com.squareup.retrofit2:converter-gson:$RETROFIT_VERSION"誉碴, 這個(gè)庫(kù)的的名字是converter-gson宦棺,那么肯定是跟gson相關(guān)的了,回想一下黔帕,剛才我們申明接口方法時(shí)的返回值代咸,統(tǒng)一都是Call<String>,之所以可以申明String類(lèi)型成黄,是因?yàn)槲覀兺ㄟ^(guò)這個(gè)依賴(lài)庫(kù)提供的Gson轉(zhuǎn)換工廠把結(jié)果轉(zhuǎn)換成了String類(lèi)型呐芥,然后請(qǐng)求返回結(jié)果后得到一個(gè)Response<String>類(lèi)型的實(shí)例responseResponse申明的泛型類(lèi)型是跟Call申明的泛型類(lèi)型一致的奋岁,而String result = response.body()就是獲得申明泛型類(lèi)型的值思瘟,通常來(lái)說(shuō)我們是知道請(qǐng)求接口會(huì)返回的數(shù)據(jù)格式,如果是json格式闻伶,每次都是通過(guò)獲得String類(lèi)型潮太,再轉(zhuǎn)換成對(duì)應(yīng)的實(shí)體類(lèi),那么會(huì)做很多重復(fù)的工作虾攻;所以上文在接口申明方法時(shí)的泛型類(lèi)型都可以替換成對(duì)應(yīng)的實(shí)體類(lèi),比如這樣:

//GitHubUser就是通過(guò)gson轉(zhuǎn)換成對(duì)應(yīng)的實(shí)體類(lèi);
@GET("users/yuhengye")
Call<GitHubUser> getUserInfo();

當(dāng)然Converter還不止是在請(qǐng)求結(jié)果返回時(shí)轉(zhuǎn)換結(jié)果更鲁,還包括發(fā)起請(qǐng)求時(shí)的一些數(shù)據(jù)轉(zhuǎn)換等霎箍;以上所提到的@HeaderMap@QueryMap澡为、FieldMap后面申明的參數(shù)類(lèi)型必須是Map漂坏,但是key和value沒(méi)有限定必須是String類(lèi)型,這3個(gè)注解和@Path媒至、@Header顶别、@Query@Field默認(rèn)最終都會(huì)調(diào)用內(nèi)置的converter調(diào)用String.valueOf(object)轉(zhuǎn)成String類(lèi)型拒啰,也就是調(diào)用Object的toString()方法驯绎;

我們也可以自定義自己的Converter工廠,本文就不再敘述了谋旦,后面會(huì)再寫(xiě)一遍關(guān)于Retrofit的源碼和原理探索還有進(jìn)階使用剩失;

相關(guān)文章:
Retrofit源碼解析

參考文檔:

Retrofit官方文檔
http://square.github.io/retrofit/

HTTP協(xié)議之multipart/form-data請(qǐng)求分析
http://blog.csdn.net/five3/article/details/7181521/

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末屈尼,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子拴孤,更是在濱河造成了極大的恐慌脾歧,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,402評(píng)論 6 499
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件演熟,死亡現(xiàn)場(chǎng)離奇詭異鞭执,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)芒粹,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,377評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門(mén)兄纺,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人是辕,你說(shuō)我怎么就攤上這事囤热。” “怎么了获三?”我有些...
    開(kāi)封第一講書(shū)人閱讀 162,483評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵旁蔼,是天一觀的道長(zhǎng)唤衫。 經(jīng)常有香客問(wèn)我效床,道長(zhǎng),這世上最難降的妖魔是什么淹接? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,165評(píng)論 1 292
  • 正文 為了忘掉前任贞谓,我火速辦了婚禮限佩,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘裸弦。我一直安慰自己祟同,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,176評(píng)論 6 388
  • 文/花漫 我一把揭開(kāi)白布理疙。 她就那樣靜靜地躺著晕城,像睡著了一般。 火紅的嫁衣襯著肌膚如雪窖贤。 梳的紋絲不亂的頭發(fā)上砖顷,一...
    開(kāi)封第一講書(shū)人閱讀 51,146評(píng)論 1 297
  • 那天,我揣著相機(jī)與錄音赃梧,去河邊找鬼滤蝠。 笑死,一個(gè)胖子當(dāng)著我的面吹牛授嘀,可吹牛的內(nèi)容都是我干的物咳。 我是一名探鬼主播,決...
    沈念sama閱讀 40,032評(píng)論 3 417
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼蹄皱,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼所森!你這毒婦竟也來(lái)了囱持?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 38,896評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤焕济,失蹤者是張志新(化名)和其女友劉穎纷妆,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體晴弃,經(jīng)...
    沈念sama閱讀 45,311評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡掩幢,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,536評(píng)論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了上鞠。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片际邻。...
    茶點(diǎn)故事閱讀 39,696評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖芍阎,靈堂內(nèi)的尸體忽然破棺而出世曾,到底是詐尸還是另有隱情,我是刑警寧澤谴咸,帶...
    沈念sama閱讀 35,413評(píng)論 5 343
  • 正文 年R本政府宣布轮听,位于F島的核電站,受9級(jí)特大地震影響岭佳,放射性物質(zhì)發(fā)生泄漏血巍。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,008評(píng)論 3 325
  • 文/蒙蒙 一珊随、第九天 我趴在偏房一處隱蔽的房頂上張望述寡。 院中可真熱鬧,春花似錦叶洞、人聲如沸鲫凶。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,659評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)掀序。三九已至,卻和暖如春惭婿,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背叶雹。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,815評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工财饥, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人折晦。 一個(gè)月前我還...
    沈念sama閱讀 47,698評(píng)論 2 368
  • 正文 我出身青樓钥星,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親满着。 傳聞我的和親對(duì)象是個(gè)殘疾皇子谦炒,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,592評(píng)論 2 353

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

  • Retrofit簡(jiǎn)介 Retrofit是大名鼎鼎的 Square 公司開(kāi)源的適用于Android與Java的網(wǎng)絡(luò)請(qǐng)...
    winter1991閱讀 19,697評(píng)論 18 71
  • 知識(shí)框架(腦圖) 出現(xiàn)背景 移動(dòng)端網(wǎng)絡(luò)請(qǐng)求復(fù)雜贯莺,代碼冗余 解決思路 Retrofit:給Android和Java用...
    Lshare_Blog閱讀 1,826評(píng)論 10 36
  • 整體Retrofit內(nèi)容如下: 1、Retrofit解析1之前哨站——理解RESTful2宁改、Retrofit解析2...
    隔壁老李頭閱讀 15,075評(píng)論 4 39
  • 簡(jiǎn)介 剛接觸Retrofit的時(shí)候缕探,就寫(xiě)了一篇簡(jiǎn)單的使用介紹:Retrofit 2.0基本使用方法,算是對(duì)Retr...
    Whyn閱讀 2,844評(píng)論 4 24
  • Retrofit--相信大家都或多或少的聽(tīng)過(guò)和用過(guò)了,不知道是什么的證明你已經(jīng)out了~我使用和研究Retrofi...
    zyyoona7閱讀 3,583評(píng)論 9 36