Clean Architecture 學(xué)習(xí)之 Retrofit

前言

Retrofit 是 square 公司開源的一個非常著名的簡化網(wǎng)絡(luò)請求的框架,但是它不是網(wǎng)絡(luò)框架,OkHttp 才是,Retrofit 相當(dāng)于是將OkHttp封裝了饼丘,方便我們使用。square公司還有很多其他非常著名的開源庫硬毕,比如OkHttp,Otto 以及Dagger(Dagger2 是由google 維護(hù)的一個分支京闰,專為移動設(shè)備開發(fā)的)。Bob大叔在Clean Architecture中使用了它掖肋,我們就來學(xué)習(xí)Retrofit的基本使用仆葡,學(xué)習(xí)完本文以后,你將能應(yīng)付80%的日常開發(fā)工作。
最新版本的Retrofit 已經(jīng)升級到2.1了沿盅,在2.0之前的版本把篓,OkHttp是可選的,但是2.0之后是必須的了腰涧。2.0之前韧掩,Converter 是內(nèi)置的,來自服務(wù)器的json字符串會自動轉(zhuǎn)換為定義好的DAO(Data Access Object)窖铡,2.0之后你必須自己手動引入疗锐。

添加依賴庫

想要在項(xiàng)目中使用Retrofit非常簡單,在build.gradle的依賴中加入下面這行代碼:

compile 'com.squareup.retrofit2:retrofit:2.1.0'

添加converter依賴庫

正如前面所說费彼,如果Retrofit 2.X 不再內(nèi)置converter,如果你想要接收json 并解析成DAO,你必須把Gson Converter作為一個獨(dú)立的依賴添加進(jìn)來:

compile 'com.squareup.retrofit:converter-gson:2.1.1'

Square還提供了很多其他常見的converter滑臊,比如Jackson、Protobuf 等到箍铲,參看詳情請移步這個鏈接

添加自定義converter

如果你想使用自己定義的Converter雇卷,你必須繼承 Converter.Factory 這個abstract類,覆蓋其中的requestBodyConverterresponseBodyConverter方法。以FastJson 為例:

FastJsonConverterFactory.java

public class FastJsonConverterFactory extends Converter.Factory{
    private Charset charset;
    private static final Charset UTF_8  = Charset.forName("UTF-8");

    public static FastJsonConverterFactory create() {
        return create(UTF_8);
    }

    public static FastJsonConverterFactory create(Charset charset) {
        return new FastJsonConverterFactory(charset);
    }

    public FastJsonConverterFactory(Charset charset) {
        this.charset = charset;
    }

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

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

FastJsonRequestBodyConverter.java

public class FastJsonRequestBodyConverter<T> implements Converter<T, RequestBody> {
    private Type type;
    private Charset charset;
    private static final MediaType MEDIA_TYPE = MediaType.parse("application/json; charset=UTF-8");

    public FastJsonRequestBodyConverter(Type type, Charset charset) {
        this.type = type;
        this.charset = charset;
    }

    @Override
    public RequestBody convert(T value) throws IOException {
        return RequestBody.create(MEDIA_TYPE, JSON.toJSONString(value).getBytes(charset));
    }
}

FastJsonResponseBodyConverter.java

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

    private Type type;
    private Charset charset;

    public FastJsonResponseBodyConverter() {
    }

    public FastJsonResponseBodyConverter(Type type, Charset charset) {
        this.type = type;
        this.charset = charset;
    }

    @Override
    public T convert(ResponseBody value) throws IOException {
        try {
            return JSON.parseObject(value.string(), type);
        } finally {
            value.close();
        }
    }
}
添加CallAdapter依賴庫

Retrofit 2.x允許你自定義CallAdapter來滿足您的特殊需求颠猴。 官方推出了支持RxJava的CallAdapter关划,要想在Retrofit的編程中使用響應(yīng)式編程,你必須加入以下依賴庫:

"com.squareup.retrofit2:adapter-rxjava:2.1.0"

添加完以上這些依賴庫之后翘瓮,我們就可以在項(xiàng)目中使用了,先看看怎么創(chuàng)建Retrofit 實(shí)例:

Retrofit retrofit = new Retrofit.Builder()
                .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
                .addConverterFactory(FastJsonConverterFactory.create())
                .baseUrl(BASE_URL)
                .build();

使用Retrofit

API 聲明

按照Square官方的說明祭玉,我們需要按照一定的格式創(chuàng)建一個接口,定義我們將要使用的Client春畔,比如下面這個REST github client:

