Android---網路程式設計之Retrofit2整體結構瞭解以及+Okhttp3+rxjava2使用
Retrofit
相關
前言
要不要寫這篇文章,其實我糾結了好久,因為網上已經有好多的關於Retrofit的文章了,不乏有很經典的文章和部落格,讀後真的收益良多,相比以前的只會用,成長良多!但是,看完以後始終覺著那些東西是非而非,時有感覺那都不是自己的東西,畢竟那不是按照自己的思路來的,所以有必要按照自己的思路再來捋一遍,人啊,關鍵得有自己的想法啊,不管是在程式設計上還是人生路上!
貼一個我感覺寫的不錯的關於Retrofit的部落格,就不轉載了
我也把我的心得及思路記錄一下,希望朋友們也多思考,畢竟思考也是成功他老母啊!
簡介
Retrofit2是基於okhttp的型別安全HTTP客戶端,retrofit2只需要專注對請求的封裝處理,而真正的網路請求是由okhttp發起的,然後處理返回的響應。
- 基於Okhttp
- 支援Restful API風格:一套網際網路應用程式的API設計理論,方便不同的前端裝置與後端進行通訊。不錯的介紹
- 支援rxjava,進行執行緒間轉換
支援轉換器序列化資料,也可以自定以轉化器
- Gson: com.squareup.retrofit2:converter-gson
- Jackson: com.squareup.retrofit2:converter-jackson
- Moshi: com.squareup.retrofit2:converter-moshi
- Protobuf: com.squareup.retrofit2:converter-protobuf
- Wire: com.squareup.retrofit2:converter-wire
- Simple XML: com.squareup.retrofit2:converter-simplexml
- Scalars (primitives, boxed, and String):com.squareup.retrofit2:converter-scalars
基於Retrofit2是對請求介面的封裝,,而我們只去關注怎樣去構造一個可用的URL就行了,至於他底層怎樣去處理(註解+動態代理)我們就不用去管了。
構成
按照我們分析Okhttp3的套路來一遍(這只是我自己的思路,大家儘量按自己的思路來理解),先整體把握一下Retrofit2的內容,瞭解其整體構成,然後到區域性使用,再到底層原理
整體大概—–具體細節——-底層原理
Retrofit2整個專案分成兩個包
- retrofit2:Retrofit將REST API轉換為Java介面。
- retrofit2.http:用於控制HTTP請求行為的介面方法註解。
介面 | 功能 |
---|---|
Call | 向web伺服器傳送請求並返回響應。 |
CallAdapter | 響應適配特定物件的連結呼叫 |
Callback | 請求回撥 |
Converter | 將HTTP轉換為物件。使用工廠設計模式 |
類 | 功能 |
---|---|
BuiltInConverters | Retrofit2內建的簡單資料轉換器(5種) |
DefaultCallAdapterFactory | 為I/O和應用程式級別的相同執行緒建立呼叫介面卡。 |
ExecutorCallAdapterFactory | 新的執行緒建立呼叫介面卡 |
HttpException | 異常,非2xx HTTP響應。 |
OkHttpCall | 對Okhhtp3的包裝 |
ParameterHandler | 引數處理程式,將我們解析的註解引數設定到RequestBuilder裡 |
Platform | 返回當前平臺 |
RequestBuilder | 配置請求引數 |
Response | HTTP響應 |
Retrofit | 通過在宣告的方法上使用註解,可以將Java介面轉換為HTTP呼叫。 |
ServiceMethod | 將介面方法的呼叫轉換為HTTP呼叫。其實Retrofit的creat方法就是返回的其的方法呼叫。 |
Utils | 工具類 |
註解 | 功能(這裡,我分為4種註解:方法註解;標記註解;引數註解;其他) |
---|---|
方法註解 | 表示我們是以什麼方法提交HTTP請求 |
DELETE | 請求刪除URL指向的資源。 |
GET | get請求 |
HEAD | 請求Request-URI所標識的資源響應訊息報頭,HEAD方法可以在響應時不返回訊息體。 |
HTTP | 使用自定義HTTP請求方法 |
OPTIONS | 請求查詢伺服器的效能,或者查詢與資源相關的選項。 |
PATCH | PATCH方法是新引入的,是對PUT方法的補充,用來對已知資源進行區域性更新 |
POST | post請求 |
PUT | 與GET相反,請求伺服器儲存一個資源,並用Request-URI做為其標識 |
標記註解 | 標識此次請求的特性 |
FormUrlEncoded | 表示請求體將使用表單URL編碼 |
Multipart | 表示支援檔案上傳,需要上傳檔案時需要在方法前新增次註解 |
Streaming | 表示處理返回的響應流,通常用於大檔案下載 |
引數註解 | 為構建正常請求配置的引數 |
Body | 適用於POST/PUT請求,引數有多個,可以封裝成Body物件 |
Field | Post方式傳遞簡單的鍵值對,需要新增@FormUrlEncoded表示表單提交 |
FieldMap | 為表單編碼的請求指定鍵/值對。將Field封裝成map集合 |
Part | 檔案上傳 |
PartMap | 以map的形式上傳檔案 |
Query | 查詢引數附加到URL |
QueryMap | Query引數比較多,那麼可以通過@QueryMap方式將所有的引數整合在一個Map統一傳遞 |
QueryName | 適用於引數沒有值的情況 |
其他 | |
Header | 替換請求頭 |
HeaderMap | 需要替換的請求頭比較多,可以新增到Map集合裡進行統一提交 |
Headers | 將值新增到請求頭中 |
Path | URL佔位符,用於替換和動態更新URL中的字元 |
Url | 對baseUrl不統一的情況需要此註解,用全路徑覆蓋baseUrl。 |
使用
到現在,我們對Retrofit2整體架構已經有了大概的瞭解,至於使用那就更加簡單了。
使用流程
- 新建一個java介面。例:
//建議 以你http返回的型別命名型別,若是Restful API風格那就更好了,可以把一系列方法都定義在這一個接口裡
public interface NewService {
//baseURL(Retrofit裡配置http://image.baidu.com/data/)+方法註解(GET)裡的字元(imgs)+方法裡面(Query註解的引數)構成一個完整資源路徑
/**
* 獲取美女圖片
* API獲取途徑http://www.jb51.net/article/61266.htm
* eg: http://image.baidu.com/data/imgs?sort=0&pn=0&rn=20&col=美女&tag=全部&tag3=&p=channel&from=1
* 通過分析,推斷並驗證了其中欄位的含義,col表示頻道,tag表示的是全部的美女,也可以是其他Tag,pn表示從第幾張圖片開始,rn表示獲取多少張
* @param
* @return
*/
//這個是適配Rxjava2以後的方法
@Headers(CACHE_CONTROL_NETWORK)
@GET("imgs")
Observable<BeautyPicture> getWelfarePhoto(@Query("sort") int sort,
@Query("pn")int startImage,
@Query("rn")int size,
@Query("col")String col,
@Query("tag")String tag,
@Query("tag3")String tag3,
@Query("p")String channel,
@Query("from")int from);
//這個是原始的
@Headers(CACHE_CONTROL_NETWORK)
@GET("imgs")
Call<BeautyPicture> getWelfarePhotoCall(@Query("sort") int sort,
@Query("pn")int startImage,
@Query("rn")int size,
@Query("col")String col,
@Query("tag")String tag,
@Query("tag3")String tag3,
@Query("p")String channel,
@Query("from")int from);
}
- 配置Retrofit2屬性,通過動態代理返回一個我們之前建立的介面物件
public static <T> T getInstanceStringUrl(String url,Class<T> clazz){
Retrofit retrofit = new Retrofit.Builder()
// .client()//配置OKhttp客戶端
.baseUrl(url)
.addConverterFactory(GsonConverterFactory.create())//json轉物件
// .validateEagerly() 在Call時,是否立即驗證介面中的方法配置
// .callbackExecutor() //回撥
.build();
T service = retrofit.create(clazz);
return service;
}
- 通過介面物件呼叫我們介面中的方法,返回一個Call物件(適配Rxjava後,返回的是Observable物件等),然後繼續非同步還是同步請求網路
RetrofitClient.getInstanceStringUrl("http://image.baidu.com/data/", NewService.class)
.getWelfarePhotoCall(0,0,50,"美女","全部", "", "channel", 1)
.enqueue(new retrofit2.Callback<BeautyPicture>() {
@Override
public void onResponse(retrofit2.Call<BeautyPicture> call, final retrofit2.Response<BeautyPicture> response) { //因為底層是基於okhttp進行網路請求,所以需要我們自己進行執行緒間轉換,若適配Rxjava後就沒這麼麻煩了。
//請求成功回撥方法
runOnUiThread(new Runnable() {
@Override
public void run() {
tvShow.setText(response.body().col+":"+response.body().col);
}
});
}
@Override
public void onFailure(retrofit2.Call<BeautyPicture> call, final Throwable t) {
//請求失敗回撥方法
runOnUiThread(new Runnable() {
@Override
public void run() {
tvShow.setText(t.getMessage());
}
});
}
});
配置okhttp:一般我們網路請求配置一個全域性OKHTTP就行了,我一般是在Application裡初始化。
public static void initNet(Context context ,Cache cache, Interceptor interceptor, Interceptor netInterceptor){
if(cache==null){
//快取目錄
cache = new Cache(new File(context.getCacheDir(),"cache"),1024*1024*1024);
}
OkHttpClient.Builder builder = new OkHttpClient.Builder()
.cache(cache)
.connectTimeout(10, TimeUnit.SECONDS)
.retryOnConnectionFailure(true);
if(interceptor!=null){
builder.addInterceptor(interceptor);
}
if(netInterceptor!=null){
builder.addNetworkInterceptor(netInterceptor);
}
okHttpClient = builder.build();
}
下面是rxjava版的請求
NetUtils.getService(MainActivity.this)
.subscribe(new Observer<BeautyPicture>() {
@Override
public void onSubscribe(@NonNull Disposable d) {
}
@Override
public void onNext(@NonNull BeautyPicture beautyPicture) {
tvShow.setText(beautyPicture.col+":"+beautyPicture.getTag());
}
@Override
public void onError(@NonNull Throwable e) {
}
@Override
public void onComplete() {
}
});
NetUtils類,我在這裡做了一層封裝,若有多個HTTP API ,直接在這裡呼叫就行,就不用新建Retrofit配置了,因為大部分APP裡的網路配置都是一樣的
public class NetUtils {
private static ObservableTransformer<BeautyPicture,BeautyPicture> transformer = getSchedulers();
public static Observable<BeautyPicture> getService(RxActivity activity){
NewService newService = RetrofitClient.getOkhttpClientServiceStringUrl("http://image.baidu.com/data/", NewService.class);
return newService.getWelfarePhoto(0,0,50,"美女","全部", "", "channel", 1)
.compose(transformer)
.compose(activity.<BeautyPicture>bindToLifecycle());
}
private static <T>ObservableTransformer<T,T> getSchedulers(){
return new ObservableTransformer<T, T>() {
@Override
public ObservableSource<T> apply(@NonNull Observable<T> upstream) {
return upstream.subscribeOn(Schedulers.io())
.unsubscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread());
}
};
}
}
public static <T> T getOkhttpClientServiceStringUrl(String url,Class<T> tClass){
Retrofit retrofit = new Retrofit.Builder()
.client(okHttpClient)
.baseUrl(url)
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.addConverterFactory(GsonConverterFactory.create())
.build();
T service = retrofit.create(tClass);
return service;
}
這樣我們的網路請求到資料顯示都完成了,當然上面只展示GET註解,下面我們來看一下其他的一些常用註解的使用方式。
其他註解使用方式
- POST方式
// http://app.pearvideo.com/clt/jsp/v2/home.jsp
// http://app.pearvideo.com/clt/jsp/v2/getCategoryConts.jsp
// http://app.pearvideo.com/clt/jsp/v2/content.jsp?contId=1064146
//這裡我實際抓取了一下Get也可以
@FormUrlEncoded
@POST("getCategoryConts.jsp")
Observable<VideoListDate> getKankanVideoFromCate(@HeaderMap HashMap<String, String> headers,
@Field("hotPageidx")int page,
@Field("start")int start,
@Field("categoryId")String categoryId);
下面是我擷取我的一個開源專案裡的一個使用(Kotlin寫的)
init {
headersMap.put("X-Channel-Code","official")
headersMap.put("X-Client-Agent","Xiaomi")
headersMap.put("X-Client-Hash","2f3d6ffkda95dlz2fhju8d3s6dfges3t")
headersMap.put("X-Client-ID","123456789123456")
headersMap.put("X-Client-Version","2.3.2")
headersMap.put("X-Long-Token","")
headersMap.put("X-Platform-Type","0")
headersMap.put("X-Platform-Version","5.0")
headersMap.put("X-User-ID","")
}
fun getCategoryDate(categoryId: String) {
mCategoryId = categoryId
var time = System.currentTimeMillis()/1000
headersMap.put("X-Serial-Num",time.toString())
start = 0
RetrofitService.getKankanVideoFromCate(headersMap,page,start,categoryId)
.doOnSubscribe { mView.showLoading() }
.compose(mView.bindToLife())
.subscribe(object :Observer<VideoListDate>{
override fun onComplete() {
mView.hideLoading()
}
override fun onSubscribe(d: Disposable) {
}
override fun onNext(t: VideoListDate) {
mView.loadVideoForCategoryidDate(t)
page++
start+=t.contList?.size!!
}
override fun onError(e: Throwable) {
mView.showNetError(object :EmptyErrLayout.OnReTryListener{
override fun onReTry() {
getCategoryDate(categoryId)
}
})
}
})
}
其他幾種情況我就沒遇到過了,請允許我在此貼一些網上的事例程式碼,但是通用的模式都是,我們根據實際註釋,配置我們需要的模組就可以了
- 檔案上傳,像下面的例子,我們配置好註解,在呼叫方法之前設定好相應的物件就行了。
/**
* 單張圖片上傳
* retrofit 2.0的上傳和以前略有不同,需要藉助@Multipart註解、@Part和MultipartBody實現。
*
* @param url
* @param file
* @return
*/
@Multipart
@POST("{url}")
Call<HttpResult<News>> upload(@Path("url") String url, @Part MultipartBody.Part file);
/**
* 多張圖片上傳
*
* @param map
* @return
*/
@Multipart
@POST("upload/upload")
Call<HttpResult<News>> upload(@PartMap Map<String, MultipartBody.Part> map);
/**
* 圖文混傳
*
* @param post
* @param map
* @return
*/
@Multipart
@POST("")
Call<HttpResult<News>> register(@Body News post, @PartMap Map<String, MultipartBody.Part> map);
/**
* 這裡需要注意的是如果下載的檔案較大,比如在10m以上,那麼強烈建議你使用@Streaming進行註解,否則將會出現IO異常.
*
* @param fileUrl
* @return
*/
@Streaming
@GET
Observable<HttpResult<News>> downloadPicture2(@Url String fileUrl);
以上介面例項出自 作者:wo叫天然呆
連結:https://www.jianshu.com/p/73216939806a
來源:簡書
著作權歸作者所有。商業轉載請聯絡作者獲得授權,非商業轉載請註明出處。
Okhttp3+Retrofit2+RxJava2的基本使用,我們基本都瞭解完了,很簡單的對吧!
上面用到的一些程式碼戳這裡