Android 網絡請求:Retrofit 使用

Android 網絡請求:Retrofit 使用

網絡請求:retrofit+okhttp3

示例項目:

有妹子的日報 MeiZiNews

總結:結合 RxJava 使用得十分的爽。如果沒有用到 RxJava,個人感覺這樣使用和用 OkHttp3 的封裝差不多


配置

Retrofit 2.0 + OkHttp 3.0 配置


轉換器

retrofit

RxAndroid+Retrofit環境搭建

轉換器可以被添加到支持其他類型。六兄弟模塊適應流行的序列化庫爲您提供方便。
Converters can be added to support other types. Six sibling modules adapt popular serialization libraries for your convenience.

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

直接返回String類型需引入:ScalarsConverterFactory.create()

<span style="font-size:14px;"> retrofit = new Retrofit.Builder()
            .client(MyOkHttpClient.getMyOkHttpClient().getOkHttpClient())//設置不同的底層網絡庫
            .baseUrl(strBaseUrl)
            .addConverterFactory(ScalarsConverterFactory.create())//添加 String類型[ Scalars (primitives, boxed, and String)] 轉換器
            .addCallAdapterFactory(RxJavaCallAdapterFactory.create())//添加 RxJava 適配器
            .build();</span>

返回Gson類型需引入:GsonConverterFactory.create()

 retrofit = new Retrofit.Builder()
            .client(MyOkHttpClient.getMyOkHttpClient().getOkHttpClient())//設置不同的底層網絡庫
            .baseUrl(strBaseUrl)
            .addConverterFactory(GsonConverterFactory.create())//添加 json 轉換器
            .addCallAdapterFactory(RxJavaCallAdapterFactory.create())//添加 RxJava 適配器
            .build();

Retrofit 結合 RxJava使用

Retrofit 結合 RxJava使用

  1. 添加依賴

    compile 'com.squareup.retrofit2:retrofit:2.0.0-beta4'
    compile 'com.squareup.retrofit2:converter-gson:2.0.0-beta4'
    
    compile 'com.squareup.retrofit2:adapter-rxjava:2.0.0-beta4'
    
    compile 'io.reactivex:rxandroid:1.1.0'
    compile 'io.reactivex:rxjava:1.1.0'

    前兩個是 Retrofit 和 Gson 的依賴,第三個是 Retrofit 中 RxJava 轉換器的依賴,最後兩個就是 RxJava 和 Rx Android 的依賴

  2. 編寫 API

    使用 RxJava 的情況下,接口文件稍有修改,接口中的方法的返回類型不再是 Call 而是 Observable 類型:

  3.  public interface GitHub {
    
                @GET("/repos/{owner}/{repo}/contributors")
                Call<List<Contributor>> contributors(@Path("owner") String owner,@Path("repo") String repo);
    
                //使用 RxJava 的方法,返回一個 Observable
                @GET("/repos/{owner}/{repo}/contributors")
                Observable<List<Contributor>> RxContributors(@Path("owner") String owner,@Path("repo") String repo);
            }
結合 RxJava 使用的 接口就定義好了,模型類不需要變動,接下來直接進行網絡請求
  1. 網絡請求

    使用 RxJava 的 Retrofit 可以直接在 主線程中編寫。

  2.  @Override
            protected void onCreate(Bundle savedInstanceState) {
                super.onCreate(savedInstanceState);
                setContentView(R.layout.activity_main);
    
                Retrofit retrofit=new Retrofit.Builder()
                        .baseUrl("https://api.github.com")
                        .addConverterFactory(GsonConverterFactory.create())//添加 json 轉換器
                        .addCallAdapterFactory(RxJavaCallAdapterFactory.create())//添加 RxJava 適配器
                        .build();
    
                GitHub gitHub=retrofit.create(GitHub.class);
                gitHub.RxContributors("square","retrofit")
                        .subscribeOn(Schedulers.io())
                        .observeOn(AndroidSchedulers.mainThread())
                        .subscribe(new Subscriber<List<Contributor>>() {
                            @Override
                            public void onCompleted() {
                                Log.i("TAG","onCompleted");
                            }
    
                            @Override
                            public void onError(Throwable e) {
    
                            }
    
                            @Override
                            public void onNext(List<Contributor> contributors) {
                               for (Contributor c:contributors){
                                   Log.i("TAG","RxJava-->"+c.getLogin()+"  "+c.getId()+"  "+c.getContributions());
                               }
                            }
                        });
            }

使用Retrofit和Okhttp實現網絡緩存。無網讀緩存,有網根據過期時間重新請求