public interface GitHubClient {  
    @GET("/repos/{owner}/{repo}/contributors")
    List<Contributor> contributors(
        @Path("owner") String owner,
        @Path("repo") String repo
    );
}

所有的client必須用interface的方式來定義脱货。方法必須用@GET @POST等Http 方法注解。需要動態(tài)設(shè)定的URL律姨,必須在方法的注解中用大括號中包起來振峻,并在方法的參數(shù)中設(shè)定。下面詳細(xì)介紹其使用方法

請求方法

每個方法必須包含一個HTTP 注解择份,注解中提供了請求方法和相關(guān)的URL扣孟,Retrofit 內(nèi)置了五個方法:GETPOST荣赶,PUT凤价,DELETEHEAD拔创。對應(yīng)的URL在注解中指定利诺,如上面的例子所示。你還可以在URL中指定query參數(shù):

@GET("users/list?sort=desc")

參數(shù)化控制URL

我們可以通過占位符和方法中的參數(shù)來動態(tài)地更新請求的URL剩燥。占位符是用大括號包起來的慢逾,響應(yīng)的參數(shù)必須用@path注解,并且使用跟占位符相同的字符串。如上面的例子所示侣滩。請求參數(shù)也可以同時添加在方法的參數(shù)中口注。

@GET("group/{id}/users")
Call<List<User>> groupList(@Path("id") int groupId, @Query("sort") String sort);

對于比較復(fù)雜的query參數(shù),我們還可以使用map:

@GET("group/{id}/users")
Call<List<User>> groupList(@Path("id") int groupId, @QueryMap Map<String, String> options);
請求體

我們可以使用@Body注解來指定一個對象作為http 請求體君珠。

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

Retrofit 會使用我們在創(chuàng)建Retrofit實(shí)例時指定的converter,將這個對象轉(zhuǎn)換寝志。如果沒有指定,則只能使用RequestBody策添。

Form encode 和 MultiPart

我們也可以聲明方法為使用form-encode 和 multipart data澈段。
如果我們在方法上使用了@FormUrlEncoded注解,retrofit就會發(fā)送form-encode data舰攒。每個鍵值對都必須用@Field注解败富,包含name,值則通過對象提供摩窃。

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

在方法上加上 @Multipart 注解來使用Multipart請求兽叮。Parts 則使用@Part注解來聲明。

@Multipart
@PUT("user/photo")
Call<User> updateUser(@Part("photo") RequestBody photo, @Part("description") RequestBody description);

Multipart parts使用Retrofit的converter之一猾愿,或者可以通過實(shí)現(xiàn) RequestBody 來處理序列化鹦聪。

Header 控制

你可以通過 @Header 注解來設(shè)置靜態(tài)的headers

@Headers("Cache-Control: max-age=640000")
@GET("widget/list")
Call<List<Widget>> widgetList();

你也可以 @Header 注解來動態(tài)更新。相應(yīng)的參數(shù)必須提供給 @Header 注解蒂秘。如果只為空泽本,這個header就會被忽略。

@GET("user")
Call<User> getUser(@Header("Authorization") String authorization)
URL定義方式

Retrofit 2.0使用了新的URL定義方式姻僧。Base URL與@Url 不是簡單的組合在一起规丽。

BASE_URL @URL RESULT
http://www.github.com/ list http://www.github.com/list
http://www.github.com/base/level/ list/ http://www.github.com/base/level/list/
http://www.github.com/base/level /list http://www.github.com/list

從上面的表格可以看出,如果@URL以/開頭的撇贺,會覆蓋BASE_URL中的部分赌莺。所以,以下規(guī)則我們最好遵從

  • BASE_URL: 總是以 /結(jié)尾
  • Url: 不要以 / 開頭

同步和異步請求

在Retrofit 2.x 中松嘶,所有的請求都被封裝成一個Call對象艘狭。在retrofit 2.X中,同步和異步請求的定義不再有區(qū)別翠订。他們只在執(zhí)行的時候不一樣巢音。
同步方法在主線程中執(zhí)行,而安卓的主線程是UI線程尽超,是不允許在主線程中執(zhí)行網(wǎng)絡(luò)請求的官撼。所以,在安卓應(yīng)用開發(fā)中橙弱,我們不使用同步請求歧寺。不過我們還是來簡單了解下同步請求的執(zhí)行方法燥狰。
client 定義如下:

public interface TaskService {  
    @GET("/tasks")
    Call<List<Task>> getTasks();
}

同步請求:

TaskService taskService = ServiceGenerator.createService(TaskService.class);  
Call<List<Task>> call = taskService.getTasks();  
List<Task>> tasks = call.execute().body();  

