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()


    retrofit = new Retrofit.Builder()
            .client(MyOkHttpClient.getMyOkHttpClient().getOkHttpClient())//设置不同的底层网络库
            .baseUrl(strBaseUrl)
            .addConverterFactory(ScalarsConverterFactory.create())//添加 String类型[ Scalars (primitives, boxed, and String)] 转换器
            .addCallAdapterFactory(RxJavaCallAdapterFactory.create())//添加 RxJava 适配器
            .build();

返回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 类型:


        public interface GitHub {
        
            @GET("/repos/{owner}/{repo}/contributors")
            Call> contributors(@Path("owner") String owner,@Path("repo") String repo);
        
            //使用 RxJava 的方法,返回一个 Observable
            @GET("/repos/{owner}/{repo}/contributors")
            Observable> RxContributors(@Path("owner") String owner,@Path("repo") String repo);
        }

结合 RxJava 使用的 接口就定义好了,模型类不需要变动,接下来直接进行网络请求
  1. 网络请求

    使用 RxJava 的 Retrofit 可以直接在 主线程中编写。


        @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>() {
                        @Override
                        public void onCompleted() {
                            Log.i("TAG","onCompleted");
                        }
        
                        @Override
                        public void onError(Throwable e) {
        
                        }
        
                        @Override
                        public void onNext(List 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();
        
栗子:

        // 完成缓存
        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> 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,这时在它返回的就是单次请求的结果了

    /**
     * Retrofit 使用Observable.from发出多条连接
     * @param context
     * @param listener
     * @return
     */
    @Override
    public Subscription loadWeb(final Context context, final OnModelListener 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>() {

                    @Override
                    public Observable 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 numList = new ArrayList();

                        for (int i = 1; i <= Integer.parseInt(size); i++) {
                            numList.add(i);
                        }
                        return Observable.from(numList);
                    }
                }
        ).flatMap(
                new Func1>() {
                    @Override
                    public Observable call(Integer integer) {
                    //   发出单次网络请求连接
                        return mzituApi.RxImgList(
                                MyOkHttpClient.getCacheControl(context),
                                imgId,
                                integer + ""
                        );
                    }
                }
        ).map(
                new Func1() {
                    @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() {

                    @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);
                    }
                }
        );
    }


问题集

  • Retrofit error URL query string must not have replace block

其他笔记

@Path:路径

@Query:查询条件,如:

xx=yy

如果有多个查询条件可以使用: @QueryMap ;


最后,推荐下自己的开源项目:

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

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

它使用了多个开源框架,并在 ReadMe 中都有标明其框架的使用在哪个方面,遇到了什么奇怪的问题,中文的哦,喜欢的, Star 一下。


我的公众号,一个在修打印机的程序员

小成小店

你可能感兴趣的:(Android 网络请求:Retrofit 使用)