iT邦幫忙

2017 iT 邦幫忙鐵人賽
DAY 24
0
自我挑戰組

Android初學筆記系列 第 24

Day 24 - 使用Retrofit與API連線

Retrofit是專為API連線而生的第三方套件,與API連線的效率非常高,最特別的是其規範的REST框架讓程式高度解耦,好寫易維護,被譽為API連線的教科書。另外它跟OkHttp同為Square公司出品,兩者可以完美整合發揮更多功能。

然而對於像我這樣基礎知識低落的廢物來說,官網說明我當初真的看不懂,額外看了好多資料兜了一大圈才了解它的用法,但只要成功用一次之後就會發現它其實不難,希望透過今天的程式範例可以讓大家快速上手使用。

http://ithelp.ithome.com.tw/upload/images/20170108/20103849SZF1NmlgHW.png
(連線處理時間比較表,可以看出Retrofit比AsyncTask快非常多,資料來源:http://instructure.github.io/blog/2013/12/09/volley-vs-retrofit/ )

加入dependencies

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

第一行是Retrofit本體,第二行是用Google的Gson套件作為資料處理的converter,Retrofit都整合好了所以我們之後只要一行程式碼就能用Gson處理資料


加入網路權限

在AndroidManifet.xml加入,可參考Day23有圖解

<uses-permission android:name="android.permission.INTERNET" />

建立資料Model

Retrofit會自動將連線取得的資料轉成物件便於做後續處理,以我們的測試資料(https://jsonplaceholder.typicode.com/albums/1 )來說,有userId, id, title三個欄位,我們就建一個Albums物件如下,注意欄位名稱要跟API裡的一致哦

public class Albums {

    private int userId;
    private int id;
    private String title;

    public Albums(int userId, int id, String title) {
        this.userId = userId;
        this.id = id;
        this.title = title;
    }

    public int getUserId() {
        return userId;
    }

    public int getId() {
        return id;
    }

    public String getTitle() {
        return title;
    }
}

建立Interface

Interface就是各個連線的接口,每個連線是用GET或POST及其路徑、參數都寫在這裡,個人覺得是Retrofit的精華,將所有連線統一管理。

// 注意是interface而不是class哦
public interface MyAPIService {

    // 測試網站      https://jsonplaceholder.typicode.com/
    // GET網址      https://jsonplaceholder.typicode.com/albums/1
    // POST網址     https://jsonplaceholder.typicode.com/albums
    // ...typicode.com/[這裡就是API的路徑]

    @GET("albums/1")    // 設置一個GET連線,路徑為albums/1
    Call<Albums> getAlbums();    // 取得的回傳資料用Albums物件接收,連線名稱取為getAlbums

    @GET("albums/{id}") // 用{}表示路徑參數,@Path會將參數帶入至該位置
    Call<Albums> getAlbumsById(@Path("id") int id);

    @POST("albums") // 用@Body表示要傳送Body資料
    Call<Albums> postAlbums(@Body Albums albums);
}

測試網址的POST是用Body方式收參數所以我們用@Body,若是要用問號帶在網址後的參數如?id=1則應改用@Query


建立Retrofit

建立連線的基底,在此設置連線網站和converter,注意是網站哦!以測試資料來說網站為
https://jsonplaceholder.typicode.com/ ,後面API連線的albums/1那些是寫在interface裡

public class RetrofitManager {

    // 以Singleton模式建立
    private static RetrofitManager mInstance = new RetrofitManager();

    private MyAPIService myAPIService;

    private RetrofitManager() {

        // 設置baseUrl即要連的網站,addConverterFactory用Gson作為資料處理Converter
        Retrofit retrofit = new Retrofit.Builder()
                .baseUrl("https://jsonplaceholder.typicode.com/")
                .addConverterFactory(GsonConverterFactory.create())
                .build();

        myAPIService = retrofit.create(MyAPIService.class);
    }

    public static RetrofitManager getInstance() {
        return mInstance;
    }

    public MyAPIService getAPI() {
        return myAPIService;
    }
}

到這邊就建立完成,可以開始使用囉。

GET連線

(若昨天有用OkHttp練習的話,今天改用Retrofit要將程式最上面import中OkHttp的部分先去掉喔)

操作步驟:

  1. 宣告MyAPIService
  2. 透過RetrofitManager取得連線基底
  3. 建立連線的Call
  4. 執行Call
public class MainActivity extends AppCompatActivity {

    // 1. 宣告MyAPIService
    MyAPIService myAPIService;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // 2. 透過RetrofitManager取得連線基底
        myAPIService = RetrofitManager.getInstance().getAPI();

        // 3. 建立連線的Call,此處設置call為myAPIService中的getAlbums()連線
        Call<Albums> call = myAPIService.getAlbums();

        // 4. 執行call
        call.enqueue(new Callback<Albums>() {
            @Override
            public void onResponse(Call<Albums> call, Response<Albums> response) {
                // 連線成功
                // 回傳的資料已轉成Albums物件,可直接用get方法取得特定欄位
                String title = response.body().getTitle();  
                Log.d("title", title);
            }

            @Override
            public void onFailure(Call<Albums> call, Throwable t) {
                // 連線失敗
            }
        });
    }
}

若要用Interface中的第二個GET方式getAlbumsById,只要改call那行的method並將參數傳入就可以了

Call<Albums> call = myAPIService.getAlbumsById(2);
call.enqueue(new Callback<Albums>() {
// 略..
});

POST連線

POST時測試網址須用Body的形式傳送,我們已經在Interface裡設定好了,只要建一個Albums物件作為Body送出就可以了。

// 建立要POST的物件
Albums albums = new Albums(1, 1, "Castle on the Hill");

// 將物件作為postAlbums的參數
Call<Albums> call = myAPIService.postAlbums(albums);

// 執行call
call.enqueue(new Callback<Albums>() {
    // 略..
});

與OkHttp整合

Retrofit的連線基底可設置的功能不像OkHttp那麼多,但沒關係,兩者師出同門,可以直接把OkHttpClient整碗拿過來用,只要在RetrofitManager中加入就可以了

我們把昨天的OkHttpClient直接拿來用,會幫我們印出連線Log,此外再多加入連線Timeout時間,當連線30秒還未成功就放棄

private RetrofitManager() {

    // 建立OkHttpClient
    OkHttpClient okHttpClient = new OkHttpClient().newBuilder()
            .connectTimeout(30, TimeUnit.SECONDS)   // 設置連線Timeout
            .addInterceptor(new HttpLoggingInterceptor().setLevel(Level.BASIC))
            .build();

    Retrofit retrofit = new Retrofit.Builder()
            .baseUrl("https://jsonplaceholder.typicode.com/")
            .addConverterFactory(GsonConverterFactory.create())
            .client(okHttpClient)  // 將okHttpClient加入連線基底
            .build();

    myAPIService = retrofit.create(MyAPIService.class);
}

此外像OkHttp的Request、FormBody等部分Retrofit也都可以用,兩者的整合度非常高,當您需要什麼功能而Retrofit沒有時,可以到OkHttp找找看並直接拿過來。


Retrofit作為當今API連線的首選,個人覺得它的學習門檻比較高一點點,用Interface和註解的方式讓我當時一看就迷惘了,折騰好一陣子才成功把專案轉成Retrofit架構。希望今天的文章可以讓大家比較好了解上手,如果還是看不懂也沒關係,請去看其他大大的文章不要就此放棄XD。

明天介紹同為Square公司出品的圖片讀取套件Picasso,用於處理網路圖片時非常方便,而且程式碼超級簡潔,明天看就知道囉。


上一篇
Day 23 - OkHttp網路連線
下一篇
Day 25 - 使用Picasso讀取圖片
系列文
Android初學筆記30

1 則留言

0
qsc236578
iT邦新手 5 級 ‧ 2019-12-13 12:49:04

請問API回傳不是字串而是陣列的話該怎麼處理呢

EX:
data:[{
id:1
name:"李"
,
id:2
name:"王"}]

我要留言

立即登入留言