通過執(zhí)行Call對象的excute()方法就會調(diào)用同步請求棘脐,反序列化的response body 可通過 body()方法得到斜筐。

異步請求:

TaskService taskService = ServiceGenerator.createService(TaskService.class);  
Call<List<Task>> call = taskService.getTasks();  
call.enqueue(new Callback<List<Task>>() {  
    @Override
    public void onResponse(Call<List<Task>> call, Response<List<Task>> response) {
        if (response.isSuccessful()) {
            // tasks available
        } else {
            // error response, no access to resource?
        }
    }

    @Override
    public void onFailure(Call<List<Task>> call, Throwable t) {
        // something went completely south (like no internet connection)
        Log.d("Error", t.getMessage());
    }
}

實(shí)現(xiàn)CallBack類,并執(zhí)行Call對象的enqueue()方法蛀缝,我們就能執(zhí)行異步請求顷链。

我們前面也提到過,Retrofit 支持響應(yīng)式編程屈梁。因?yàn)槲覀兛梢允褂肦xJava來寫出優(yōu)雅而又邏輯簡單的代碼:
client定義:

public interface GoodsService {
    @POST("categories.do")
    Observable<GoodsCategoriesResp> getGoodsCategories(@Body GoodsCategoriesReq req);
}

請求的返回值嗤练,不再是Call對象,而變成了Observable對象了在讶。因?yàn)槲覀冊趧?chuàng)建Retrofit實(shí)例的時候煞抬,已經(jīng)添加了CallAdapter了。

GoodsCategoriesReq req = new GoodsCategoriesReq();
req.setId(id);
Retrofit retrofit = new Retrofit.Builder()
                .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
                .addConverterFactory(FastJsonConverterFactory.create())
                .baseUrl(BASE_URL)
                .build();
retrofit
    .create(GoodsService.class)
    .getGoodsCategories(req)
    .flatMap(categoriesResp->{
        if (categoriesResp.getCode() != 200){
            return Observable.error(new BusinessException(categoriesResp.getCode(),categoriesResp.getMsg()));
        }
        return Observable.just(categoriesResp);
    })
    .map(resp->{
        List<GoodsCategory> categories = new ArrayList<>();

        for(GoodsCategoriesResp.Category item: resp.getCategories()) {
            GoodsCategory category = new GoodsCategory();
            category.setId(item.getId());
            category.setParentId(id);
            category.setName(item.getName());
            category.setImg(item.getImg());
            category.setHasNext(item.getHasNext() == 1);

            categories.add(category);
        }
        return categories;
    })
    .subscribe(new BaseSubscriber<List<GoodsCategory>>() {
                    @Override
                    public void onCompleted() {

                    }

                    @Override
                    public void onError(Throwable e) {

                    }

                    @Override
                    public void onNext(List<GoodsCategory> categories) {
                    }
        });

在上面的代碼中构哺,當(dāng)Retrofit異步執(zhí)行完getGoodsCategories(req)之后革答,我們就得到了一個Observable<GoodsCategoriesResp>對象,但這個對象并不能直接提供給上層UI使用曙强,我們我們需要進(jìn)行一次轉(zhuǎn)換残拐,將其轉(zhuǎn)換成List<GoodsCategory>對象。如果有錯誤發(fā)生碟嘴,Subscriber的onError方法就會被調(diào)用溪食,我們在里面進(jìn)行出錯處理。我們用了一系列的鏈?zhǔn)讲僮髂壬龋屯瓿闪顺鲥e處理和數(shù)據(jù)轉(zhuǎn)換等工作错沃,邏輯簡單明了。

上傳和下載文件

上傳文件

在Retrofit 2.x中雀瓢,由于舍棄了1.x的TypedFile方式捎废,所以只能借助OkHttp庫來執(zhí)行與網(wǎng)絡(luò)有關(guān)的操作。在2.x中有兩種方式可以選擇致燥,OkHttp的RequestBody或者MultipartBody.Part,把你的文件封裝進(jìn)request body中登疗。先看看上傳文件的接口定義

public interface FileUploadService {  
    @Multipart
    @POST("upload")
    Call<ResponseBody> upload(@Part("description") RequestBody description,
                              @Part MultipartBody.Part file);
}

@description只是包含在RequestBody實(shí)例中的一個字符串。而第二個參數(shù)才是真正的文件嫌蚤。使用@MultipartBody.Part類的原因是它允許我們發(fā)送文件的真正的名字辐益,而不是request中的二進(jìn)制文件的數(shù)據(jù)。
下面這段代碼展示了在Android 客戶端中上傳文件脱吱,每個步驟都包含了注釋智政。這個方法需要一個文件的URI。

private void uploadFile(Uri fileUri) {  
    // create upload service client
    FileUploadService service =
            ServiceGenerator.createService(FileUploadService.class);

    // https://github.com/iPaulPro/aFileChooser/blob/master/aFileChooser/src/com/ipaulpro/afilechooser/utils/FileUtils.java
    // use the FileUtils to get the actual file by uri
    File file = FileUtils.getFile(this, fileUri);

    // create RequestBody instance from file
    RequestBody requestFile =
            RequestBody.create(MediaType.parse("multipart/form-data"), file);

    // MultipartBody.Part is used to send also the actual file name
    MultipartBody.Part body =
            MultipartBody.Part.createFormData("picture", file.getName(), requestFile);

    // add another part within the multipart request
    String descriptionString = "hello, this is description speaking";
    RequestBody description =
            RequestBody.create(
                    MediaType.parse("multipart/form-data"), descriptionString);

    // finally, execute the request
    Call<ResponseBody> call = service.upload(description, body);
    call.enqueue(new Callback<ResponseBody>() {
        @Override
        public void onResponse(Call<ResponseBody> call,
                               Response<ResponseBody> response) {
            Log.v("Upload", "success");
        }

        @Override
        public void onFailure(Call<ResponseBody> call, Throwable t) {
            Log.e("Upload error:", t.getMessage());
        }
    });
}

一個需要注意的地方是Content-Type箱蝠。如果你攔截了OkHttp Client 并且將其修改為 application/json,服務(wù)器在反序列化你的文件的時候就會有問題续捂。所以務(wù)必要確定Request Header 使用的是multipart/form-data,而不是application/json垦垂。

下載文件

下載文件與其他普通的Request并沒有區(qū)別,唯一需要注意的是返回值類型必須是ResponseBody類型牙瓢,否則Retrofit就會去嘗試解析和轉(zhuǎn)換劫拗,無疑這會引起異常。

FileDownloadService downloadService = ServiceGenerator.create(FileDownloadService.class);

Call<ResponseBody> call = downloadService.downloadFileWithDynamicUrlSync(fileUrl);

call.enqueue(new Callback<ResponseBody>() {  
    @Override
    public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
        if (response.isSuccess()) {
            Log.d(TAG, "server contacted and has file");

            boolean writtenToDisk = writeResponseBodyToDisk(response.body());

            Log.d(TAG, "file download was a success? " + writtenToDisk);
        } else {
            Log.d(TAG, "server contact failed");
        }
    }

    @Override
    public void onFailure(Call<ResponseBody> call, Throwable t) {
        Log.e(TAG, "error");
    }
});

此外矾克,由于Retrofit默認(rèn)在下載文件的時候是將其放在內(nèi)存中的页慷,所以在下載大文件的時候,我們需要:

  1. 在Client的方法上加上@Streaming注解胁附。
  2. 將網(wǎng)絡(luò)請求放在另一個后臺線程中酒繁,否則Android會觸發(fā)android.os.NetworkOnMainThreadException異常。

使用Interceptor

Interceptor即攔截器控妻,意味著我們可以在進(jìn)行網(wǎng)絡(luò)請求的時候?qū)@些請求進(jìn)行攔截州袒,對Request或者Response進(jìn)行預(yù)先處理,最常見的莫過于模擬網(wǎng)絡(luò)返回值了弓候。

public class MockInterceptor implements Interceptor {

    private String responeJsonPath;

    @Inject
    public MockInterceptor() {
    }

    @Override
    public Response intercept(Chain chain) throws IOException {
        String responseString = createResponseBody(chain);

        return new Response.Builder()
                .code(200)
                .message(responseString)
                .request(chain.request())
                .protocol(Protocol.HTTP_1_0)
                .body(ResponseBody.create(MediaType.parse("application/json"), responseString.getBytes()))
                .addHeader("content-type", "application/json")
                .build();
    }

    private String createResponseBody(Chain chain) {

        String responseString = null;

        HttpUrl uri = chain.request().url();
        String path = uri.url().getPath();

        if (path.equals("login.do")) {
            responseString = UserJson.getContent();
        } else if (path.contains("logout.do")) {

        } else if (path.contains("register.do")) {

        }else if (path.contains("categories.do")) {
            responseString = CategoryJson.getContent();
        }
        return responseString;
    }

}

