天天看點

Android架構學習筆記03Retrofit架構

  前面我們學習過了Android網絡開發中的Okhttp架構和Asynchttpclient架構,這一篇我們學習一個非常強大的架構——Retrofit架構。Retrofit現在最新版本是2.1.0,Retrofit架構是Square公司推出來的,是在Okhttp基礎上的進一步封裝。

  在這裡對Okhttp做一些說明:Okhttp是基于HTTP協定封裝的,跟HttpClient、HttpUrlConnection的職責是一樣的,雖然Okhttp也可以開線程,但它是更多偏向于真正的網絡通信。Okhttp是基于NIO和OKio的,這裡可能很多人不明白什麼是IO、NIO和Okio,這裡做一個簡單的解釋:IO和NIO是Java的概念,IO是一種阻塞式的資料讀取,舉個例子:我向網絡中請求資料,程式就一種在等待,等到網絡中傳回資料并解析好了,程式才往下執行,這種就是阻塞式的;而NIO則是非阻塞式的,還是剛才那個例子,向網絡請求資料,但是程式繼續執行,等到資料傳回和處理完才通知我,然後通過回調處理,這種就是非阻塞式的,非阻塞式的效率比阻塞式的高。而Okio是Square公司在IO和NIO基礎上封裝的一個更高效、更簡單的資料處理庫。

下面我們介紹一下Retrofit這個架構

概述

  Retrofit架構是基于Okhttp封裝的,在1.0版本的時候是預設采用Okhttp通信,不過在沒有添加Okhttp依賴的時候也可以采用自帶的網絡請求通信,解析資料方式是Gson(一個Google推出的Json資料解析架構,後面我們會介紹),是以我們需要添加Gson的依賴。但是在2.0後的版本中,Retrofit是必須要采用Okhttp請求,還有請求傳回資料的解析也不是Gson,需要我們自己手動指定。

  Retrofit是通過注解将HTTP請求轉化為Java接口,那麼,我們了解一下Retrofit提供有哪些注解?

請求方式的注解

  • DELETE:建構一個DELETE請求
  • GET:建構一個GET請求
  • HTTP:可以代替其它的任意一種
  • OPTIONS:建構一個OPTIONS請求
  • PATCH:建構一個PATCH請求
  • POST:建構一個POST請求
  • PUT:建構一個PUT請求

上面列出的都是Retrofit2.0提供的網絡請求方式的注解,其中,我們可以通過HTTP這個注解自定義一個請求方式,例如:

interface Service {
   @HTTP(method = "CUSTOM", path = "custom/endpoint/")
   Call<ResponseBody> customEndpoint();
 }
           

甚至HTTP注解也可以用于發送一個DELETE請求方式的請求體,例如:

interface Service {
   @HTTP(method = "DELETE", path = "remove/", hasBody = true)
   Call<ResponseBody> deleteObject(@Body RequestBody object);
 }
           

标記注解

  • FormUrlEncoded注解:與@Field或者@FieldMap一起使用,上傳表單資料
  • Multipart注解:與@Part一起使用
  • Streaming注解:用于下載下傳大檔案

參數注解

  • Body注解::用于POST請求體,将執行個體對象根據轉換方式轉換為對應的json字元串參數,這個轉化方式是GsonConverterFactory定義的。
  • Field注解:@Field和@FormURLEncoded一起使用,表示送出一個表單資料給伺服器,我們都知道,表單資料是以鍵值對的形式存在,是以,@Field注解中的參數字元串為“鍵”,而被@Field标注的參數值為 “值”,例如:
    @FormUrlEncoded
     @POST("/list")
     Call<ResponseBody> example(@Field("name") String... names);
               
  • FieldMap注解:這個注解跟上面的@Field注解差不多,也是和@FieldURLEncoded一起使用。注解的是一個Map,鍵作為表單資料的”鍵”,值作為表單資料的”值”,例如
    @FormUrlEncoded
    @POST("/things")
    Call<ResponseBody> things(@FieldMap Map<String, String> fields);
               
  • Part注解:與@PartMap一樣
  • PartMap注解:用于POST檔案上傳,其中@Part MultipartBody.Part代表檔案,@Part(“key”)RequestBody代表參數,需要添加@Multipart表示支援檔案上傳的表單,Content-Type: multipart/form-data,例如:
    @Multipart
    @POST("/upload")
    Call<ResponseBody> upload(
    @Part("file") RequestBody file,
    @PartMap Map<String, RequestBody> params);
               
  • Query注解:表示查詢參數,非常簡單
  • QueryMap注解:查詢參數,用于GET查詢,需要注意的是@QueryMap可以約定是否需要encode

