1. 程式人生 > >Android---網路程式設計之Retrofit2整體結構瞭解以及+Okhttp3+rxjava2使用

Android---網路程式設計之Retrofit2整體結構瞭解以及+Okhttp3+rxjava2使用

Retrofit

相關

前言

要不要寫這篇文章,其實我糾結了好久,因為網上已經有好多的關於Retrofit的文章了,不乏有很經典的文章和部落格,讀後真的收益良多,相比以前的只會用,成長良多!但是,看完以後始終覺著那些東西是非而非,時有感覺那都不是自己的東西,畢竟那不是按照自己的思路來的,所以有必要按照自己的思路再來捋一遍,人啊,關鍵得有自己的想法啊,不管是在程式設計上還是人生路上!

貼一個我感覺寫的不錯的關於Retrofit的部落格,就不轉載了

我也把我的心得及思路記錄一下,希望朋友們也多思考,畢竟思考也是成功他老母啊!

簡介

Retrofit2是基於okhttp的型別安全HTTP客戶端,retrofit2只需要專注對請求的封裝處理,而真正的網路請求是由okhttp發起的,然後處理返回的響應。

  • 基於Okhttp
  • 支援Restful API風格:一套網際網路應用程式的API設計理論,方便不同的前端裝置與後端進行通訊。不錯的介紹
  • 支援rxjava,進行執行緒間轉換
  • 支援轉換器序列化資料,也可以自定以轉化器

    1. Gson: com.squareup.retrofit2:converter-gson
    2. Jackson: com.squareup.retrofit2:converter-jackson
    3. Moshi: com.squareup.retrofit2:converter-moshi
    4. Protobuf: com.squareup.retrofit2:converter-protobuf
    5. Wire: com.squareup.retrofit2:converter-wire
    6. Simple XML: com.squareup.retrofit2:converter-simplexml
    7. 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的基本使用,我們基本都瞭解完了,很簡單的對吧!
上面用到的一些程式碼戳這裡

拼搏在技術道路上的一隻小白And成長之路