這里定義的攔截器是模擬網(wǎng)絡(luò)請求的返回郎哭,所有的網(wǎng)絡(luò)請求都將返回200,response body 則是文件解析得到的內(nèi)容弓叛。
接下來彰居,我們需要定義OkHttp Client,將我們定義的Interceptor設(shè)置進(jìn)去:

public OkHttpClient provideOkHttpClient(MockInterceptor intercepter){

        return new OkHttpClient.Builder()
                .addInterceptor(intercepter)
                .connectTimeout(30, TimeUnit.SECONDS)
                .build();
}

最后就是把自定義的client設(shè)置到Retrofit中去了:

public Retrofit provideRetrofit(OkHttpClient client){
    return new Retrofit.Builder()
            .client(client)
            .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
            .addConverterFactory(FastJsonConverterFactory.create())
            .baseUrl(BASE_URL)
            .build();
}

總結(jié)

本文只是對Retrofit的簡單介紹撰筷,只涵蓋了最基本的使用陈惰。更多的內(nèi)容需要在以后的工作中遇到了再去學(xué)習(xí)了。更多教程請戳這個鏈接

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末毕籽,一起剝皮案震驚了整個濱河市抬闯,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌关筒,老刑警劉巖溶握,帶你破解...
    沈念sama閱讀 211,743評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異蒸播,居然都是意外死亡睡榆,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,296評論 3 385
  • 文/潘曉璐 我一進(jìn)店門袍榆,熙熙樓的掌柜王于貴愁眉苦臉地迎上來胀屿,“玉大人,你說我怎么就攤上這事包雀∷拚福” “怎么了?”我有些...
    開封第一講書人閱讀 157,285評論 0 348
  • 文/不壞的土叔 我叫張陵才写,是天一觀的道長葡兑。 經(jīng)常有香客問我奖蔓,道長,這世上最難降的妖魔是什么讹堤? 我笑而不...
    開封第一講書人閱讀 56,485評論 1 283
  • 正文 為了忘掉前任吆鹤,我火速辦了婚禮,結(jié)果婚禮上蜕劝,老公的妹妹穿的比我還像新娘檀头。我一直安慰自己轰异,他們只是感情好岖沛,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,581評論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著搭独,像睡著了一般婴削。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上牙肝,一...
    開封第一講書人閱讀 49,821評論 1 290
  • 那天唉俗,我揣著相機(jī)與錄音,去河邊找鬼配椭。 笑死虫溜,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的股缸。 我是一名探鬼主播衡楞,決...
    沈念sama閱讀 38,960評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼敦姻!你這毒婦竟也來了瘾境?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,719評論 0 266
  • 序言:老撾萬榮一對情侶失蹤镰惦,失蹤者是張志新(化名)和其女友劉穎迷守,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體旺入,經(jīng)...
    沈念sama閱讀 44,186評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡兑凿,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,516評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了茵瘾。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片礼华。...
    茶點(diǎn)故事閱讀 38,650評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖龄捡,靈堂內(nèi)的尸體忽然破棺而出卓嫂,到底是詐尸還是另有隱情,我是刑警寧澤聘殖,帶...
    沈念sama閱讀 34,329評論 4 330
  • 正文 年R本政府宣布晨雳,位于F島的核電站行瑞,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏餐禁。R本人自食惡果不足惜血久,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,936評論 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望帮非。 院中可真熱鬧氧吐,春花似錦、人聲如沸末盔。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,757評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽陨舱。三九已至翠拣,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間游盲,已是汗流浹背误墓。 一陣腳步聲響...
    開封第一講書人閱讀 31,991評論 1 266
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留益缎,地道東北人谜慌。 一個月前我還...
    沈念sama閱讀 46,370評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像莺奔,于是被迫代替她去往敵國和親欣范。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,527評論 2 349

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

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 171,777評論 25 707
  • Retrofit--相信大家都或多或少的聽過和用過了弊仪,不知道是什么的證明你已經(jīng)out了~我使用和研究Retrofi...
    zyyoona7閱讀 3,574評論 9 36
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理熙卡,服務(wù)發(fā)現(xiàn),斷路器励饵,智...
    卡卡羅2017閱讀 134,633評論 18 139
  • 簡介 剛接觸Retrofit的時候驳癌,就寫了一篇簡單的使用介紹:Retrofit 2.0基本使用方法,算是對Retr...
    Whyn閱讀 2,838評論 4 24
  • 整體Retrofit內(nèi)容如下: 1、Retrofit解析1之前哨站——理解RESTful2役听、Retrofit解析2...
    隔壁老李頭閱讀 3,979評論 8 19