天天看點

每天學習一個Android中的常用架構——5.Retrofit

文章目錄

  • ​​1.簡介​​
  • ​​2.特性​​
  • ​​3.示範​​
  • ​​3.1 內建​​
  • ​​3.2 配置​​
  • ​​3.3 布局檔案和URL封裝​​
  • ​​3.5 GET請求——傳統網絡參數​​
  • ​​3.6 GET請求——RestFul風格​​
  • ​​3.7 POST請求​​
  • ​​4.源碼位址​​

1.簡介

在介紹Android中相關網絡架構的時候,我們介紹過了Square公司制作的OkHttp和OKIo。事實上,這兩個架構配合使用的時候,基本上已經可以完成大部分的網絡業務需求了。不過,學習是永遠沒有盡頭的。這篇部落格中,我們将介紹同樣由Square公司制作,目前同樣流行的另一個網絡架構——Retrofit

Retrofit是一個現在比較火的網絡請求架構,它的底層是依靠OkHttp實作的。确切的講,Retrofit是對OkHttp的進一步封裝,它的功能更加強大,支援同步和異步、支援多種資料的解析(預設使用Gson),也支援RxJava。

Retrofit的一個優勢,就是簡潔易用,它通過注解配置網絡請求的參數,采用大量的設計模式來簡化我們的使用。而且它的拓展性也做的相當的好,Retrofit的功能子產品高度封裝,高内聚低耦合,我們可以自定義自己想要的元件,比如說我們可以自己選擇解析工具而不用預設的Gson,實作了高度的定制性。

除此之外,Retrofit還有諸如性能好,處理速度快,代碼簡化等優勢,給足了理由讓我們去嘗試使用這款網絡請求架構。

2.特性

在使用Retrofit之前,我們應該先了解它的特性。使用Retrofit的過程中,由于其大量注解開發的形式,我覺得十分類似于Java中的Spring MVC架構,通過辨別專屬的注解,就可以達到快速的配置和開發,一些Retrofit常見的方法注解如圖所示:

每天學習一個Android中的常用架構——5.Retrofit

除此之外,一些常見的字段注解如圖所示:

每天學習一個Android中的常用架構——5.Retrofit

将這些注解進行整理,可以得到一張大緻的結構圖:

其中:

  1. 網絡請求方法
  2. 每天學習一個Android中的常用架構——5.Retrofit
  3. 标記類
  4. 每天學習一個Android中的常用架構——5.Retrofit
  5. 網絡請求參數
  6. 每天學習一個Android中的常用架構——5.Retrofit

看到這麼多的注解,是否有一種頭暈目眩的感覺?沒關系,在本篇部落格中,隻會示範比較常用的注解:​

​@GET,@POST,@Path,@Query​

​​ ,其他的代碼可以根據需求進行相應地調用,調用過程是類似的,具體的api調用還是那句話:檢視​​Retrofit官網​​擷取更多的相關資訊。

3.示範

3.1 內建

在使用任何架構之前,內建都是第一步。由于Retrofit已經用到了OkHttp,是以這裡隻需要導入Retrofit一個依賴即可,修改module下的build.gradle,代碼如下:

implementation 'com.squareup.retrofit2:retrofit:2.8.1'      

修改完成後Sync一下,確定Retrofit內建到了你的項目中。

3.2 配置

這裡的配置主要是針對在使用Retrofit遇到的一個坑,簡單提一下避免讀者在使用的時候踩坑。

問題:導入Retrofit依賴後,在build工程後出現如圖所示的錯誤

每天學習一個Android中的常用架構——5.Retrofit
每天學習一個Android中的常用架構——5.Retrofit

原因:從出錯原因可以很簡單地看出,使用Retrofit需要你的Android版本最低達到Android O(即api 26),是以隻需要修改有關此處的配置資訊即可。

解決方法:在項目下的build.gradle檔案添加一個​

​compileOptions​

​閉包,聲明JDK版本,代碼如下:

apply plugin: 'com.android.application'

android {
    compileSdkVersion 29
    buildToolsVersion "29.0.3"

    defaultConfig {
        applicationId "com.androidframelearn.http_retrofit"
        minSdkVersion 16
        targetSdkVersion 29
        versionCode 1
        versionName "1.0"

        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    }

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }

    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }

}

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])

    implementation 'com.squareup.retrofit2:retrofit:2.8.1'

    implementation 'androidx.appcompat:appcompat:1.1.0'
    implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'androidx.test.ext:junit:1.1.1'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
}      

3.3 布局檔案和URL封裝

接下來,我們直接開始布局檔案activity_main.xml的編寫。該布局很簡單,僅有四個按鈕,代碼如下:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity"
    android:orientation="vertical">

    <Button
        android:id="@+id/btn_get"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="發送get請求——通過http拼接網絡參數"/>

    <Button
        android:id="@+id/btn_get_restful"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="發送get請求——通過restful拼接網絡參數"/>

    <Button
        android:id="@+id/btn_post"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="發送post請求"/>


</LinearLayout>      

之後,我們簡單用字元串封裝一下要請求的URL。這裡封裝兩個URL,是為了示範分别通過傳統方式和RestFul風格送出GET請求的不同效果(現在很少有RestFul風格的免費開放api,是以這裡就隻能假設一個本地的Tomcat伺服器,然後放置一個JSON檔案來進行校驗),代碼如下(伺服器URL不固定,根據自己的伺服器路徑名進行相應修改):

private static final String URL = "https://tcc.taobao.com/cc/json/";

    private static final String LOCALURL = "http://10.0.2.2:8080/";      

這裡的​

​URL​

​​是一個目前還開放的可以檢視電話歸屬地資訊的api,而​

​LOCALURL​

​則是本地伺服器的api。

另外:保證你的URL路徑輸入到網頁的連結欄中是可以通路的!這點很重要,如果你輸入的網頁是不存在的(即body為null),那麼在調用​

​response.body().string()​

​時就會報出空指針的異常,如圖所示:

每天學習一個Android中的常用架構——5.Retrofit

3.5 GET請求——傳統網絡參數

編寫一個名為TestService的接口,其中有一個名為getInfoByHttp的方法,代碼如下:

public interface TestService {
/**
     /**
     * 通過拼接網絡參數的方式送出GET請求(下面的參數為https://tcc.taobao.com/cc/json/mobile_tel_segment.htm?tel=15878896543)
     * 注意:這種方式為http的參數傳遞方式,若使用這種方式請求,需要在注解上使用@Query而非@Path
     * @param tel
     * @return
     */
    @GET("mobile_tel_segment.htm")
    Call<ResponseBody> getInfoByHttp(@Query("tel") String tel);
}      

我們定義了一個getInfoByHttp方法,其傳回類型為retrofit的Call類型,尖括号裡是okhttp的ResponseBody類型,​

​@GET​

​​注解的作用是聲明采用GET的方法進行網絡請求,而​

​@Query​

​​注解的作用是聲明其之後的​

​String tel​

​​參數,将以路徑的形式被拼接到​

​@GET​

​​注解括号裡的​

​{mobile_tel_segment.htm}​

​​處。比如說,之後我們在調用getInfoByHttp方法時傳入參數​

​15878896543​

​​,​

​@GET​

​​注解裡将會被轉化為 ​

​mobile_tel_segment.htm?tel=15878896543​

​​,這個​

​15878896543​

​​又會被接到我們之前設定的​

​baseUrl​

​​之後,變成 ​

​https://tcc.taobao.com/cc/json/mobile_tel_segment.htm?tel=15878896543​

​,這樣就變成了一個完整的網絡請求位址。

其實使用Retrofit進行GET請求有點類似于OkHttp,步驟可以大抵分為:

  • 通過建造者模式建立Retrofit對象
  • 通過Retrofit對象的​

    ​create()​

    ​方法來擷取服務接口的執行個體
  • 通過接口服務的執行個體來建立Call對象
  • 通過Call對象的​

    ​enqueue()​

    ​異步送出請求
  • 通過​

    ​response.body().string()​

    ​擷取請求資訊

代碼如下:

private void getByRetrogit() {
        btn_get.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                // 1.使用建造者模式建立Retrofit執行個體
                Retrofit retrofit = new Retrofit.Builder()
                        .baseUrl(URL)
                        .build();
                // 2.使用Retrofit對象調用create()建立服務執行個體(BookService)
                TestService testService = retrofit.create(TestService.class);
                // 3.通過服務執行個體建立Call對象
                Call<ResponseBody> call = testService.getInfoByHttp("15878896543");
                // 4.通過Call對象建構網絡請求(異步)
                call.enqueue(new Callback<ResponseBody>() {
                    @Override
                    public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
                        try {
                            String output = response.body().string();
                            Log.i(TAG,"GET請求發送成功!擷取到的資訊為:" + output);
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                    @Override
                    public void onFailure(Call<ResponseBody> call, Throwable t) {
                        Log.i(TAG,"GET請求發送失敗");
                    }
                });
            }
        });
    }      

測試效果如圖所示:

每天學習一個Android中的常用架構——5.Retrofit

3.6 GET請求——RestFul風格

編寫一個名為TestService的接口,其中有一個名為getInfoByRestFul的方法,代碼如下:

public interface TestService {
/**
     * 通過restful的方式送出GET請求(下面的參數為http://10.0.2.2:8080/update74.json)
     * @param filename
     * @return
     */
    @GET("{filename}")
    Call<ResponseBody> getInfoByRestFul(@Path("filename") String filename);
}      

我們定義了一個getInfoByRestFul方法,其傳回類型為retrofit的Call類型,尖括号裡是okhttp的ResponseBody類型,​

​@GET​

​​注解的作用是聲明采用GET的方法進行網絡請求,而​

​@Path​

​​注解的作用是聲明其之後的​

​String filename​

​​參數,将以路徑的形式被替換到​

​@GET​

​​注解括号裡的​

​{filename}​

​​處。比如說,之後我們在調用getInfoByRestFul方法時傳入參數​

​update74.json​

​​,​

​@GET​

​​注解裡将會被轉化為 ​

​update74.json​

​​,這個​

​update74.json​

​​又會被接到我們之前設定的​

​baseUrl​

​​之後,變成 ​

​http://10.0.2.2:8080/update74.json​

​,這樣就變成了一個完整的網絡請求位址。

其實使用Retrofit進行GET請求有點類似于OkHttp,步驟可以大抵分為:

  • 通過建造者模式建立Retrofit對象
  • 通過Retrofit對象的​

    ​create()​

    ​方法來擷取服務接口的執行個體
  • 通過接口服務的執行個體來建立Call對象
  • 通過Call對象的​

    ​enqueue()​

    ​異步送出請求
  • 通過​

    ​response.body().string()​

    ​擷取請求資訊

代碼如下:

private void getByRetrogitForRestful() {
        btn_get_restful.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                // 1.使用建造者模式建立Retrofit執行個體
                Retrofit retrofit = new Retrofit.Builder()
                        .baseUrl(LOCALURL)
                        .build();
                // 2.使用Retrofit對象調用create()建立服務執行個體(BookService)
                TestService testService = retrofit.create(TestService.class);
                // 3.通過服務執行個體建立Call對象
                Call<ResponseBody> call = testService.getInfoByRestFul("update74.json");
                // 4.通過Call對象建構網絡請求(異步)
                call.enqueue(new Callback<ResponseBody>() {
                    @Override
                    public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
                        try {
                            String output = response.body().string();
                            Log.i(TAG,"GET請求發送成功!擷取到的資訊為:" + output);
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                    @Override
                    public void onFailure(Call<ResponseBody> call, Throwable t) {
                        Log.i(TAG,"GET請求發送失敗");
                    }
                });
            }
        });
    }      

測試效果如圖所示:

每天學習一個Android中的常用架構——5.Retrofit

3.7 POST請求

編寫一個名為TestService的接口,其中有一個名為postInfoByRestFul的方法,代碼如下:

public interface TestService {
/**
     * 通過restful的方式送出POST請求(下面的參數為http://10.0.2.2:8080/update74.json)
     * @param filename
     * @return
     */
    @POST("{filename}")
    Call<ResponseBody> postInfoByRestFul(@Path("filename") String filename);
}      

事實上,發送POST請求與GET請求的差別就是注解,需要使用​

​@POST​

​。其他的調用方式跟上面是一樣的,建立POST請求送出的步驟也是跟GET請求也一樣,是以這裡就不再贅述了,直接貼上代碼:

private void postByRetrogit() {
        btn_post.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                // 1.使用建造者模式建立Retrofit執行個體
                Retrofit retrofit = new Retrofit.Builder()
                        .baseUrl(LOCALURL)
                        .build();
                // 2.使用Retrofit對象調用create()建立服務執行個體(BookService)
                TestService testService = retrofit.create(TestService.class);
                // 3.通過服務執行個體建立Call對象
                Call<ResponseBody> call = testService.postInfoByRestFul("update74.json");
                // 4.通過Call對象建構網絡請求(異步)
                call.enqueue(new Callback<ResponseBody>() {
                    @Override
                    public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
                        try {
                            String output = response.body().string();
                            Log.i(TAG,"POST請求發送成功!擷取到的資訊為:" + output);
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                    @Override
                    public void onFailure(Call<ResponseBody> call, Throwable t) {
                        Log.i(TAG,"POST請求發送失敗");
                    }
                });
            }
        });
    }      

測試效果如圖所示:

4.源碼位址

繼續閱讀