其它注解

  • Path注解:URL占位符,我們看一個例子:
    @GET("/image/{id}")
    Call<ResponseBody> example(@Path("id") int id);
    @GET("/user/{name}")
    Call<ResponseBody> encoded(@Path("name") String name);
    就是将例子中用@Path标注的參數用來替換伺服器位址中使用“{}”括起來的字元串,簡單說就是,這是一個占位符。
    
    但是注意的是,不能替換URL中的字元串,例如:在http://www.hitomis.com/user.do?action=findUser&username="zuck"中,不能替換action=findUser&username="zuck"路徑中的内容
               
  • Url注解:使用全路徑複寫baseUrl,用于不是同一baseUrl的場景
  • Header注解:設定請求頭,可以不設定。設定的話就是@Header裡面注解參數為”鍵”,被@Header注解的參數作為”值”,例如:
    @GET("/")
    Call<ResponseBody> foo(@Header("Accept-Language") String lang);
               
  • Headers注解:用于修飾方法,用于設定多個Header值
  • HeaderMap注解:與Header類似

好了,Retrofit就簡單介紹到這裡,下面我們還是學習一下怎麼使用這個架構!

使用

Android Studio使用需要在Gradle中添加依賴,建議先去GitHub上擷取最新的依賴,在這裡給出Retrofit的GitHub位址和官網位址:

GItHub位址:https://github.com/square/retrofit

官網位址:http://square.github.io/retrofit/

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

這是Gradle依賴,我們需要用到java/Json轉換器工廠,是以需要添加轉換器的依賴,可以直接在AS裡面搜尋最新,包括上面的Retrofit依賴也可以在AS裡面搜尋

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

如果你想用RxJava支援的擴充卡的話,需要添加擴充卡的依賴,也可以直接在AS裡面搜尋,這裡我們沒有用到,是以就不需要添加支援RxJava擴充卡的依賴。

簡單描述一下使用Retrofit架構的基本步驟:

  1. 第一步當然就是添加依賴了,Android開發的話基本都是用Gradle的,如果是Java可以用Maven。
  2. 第二步則是定義一些接口,Retrofit是面向接口程式設計的。
  3. 第三步是建立Retrofit對象,一般是使用Builder模式建立這個對象
  4. 第四步就是使用Retrofit。

上面列出的就是使用Retrofit的基本步驟,下面我們具體使用一下這個強大的網絡請求架構

我們說過Retrofit是面向接口程式設計的,是以使用之前我們需要定義一些我們用到的接口,那麼我們先看一下我們定義的接口:

package com.example.frame.common;

import com.example.frame.bean.ContributorsBean;

import java.util.List;

import okhttp3.MultipartBody;
import okhttp3.ResponseBody;
import retrofit2.Call;
import retrofit2.http.Field;
import retrofit2.http.FormUrlEncoded;
import retrofit2.http.GET;
import retrofit2.http.Multipart;
import retrofit2.http.POST;
import retrofit2.http.Part;
import retrofit2.http.Query;
import retrofit2.http.Url;

/**
 * Created by :Huawen
 * Created time : 2016/9/22 16:15
 * Description :
 * Github: https://github.com/Devin1102
 */

public interface RetrofitAPI {
/**
 * 普通的請求
 *
 * @return
 */
@GET("repos/square/retrofit/contributors")
Call<List<ContributorsBean>> getData();

/**
 * 查詢
 *
 * @param username
 * @return
 */
@GET("sayHello")
Call<ResponseBody> getQuery(@Query("username") String username);

/**
 * 上傳檔案
 *
 * @param fileUrl
 * @return
 */
@GET
Call<ResponseBody> downloadFiel(@Url String fileUrl);

/**
 * 上傳檔案
 *
 * @param description
 * @param file
 * @return
 */
@Multipart
@POST("upload")
Call<ResponseBody> uploadFile(@Part("description") ResponseBody description, @Part MultipartBody.Part file);

/**
 * 上傳表單資料
 *
 * @param username
 * @param password
 * @return
 */
@FormUrlEncoded
@POST("sayHello")
Call<ResponseBody> postForm(@Field("username") String username, @Field("password") String password);
}
           

除了接口之外,我們還将Retrofit對象的擷取封裝了,看一下封裝的代碼:

public class RetrofitService {
 protected Retrofit getRetrofit() {
    Retrofit retrofit = new Retrofit.Builder()
            .baseUrl(UrlUtils.GET_BASE_URL)
            .addConverterFactory(GsonConverterFactory.create())
            .build();
    return retrofit;
 }

    public RetrofitAPI createRetrofitAPI() {
        return getRetrofit().create(RetrofitAPI.class);
    }

}
           

這裡也沒有什麼好解析,我們這裡沒有添加OkhttpClient端,Retrofit會自動建立一個,當然我們也可以自己手動設定一個。

使用Retrofit實作簡單的GET請求

使用Retrofit架構完成GET請求非常簡單,我們先看一下代碼:

private void retrofitGet() {
    Call<List<ContributorsBean>> call = mRetrofit.getData();
    call.enqueue(new Callback<List<ContributorsBean>>() {
        @Override
        public void onResponse(Call<List<ContributorsBean>> call, Response<List<ContributorsBean>> response) {
            Log.i(TAG, "onResponse請求成功");
            List<ContributorsBean> beans = response.body();
            for (ContributorsBean contributorsBen : beans) {
                Log.i(TAG, "onResponse" + contributorsBen);
            }
        }

        @Override
        public void onFailure(Call<List<ContributorsBean>> call, Throwable t) {
            Log.i(TAG, "onFailure請求失敗");
        }
    });
}
           

這裡對應的接口裡面的GET請求,裡面我們沒有設定參數。這種請求跟Okhttp架構請求方式差不多,使用的也是異步的請求。

Retrofit實作Query參數請求

這個是一個簡單的GET請求,不過設定一些參數,上面我們說過@Query和@QueryMap是用于GET請求的設定參數,我們看一下具體的代碼:

private void retrofitGetQuery() {
    Call<ResponseBody> call = mRetrofit.getQuery("Devin");
    call.enqueue(new Callback<ResponseBody>() {
        @Override
        public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
            Log.i(TAG, getResources().getString(R.string.req_success));
        }

        @Override
        public void onFailure(Call<ResponseBody> call, Throwable t) {
            Log.i(TAG, getResources().getString(R.string.req_failed));
        }
    });

}
           

這裡我們設定一個參數,與接口一緻。

Retrofit實作POST上傳表單資料

我們在做實際項目的時候,需要向伺服器傳遞一些表單資料,比如登入、注冊功能的時候,Retrofit也給我們提供了實作表單資料上傳的方法,我們看一下具體的例子:

private void retrofitPostForm() {
    Call<ResponseBody> call = mRetrofit.postForm("Devin", "Devin");
    call.enqueue(new Callback<ResponseBody>() {
        @Override
        public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
            Log.i(TAG, getResources().getString(R.string.req_success));
        }

        @Override
        public void onFailure(Call<ResponseBody> call, Throwable t) {
            Log.i(TAG, getResources().getString(R.string.req_failed));
        }
    });

}
           

設定表單資料上傳的時候需要在接口中設定@FormUrlEncoded這個方法标記,通過@Field或者@FieldMap實作設定資料。

Retrofit實作檔案下載下傳

檔案下載下傳是經常使用的網絡操作,比如圖檔和視訊下載下傳、APP更新等。我們看一下Retrofit架構提供的檔案下載下傳功能:

private void retrofitDownloadFile() {
    Call<ResponseBody> call = mRetrofit.downloadFiel("http://repo1.maven.org/maven2/com/squareup/retrofit2/retrofit/2.1.0/retrofit-2.1.0.jar");
    call.enqueue(new Callback<ResponseBody>() {
        @Override
        public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
            InputStream is = response.body().byteStream();
            try {
                FileOutputStream fos = new FileOutputStream(new File("/sdcard/retrofit-2.1.0.jar"));
                byte[] bytes = new byte[1024];
                int len = 0;
                while ((len = is.read(bytes)) != -1) {
                    fos.write(bytes, 0, len);
                }
                fos.flush();
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
            Log.i(TAG, "onResponse" + getResources().getString(R.string.req_success));
        }

        @Override
        public void onFailure(Call<ResponseBody> call, Throwable t) {
            Log.i(TAG, "onFailure" + getResources().getString(R.string.req_failed));
        }
    });
}
           

在定義檔案下載下傳接口的時候,我們設定了@Url參數,這個是複寫BaseUrl的,用于我們不使用baseUrl的情況,裡面被@Url标志的資料是字元串類型,就是檔案下載下傳的URL。然後下載下傳成功之後,以讀流的形式将檔案儲存起來。

Retrofit實作檔案上傳

檔案上傳也是經常使用的功能,我們看一下Retrofit架構實作檔案上傳的功能:

private void retrofitUploadFile() {
    String descriptionString = "UploadFile";
    ResponseBody description = ResponseBody.create(MediaType.parse("multipart/form-data"), descriptionString);

    RequestBody requestFile = RequestBody.create(MediaType.parse("multipart/form-data"), new File("/sdcard/retrofit-2.1.0.jar"));
    MultipartBody.Part body = MultipartBody.Part.createFormData("picture", "retrofit-2.1.0.jar", requestFile);
    Call<ResponseBody> call = mRetrofit.uploadFile(description, body);
    call.enqueue(new Callback<ResponseBody>() {
        @Override
        public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
            Log.i(TAG, "onResponse" + getResources().getString(R.string.req_success));
        }

        @Override
        public void onFailure(Call<ResponseBody> call, Throwable t) {
            Log.i(TAG, "onFailure" + getResources().getString(R.string.req_failed));
        }
    });
}
           

檔案上傳需要在接口中設定@Multipart标記,在接口中我們設定了兩個參數:@Part(“description”) ResponseBody description, @Part MultipartBody.Part file,我們需要建構兩個參數,然後實作上傳。

以上是我對Retrofit架構的基本了解和具體的使用,可能寫法不是很好,如果大家有更好的寫法,歡迎大家留言交流。

上面Demo已經上傳到GitHub了,當然還有其它架構的使用Demo,Demo的GitHub位址:https://github.com/Devin1102/AndroidFrameDemo

繼續閱讀