okhttp 請求設置官方文檔

使用Retrofit和Okhttp實現網絡緩存。無網讀緩存,有網根據過期時間重新請求

OuNews 新聞:RetrofitManager類下的 initOkHttpClient 方法

okhttp3.X,retrofit:2.0.0-beta4適用

1 配置okhttp中的Cache

 OkHttpClient okHttpClient;
        File cacheFile = new File(DemoActivity.this.getCacheDir(), "[緩存目錄]");
        Cache cache = new Cache(cacheFile, 1024 * 1024 * 10); //100Mb
        okHttpClient = new OkHttpClient.Builder()
             .cache(cache)
             .build();

<pre name="code" class="java">     // 完成緩存
        public class MyOkHttpClient {

            //設緩存有效期爲兩天
            protected static final long CACHE_STALE_SEC = 60 * 60 * 24 * 2;
            //查詢緩存的Cache-Control設置,爲if-only-cache時只查詢緩存而不會請求服務器,max-stale可以配合設置緩存失效時間
            protected static final String CACHE_CONTROL_CACHE = "only-if-cached, max-stale=" + CACHE_STALE_SEC;
            //查詢網絡的Cache-Control設置,頭部Cache-Control設爲max-age=0時則不會使用緩存而請求服務器
            protected static final String CACHE_CONTROL_NETWORK = "max-age=0";

            private String TAG = "MyOkHttpClient";

            private OkHttpClient okHttpClient;
            private static MyOkHttpClient myOkHttpClient;

            public static MyOkHttpClient getMyOkHttpClient() {
                if (myOkHttpClient == null) {
                    myOkHttpClient = new MyOkHttpClient();

                }

                return myOkHttpClient;
            }

            /**
             * 初始化
             *
             * @param mContext
             */
            public void init(final Context mContext) {

                if (okHttpClient != null) {
                    return;
                }


                Log.e(TAG, "初始化okHttpClient");
                // 因爲BaseUrl不同所以這裏Retrofit不爲靜態,但是OkHttpClient配置是一樣的,靜態創建一次即可
                File cacheFile = new File(
                        mContext.getCacheDir(),
                        "HttpCache"
                ); // 指定緩存路徑
                Cache cache = new Cache(cacheFile, 1024 * 1024 * 100); // 指定緩存大小100Mb
                // 雲端響應頭攔截器,用來配置緩存策略
                Interceptor rewriteCacheControlInterceptor = new Interceptor() {
                    @Override
                    public Response intercept(Chain chain) throws IOException {
                        Request request = chain.request();
                        if (!NetUtil.isConnected(mContext)) {
                            request = request.newBuilder()
                                    .cacheControl(CacheControl.FORCE_CACHE).build();
                            Log.e(TAG, "no network");
                        }
                        Response originalResponse = chain.proceed(request);
                        if (NetUtil.isConnected(mContext)) {
                            //有網的時候讀接口上的@Headers裏的配置,你可以在這裏進行統一的設置
                            String cacheControl = request.cacheControl().toString();
                            return originalResponse.newBuilder()
                                    .header("Cache-Control", cacheControl)
                                    .removeHeader("Pragma").build();
                        } else {
                            return originalResponse.newBuilder().header("Cache-Control",
                                    "public, only-if-cached," + CACHE_STALE_SEC)
                                    .removeHeader("Pragma").build();
                        }
                    }
                };
                okHttpClient = new OkHttpClient.Builder().cache(cache)
                        .addNetworkInterceptor(rewriteCacheControlInterceptor)
                        .addInterceptor(rewriteCacheControlInterceptor)
                        .connectTimeout(30, TimeUnit.SECONDS).build();

            }

            public OkHttpClient getOkHttpClient() {
                return okHttpClient;
            }

            /**
             * 根據網絡狀況獲取緩存的策略
             *
             * @return
             */
            @NonNull
            public static String getCacheControl(Context mContext) {
                return NetUtil.isConnected(mContext) ? CACHE_CONTROL_NETWORK : CACHE_CONTROL_CACHE;
            }

        }

2 配置請求頭中的cache-control

  //使用 RxJava 的方法,返回一個 Observable
        @Headers("Cache-Control: public, max-age=3600")
        @GET("/repos/{owner}/{repo}/contributors")
        Observable<List<Contributor>> RxContributors(@Path("owner") String owner,@Path("repo") String repo);

