文章目錄
- 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常見的方法注解如圖所示:
除此之外,一些常見的字段注解如圖所示:
将這些注解進行整理,可以得到一張大緻的結構圖:
其中:
- 網絡請求方法
- 标記類
- 網絡請求參數
看到這麼多的注解,是否有一種頭暈目眩的感覺?沒關系,在本篇部落格中,隻會示範比較常用的注解:
@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工程後出現如圖所示的錯誤
原因:從出錯原因可以很簡單地看出,使用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()
時就會報出空指針的異常,如圖所示:
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請求發送失敗");
}
});
}
});
}
測試效果如圖所示:
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請求發送失敗");
}
});
}
});
}
測試效果如圖所示:
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請求發送失敗");
}
});
}
});
}
測試效果如圖所示: