Retrofit 使用教學

Retrofit簡介

Retrofit 是一個網路連結套件,可以在連結網路的時候做好封裝的效果,可以跟 OkHttp 以及 RxJava 合併使用

Retrofit應用

使用到的三方套件:

implementation 'com.squareup.retrofit2:retrofit:2.4.0'
implementation 'com.squareup.retrofit2:converter-gson:2.4.0'
implementation 'com.squareup.okhttp3:okhttp:3.11.0'

首先要先產生出Retrofit的實體物件:

class RetrofitServiceGenerator private constructor() {
    private val retrofit: Retrofit
    private val okHttpClient = OkHttpClient()

    init {
        retrofit = Retrofit.Builder()
                .baseUrl(Config.URL)
                .addConverterFactory(GsonConverterFactory.create())
                .client(okHttpClient)
                .build()
    }

    companion object {
        private val manager = AppClientManager()
        val client: Retrofit
            get() = manager.retrofit
    }
}

上述可看到兩行程式碼:

.addConverterFactory(GsonConverterFactory.create())

這邊是使用Google出的Json處理工具來進行轉換(Gson)

.client(okHttpClient)

另外可以看到.client這邊目前是使用預設的okHttpClient,Retrofit本身底層是使用OkHttp的Client,若有一些特殊情境(ex. 網路回傳錯誤代碼)可以寫自己的攔截器或是調整設定

實體物件建立完後,再來示範串接一個RESTFUL API

隨便拿一個網路上提供的GET網址如下

https://jsonplaceholder.typicode.com/posts

回傳資料格式如下:

[
  {
    "userId": 1,
    "id": 1,
    "title": "sunt aut facere repellat provident occaecati excepturi optio reprehenderit",
    "body": "quia et suscipit\nsuscipit recusandae consequuntur expedita et cum\nreprehenderit molestiae ut ut quas totam\nnostrum rerum est autem sunt rem eveniet architecto"
  },
  {
    "userId": 1,
    "id": 2,
    "title": "qui est esse",
    "body": "est rerum tempore vitae\nsequi sint nihil reprehenderit dolor beatae ea dolores neque\nfugiat blanditiis voluptate porro vel nihil molestiae ut reiciendis\nqui aperiam non debitis possimus qui neque nisi nulla"
  },
//...
 ]

範例回傳的格式是由四個欄位組成,因此我們可以將Response 宣告成以下類別:

class GetSampleElementResponse {
    @SerializedName("userId")
    var userId: Int = 0
    @SerializedName("id")
    var id: Int = 0
    @SerializedName("title")
    var title: String? = null
    @SerializedName("body")
    var body: String? = null
}

此時也宣告一個interface透過retrofit使用這個response class,API回來的資訊為非同部,因此使用Call的方式來處理,retrofit 會對這個行為進行非同步處理,如下:

interface ApiService {
    @GET("/posts")
    fun getSampleElement(): Call<list<getsampleelementresponse>&gt;
}

設定完成後就可以開始使用Retrofit,宣告一個按鈕並寫其觸發事件:

test.setOnClickListener {
  val apiService = RetrofitServiceGenerator.client.create(ApiService::class.java)
    apiService.index().enqueue(object : Callback<list<getsampleelementresponse>&gt; {
        override fun onResponse(call: Call<list<getsampleelementresponse>&gt;, response: Response<list<getsampleelementresponse>&gt;) {
            val sb = StringBuffer()
            val list = response.body()
            for (p in list!!) {
                sb.append(p.body)
                sb.append("\n")
                sb.append("---------------------\n")
            }
            tv.text = sb.toString()
        }

        override fun onFailure(call: Call<list<posts>&gt;, t: Throwable) {

        }
    })
}

當我們把資料抓取回來就可以透過Response物件取出我們的Json,Retrofit會自動幫我們把對應的Json轉成對應的物件GetSampleElementResponse,根據一開始interface定義好的方式轉成List,這樣就可以直接使用List。

Retrofit 延伸功能

APIService 其他使用方式

在範例中我們是使用一個簡單的Get方法,在APIService中還有許多使用方式

網址後面可以直接帶入相對應的變數:

@GET("api/user/detail/{listid}")
 fun getSampleElement(@Path("listid")id: String): Call<list<getsampleelementresponse>&gt;

若你的參數是裝在Body裡面,可以這樣使用

@GET("api/user/detail/{listid}")
 fun getSampleElement(@Path("listid")id: String , @Body requestBody: GetSampleRequestBody): Call<list<getsampleelementresponse>&gt;

若帶入的參數很多時,可以使用QueryMap包起來

@GET("api/user/detail/{listid}")
 fun getSampleElement(@Path("listid")id: String , @QueryMap map: Map<string, string?="">): Call<list<getsampleelementresponse>&gt;

OkHttp提供攔截器功能

特殊情境Sample:Server端若回傳307(或是其它需特殊處理的Error Code)錯誤後我們要自己手動轉location所導的網址,可能就需要自己寫一個攔截器,去做重新轉址的動作

 OkHttpClient client = new OkHttpClient.Builder()
                    .followRedirects(false);  //禁止OkHttp的重新轉址動作
                    .addInterceptor(new RedirectInterceptor()) //我們自己的攔截器
                    .build();
public class RedirectInterceptor implements Interceptor {
    @Override
    public Response intercept(Chain chain) throws IOException {
        okhttp3.Request request = chain.request();
        Response response = chain.proceed(request);
        int code = response.code();
        if (code == 307) {
            //拿要轉的網址
            String location = response.headers().get("Location");
            LogUtils.e("重新導的網址:", "location = " + location);
            Request newRequest = request.newBuilder().url(location).build();
            response = chain.proceed(newRequest);
        }
        return response;
    }
}

留言

這個網誌中的熱門文章

Privacy Policy