3 配置請求 Retrofit 設置不同的底層網絡庫

 Retrofit retrofit = new Retrofit.Builder()
                .client(okHttpClient)
                .baseUrl("https://api.github.com")
                .addConverterFactory(GsonConverterFactory.create())//添加 json 轉換器
                .addCallAdapterFactory(RxJavaCallAdapterFactory.create())//添加 RxJava 適配器
                .build();

4 開啓打印連接日誌

    /**
         * 開啓打印連接日誌
         * @return
         */
        private HttpLoggingInterceptor getHttpLoggingInterceptor() {
            boolean isDebug= false;//是否開啓
            HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();

            if(isDebug)
            {
                interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
            }

            //開啓打印連接日誌
            return interceptor;
        }

        OkHttpClient.Builder().addInterceptor(getHttpLoggingInterceptor())

        eg:

        okHttpClient = new OkHttpClient.Builder()
                .addInterceptor(getHttpLoggingInterceptor())
                .cache(cache)
                .addNetworkInterceptor(rewriteCacheControlInterceptor)
                .addInterceptor(rewriteCacheControlInterceptor)
                .connectTimeout(30, TimeUnit.SECONDS).build();

Retrofit 如何使用Observable.from發出多條連接的?

以自己的開源項目 MeiZiNews 中的 ` MzituImgListModel 爲栗子,大概是這3步:

  1. 先向網站請求參數鏈表
  2. 使用 flatMap,它可以返回 Observable,所以在 flatMap 裏使用 Observable.from
  3. 再使用 flatMap,在它裏面發出單次網絡請求連接的 Observable,這時在它返回的就是單次請求的結果了
  4.  /**
         * Retrofit 使用Observable.from發出多條連接
         * @param context
         * @param listener
         * @return
         */
        @Override
        public Subscription loadWeb(final Context context, final OnModelListener<ImgItem> listener) {
    
            MyStringRetrofit.getMyStringRetrofit().init(context, MeiZiApi.MZITU_API);
            final MzituApi mzituApi = MyStringRetrofit.getMyStringRetrofit().getCreate(MzituApi.class);
    
            Observable observable = mzituApi.RxImgList(
                    MyOkHttpClient.getCacheControl(context),
                    imgId,
                    page
            ).flatMap(
                    new Func1<String, Observable<Integer>>() {
    
                        @Override
                        public Observable<Integer> call(String s) {
                            // 第一次請求先請求頁數,從而獲得要發的鏈接數
    
                            Elements mElements = JsoupUtil.getMzituImgPage(s);
                            Elements tempElements = mElements.select("span");
                            String size = tempElements.get(tempElements.size() - 2).text();
                            if (TextUtils.isEmpty(size)) {
                                return null;
                            }
    
                            ArrayList<Integer> numList = new ArrayList<Integer>();
    
                            for (int i = 1; i <= Integer.parseInt(size); i++) {
                                numList.add(i);
                            }
                            return Observable.from(numList);
                        }
                    }
            ).flatMap(
                    new Func1<Integer, Observable<String>>() {
                        @Override
                        public Observable<String> call(Integer integer) {
                        //   發出單次網絡請求連接
                            return mzituApi.RxImgList(
                                    MyOkHttpClient.getCacheControl(context),
                                    imgId,
                                    integer + ""
                            );
                        }
                    }
            ).map(
                    new Func1<String, ImgItem>() {
                        @Override
                        public ImgItem call(String s) {
                        //   處理每次請問的結果
    
                            ImgItem img = new ImgItem();
                            Elements mElements = JsoupUtil.getMzituImgItem(s);
                            Element tempElement = mElements.first();
    
                            img.setImgUrl(tempElement.select("img").attr("src"));
                            img.setUrl(tempElement.select("img").attr("src"));
                            img.setStory_title(tempElement.select("img").attr("alt"));
                            return img;
                        }
                    }
            );
    
            return RxJavaUtil.rxIoAndMain(
                    observable,
                    new Subscriber<ImgItem>() {
    
                        @Override
                        public void onCompleted() {
                            listener.onCompleted();
                        }
    
                        @Override
                        public void onError(Throwable e) {
                            listener.onError(e.toString());
                            DebugUtil.debugLogErr(e, e.toString());
                        }
    
                        @Override
                        public void onNext(ImgItem imgItem) {
                            listener.onSuccess(imgItem);
                        }
                    }
            );
        }

問題集


其他筆記

@Path:路徑

@Query:查詢條件,如:

xx=yy

如果有多個查詢條件可以使用: @QueryMap ;


最後,推薦下自己的開源項目:

有妹子的日報:https://github.com/qq137712630/MeiZiNews

APK下載:http://www.wandoujia.com/apps/com.ms.meizinewsapplication

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章