Retrofit基础入门

一、 简介

Retrofit是Square公司开发的一款针对Android网络请求的框架,Retrofit2底层基于OkHttp实现的,OkHttp现在已经得到Google官方认可,大量的app都采用OkHttp做网络请求,其源码详见OkHttp Github。

二、如何使用

首先需要在build.gradle文件中引入需要的第三包,配置如下:

    compile 'com.squareup.retrofit2:retrofit:2.3.0'
    compile 'com.squareup.retrofit2:converter-scalars:2.3.0'
    compile 'com.squareup.retrofit2:adapter-rxjava:2.3.0'
    compile 'com.squareup.retrofit2:converter-gson:2.3.0'
    compile 'io.reactivex:rxjava:1.3.0'
    compile 'io.reactivex:rxandroid:1.2.1'

导入第三方包,接下来是使用retrofit进行网络请求,接下来我会使用我项目内的代码进行说明

Post请求:

Post请求需要把请求参数放置在请求体中,而非拼接在url后面

/**
     * 普通写法
     */
    //附近的商品
    @POST("open/market/getNearbyGoods.do")
    Observable<String> getNearbyGoods(@QueryMap Map<String, String> map);
注释写的比较清晰,所有我这里说下,它的缺点,每个接口都需要写一个类似的方法,比较繁琐,重新造轮子。下面是优化版:

/**
     * post方式 map传参
     * @param url
     * @param maps
     * @return
     */
    @POST("{url}")
    Observable<String> executePost(
            @Path(value = "url", encoded = true) String url,
            @QueryMap Map<String, String> maps);


 /**
     * post方式 json传参
     * @param url
     * @param jsonStr
     * @return
     */
    @POST("{url}")
    Observable<String> json(
            @Path(value = "url", encoded = true) String url,
            @Body RequestBody jsonStr);


上传文件

/**
     * post方式 上传单个文件
     * @param url
     * @param description 文件描述
     * @param file
     * @return
     */
    @Multipart
    @POST("{url}")
    Observable<String> upLoadFile(
            @Path(value = "url", encoded = true) String url,
            @Part("description") RequestBody description,
            @Part MultipartBody.Part file);

    /**
     * post方式 上传多个文件
     * @param url
     * @param maps
     * @return
     */
    @POST("{url}")
    Observable<String> upLoadFiles(
            @Path(value = "url", encoded = true) String url,
            @PartMap() Map<String, MultipartBody.Part> maps);
图文上传
 /**
     * post方式  图文同时上传
     * @param url
     * @param partMap
     * @param file
     * @return
     */
    @Multipart
    @POST
    Observable<String> uploadFileWithPartMap(
            @Url() String url,
            @QueryMap() Map<String, String> partMap,
            @Part("file") MultipartBody.Part file);
Get请求:

get请求跟post请求差不多,这里只写一些

 /**
     * get方式 map传参
     * @param url
     * @param maps
     * @return
     */
    @GET("{url}")
    Observable<String> executeGet(
            @Path(value = "url", encoded = true) String url,
            @QueryMap Map<String, String> maps
    );

/**
     * get方式 下载文件
     * @param fileUrl
     * @return
     */
    @Streaming
    @GET
    Observable<ResponseBody> downloadFile(@Url String fileUrl);


1.创建业务请求接口,具体代码如下:

这里需要稍作说明,@GET注解就表示get请求,@Query表示请求参数,将会以key=value的方式拼接在url后面

public interface BlueService {
   @GET("book/search")
   Call<BookSearchResponse> getSearchBooks(@Query("q") String name, 
        @Query("tag") String tag, @Query("start") int start, 
        @Query("count") int count);
}

2.创建一个Retrofit的示例,并完成相应的配置

Retrofit retrofit = new Retrofit.Builder()
   .baseUrl("https://api.douban.com/v2/")
   .addConverterFactory(GsonConverterFactory.create())
   .build();
BlueService service = retrofit.create(BlueService.class);
这里的baseUrl就是网络请求URL相对固定的地址,一般包括请求协议(如Http)、域名或IP地址、端口号等,当然还会有很多其他的配置,下文会详细介绍。还有addConverterFactory方法表示需要用什么转换器来解析返回值,GsonConverterFactory.create()表示调用Gson库来解析json返回值,具体的下文还会做详细介绍。

3. 调用请求方法,并得到Call实例

Call<BookSearchResponse> call = mBlueService.getSearchBooks("小王子", "", 0, 3);
Call其实在Retrofit中就是行使网络请求并处理返回值的类,调用的时候会把需要拼接的参数传递进去

4. 使用Call实例完成同步或异步请求

4.1同步请求

BookSearchResponse response = call.execute().body();
这里需要注意的是网络请求一定要在子线程中完成,不能直接在UI线程执行,不然会crash

4.2异步请求

call.enqueue(new Callback<BookSearchResponse>() {
@Override
public void onResponse(Call<BookSearchResponse> call,        Response<BookSearchResponse> response) {
asyncText.setText("异步请求结果: " + response.body().books.get(0).altTitle);
}
@Override
public void onFailure(Call<BookSearchResponse> call, Throwable t) {

}
});
上面是简单的使用

我项目中使用的是rxjava+retrofit框架,目前是比较大众的写法,网上有许多框架的封装。这里我把我自己的代码直接上

/**
 * RetrofitClient
 */
public class RetrofitClient {
    private static final int DEFAULT_TIMEOUT = 20;
    private Retrofit retrofit;
    private Cache cache = null;
    private File httpCacheDirectory;
    private static OkHttpClient okHttpClient;

    public static RetrofitClient getInstance(Context context) {
        return new RetrofitClient(context);
    }

    public RetrofitClient(Context context) {
        if (httpCacheDirectory == null) {
            httpCacheDirectory = new File(context.getCacheDir(), "tamic_cache");
        }

        try {
            if (cache == null) {
                cache = new Cache(httpCacheDirectory, 10 * 1024 * 1024);
            }
        } catch (Exception e) {
            Log.e("OKHttp", "Could not create http cache", e);
        }
        okHttpClient = new OkHttpClient.Builder()
                //设置可以从传入的HTTP响应接受cookie并向传出HTTP请求提供cookie的处理程序。
//                .cookieJar(new NovateCookieManger(context))
                //设置响应缓存用于读取和写入缓存响应。
                .cache(cache)
                //链接超时
                .connectTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS)
                .readTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS)
                //新链接默认超时时间
                .writeTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS)
                //设置用于回收HTTP和HTTPS连接的连接池。
                .connectionPool(new ConnectionPool(8, 15, TimeUnit.SECONDS))
                // 这里你可以根据自己的机型设置同时连接的个数和时间,我这里8个,和每个保持时间为10s
                .build();

        retrofit = new Retrofit.Builder()
                .client(okHttpClient)
                .baseUrl(BaseRetrofit.API)
                //增加返回值为String的支持
                .addConverterFactory(ScalarsConverterFactory.create())
                //增加返回值为Gson的支持(以实体类返回)
                .addConverterFactory(GsonConverterFactory.create())
                //增加返回值为Oservable<T>的支持
                .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
                .build();
    }

    /**
     * post方式的访问api
     *
     * @param url
     * @param map
     * @param subscribe
     * @return
     */
    public Subscription initMap(String url, Map<String, String> map, Subscriber<String> subscribe) {
        BaseRetrofit service = retrofit.create(BaseRetrofit.class);
        Subscription subscription = service.executePost(url, map)
                .compose(schedulersTransformer())
                .subscribe(subscribe);
        return subscription;
    }

    /**
     * post方式  单个文件上传
     *
     * @param url
     * @param file
     * @param subscribe
     * @return
     */
    public Subscription upLoadFile(String url, File file, Subscriber<String> subscribe) {
        // 创建 RequestBody,用于封装构建RequestBody
        RequestBody requestFile = RequestBody.create(MediaType.parse("multipart/form-data"), file);

        // MultipartBody.Part  和后端约定好Key,这里的partName是用image
        MultipartBody.Part body = MultipartBody.Part.createFormData("image", file.getName(), requestFile);

        // 添加描述
        String descriptionString = "hello, 这是文件描述";
        RequestBody description = RequestBody.create(MediaType.parse("multipart/form-data"), descriptionString);
        BaseRetrofit service = retrofit.create(BaseRetrofit.class);
        Subscription subscription = service.upLoadFile(url, description, body)
                .compose(schedulersTransformer())
                .subscribe(subscribe);
        return subscription;
    }

    /**
     * post方式  多个文件上传
     *
     * @param url
     * @param files
     * @param subscribe
     * @return
     */
    @RequiresApi(api = Build.VERSION_CODES.KITKAT)
    public Subscription upLoadFiles(String url, Map<String, File> files, Subscriber<String> subscribe) {
        Map<String, MultipartBody.Part> map = new ArrayMap<>();
        for (int i = 0; i < files.size(); i++) {
            // 创建 RequestBody,用于封装构建RequestBody
            RequestBody requestFile = RequestBody.create(MediaType.parse("multipart/form-data"), files.get(i));

            // MultipartBody.Part  和后端约定好Key,这里的partName是用image
            MultipartBody.Part body = MultipartBody.Part.createFormData("image", files.get(i).getName(), requestFile);
            map.put(i + "", body);
        }

        // 添加描述
        BaseRetrofit service = retrofit.create(BaseRetrofit.class);
        Subscription subscription = service.upLoadFiles(url, map)
                .compose(schedulersTransformer())
                .subscribe(subscribe);
        return subscription;
    }

    /**
     * get方式 下载文件
     * @param url 文件url
     * @param subscribe
     * @return
     */
    public Subscription downLoadFile(String url, Subscriber<ResponseBody> subscribe) {
        // 添加描述
        BaseRetrofit service = retrofit.create(BaseRetrofit.class);
        Subscription subscription = service.downloadFile(url)
                .subscribeOn(Schedulers.io())
                .subscribe(subscribe);
        return subscription;
    }

    Observable.Transformer schedulersTransformer() {
        return new Observable.Transformer() {


            @Override
            public Object call(Object observable) {
                return ((Observable) observable).subscribeOn(Schedulers.io())
                        .unsubscribeOn(Schedulers.io())
                        .observeOn(AndroidSchedulers.mainThread());
            }

           /* @Override
            public Observable call(Observable observable) {
                return observable.subscribeOn(Schedulers.io())
                        .unsubscribeOn(Schedulers.io())
                        .observeOn(AndroidSchedulers.mainThread());
            }*/
        };
    }
}
网络请求错误进行自定义判断

public class ExceptionHandle {

    private static final int UNAUTHORIZED = 401;
    private static final int FORBIDDEN = 403;
    private static final int NOT_FOUND = 404;
    private static final int REQUEST_TIMEOUT = 408;
    private static final int INTERNAL_SERVER_ERROR = 500;
    private static final int BAD_GATEWAY = 502;
    private static final int SERVICE_UNAVAILABLE = 503;
    private static final int GATEWAY_TIMEOUT = 504;

    public static ResponeThrowable handleException(Throwable e) {
        ResponeThrowable ex;
        if (e instanceof HttpException) {
            HttpException httpException = (HttpException) e;
            ex = new ResponeThrowable(e, ERROR.HTTP_ERROR);
            switch (httpException.code()) {
                case UNAUTHORIZED:
                case FORBIDDEN:
                case NOT_FOUND:
                case REQUEST_TIMEOUT:
                case GATEWAY_TIMEOUT:
                case INTERNAL_SERVER_ERROR:
                case BAD_GATEWAY:
                case SERVICE_UNAVAILABLE:
                default:
                    ex.message = "网络错误";
                    break;
            }
            return ex;
        } else if (e instanceof ServerException) {
            ServerException resultException = (ServerException) e;
            ex = new ResponeThrowable(resultException, resultException.code);
            ex.message = resultException.message;
            return ex;
        } else if (e instanceof JsonParseException || e instanceof JSONException || e instanceof ParseException) {
            ex = new ResponeThrowable(e, ERROR.PARSE_ERROR);
            ex.message = "解析错误";
            return ex;
        } else if (e instanceof ConnectException) {
            ex = new ResponeThrowable(e, ERROR.NETWORD_ERROR);
            ex.message = "连接失败";
            return ex;
        } else if (e instanceof javax.net.ssl.SSLHandshakeException) {
            ex = new ResponeThrowable(e, ERROR.SSL_ERROR);
            ex.message = "证书验证失败";
            return ex;
        } else if (e instanceof ConnectTimeoutException) {
            ex = new ResponeThrowable(e, ERROR.TIMEOUT_ERROR);
            ex.message = "连接超时";
            return ex;
        } else if (e instanceof java.net.SocketTimeoutException) {
            ex = new ResponeThrowable(e, ERROR.TIMEOUT_ERROR);
            ex.message = "连接超时";
            return ex;
        } else {
            if (!NetworkUtil.isNetworkAvailable(App.getInstance())) {
                ex = new ResponeThrowable(e, ERROR.UNKNOWN);
                ex.message = "无网络,请连接网络";
            }else{
                ex = new ResponeThrowable(e, ERROR.UNKNOWN);
                ex.message = "未知错误";
            }

            return ex;
        }
    }


    /**
     * 约定异常
     */
    class ERROR {
        /**
         * 未知错误
         */
        public static final int UNKNOWN = 1000;
        /**
         * 解析错误
         */
        public static final int PARSE_ERROR = 1001;
        /**
         * 网络错误
         */
        public static final int NETWORD_ERROR = 1002;
        /**
         * 协议出错
         */
        public static final int HTTP_ERROR = 1003;

        /**
         * 证书出错
         */
        public static final int SSL_ERROR = 1005;

        /**
         * 连接超时
         */
        public static final int TIMEOUT_ERROR = 1006;
    }

    public static class ResponeThrowable extends Exception {
        public int code;
        public String message;

        public ResponeThrowable(Throwable throwable, int code) {
            super(throwable);
            this.code = code;
        }

    }

    public class ServerException extends RuntimeException {
        public int code;
        public String message;
    }
}
public abstract class BaseSubscriber<T> extends Subscriber<T> {

    @Override
    public void onStart() {
        onReStart();
        super.onStart();
    }

    @Override
    public void onError(Throwable e) {
        if (e instanceof ExceptionHandle.ResponeThrowable) {
            onError((ExceptionHandle.ResponeThrowable) e);
        } else {
            onError(ExceptionHandle.handleException(e));
        }
    }

    @Override
    public void onNext(T t) {
        BaseResponse baseResponse = ResponseParserUtils.parseResponse((String) t, String.class, false);
        onNext(baseResponse);
    }

    public abstract void onNext(BaseResponse baseResponse);

    public abstract void onReStart();

    public abstract void onError(ExceptionHandle.ResponeThrowable e);

}
准备好了之后,进行网络请求方法:

 Subscription subscription = RetrofitClient.getInstance(this)
                .downLoadFile(downUrl, new Subscriber<ResponseBody>() {
                    @Override
                    public void onCompleted() {
                        stopService(intent);
                        CatalogActivity.downOk=true;
                    }

                    @Override
                    public void onError(Throwable e) {
                        LogUtils.i("DownNovelService", e.toString());
                        ToastUtils.showMessageLong(e.toString());
                        stopService(intent);
                        CatalogActivity.downOk=true;
                    }

                    @Override
                    public void onNext(ResponseBody responseBody) {
                        if (WriteFileManager.writeResponseBodyToDisk(responseBody, novelName + ".text")) {
                            ToastUtils.showMessageLong("下载成功");
                        } else {
                            ToastUtils.showMessageLong("下载失败");
                        }
                    }
                });
三、其他需要知道事项

网络请求看起来是那么完美,但是仔细看并进行试验时,你就会发现,怎么没有取消网络请求的方法。

retrofit取消请求:call.cancel();

rxjava取消订阅:Subscription.unsubscribe();

看具体情况使用。

这里我封装了一个rxjava+retrofit取消请求的类,直接上代码:

public interface RxActionManager<T> {

    void add(T tag, Subscription subscription);
    void remove(T tag);

    void cancel(T tag);

    void cancelAll();
}
/**
 * describe Retrofit+RxJava取消接口管理
 * authors liuyaolin
 * createTime 2017/6/6 16:31
 */

public class RxApiManager implements RxActionManager<Object> {

    private static RxApiManager sInstance = null;

    private ArrayMap<Object, Subscription> maps;

    public static RxApiManager get() {

        if (sInstance == null) {
            synchronized (RxApiManager.class) {
                if (sInstance == null) {
                    sInstance = new RxApiManager();
                }
            }
        }
        return sInstance;
    }

    private RxApiManager() {
        maps = new ArrayMap<>();
    }


    @Override
    public void add(Object tag, Subscription subscription) {
        maps.put(tag, subscription);
    }


    @Override
    public void remove(Object tag) {
        if (!maps.isEmpty()) {
            maps.remove(tag);
        }
    }

    public void removeAll() {
        if (!maps.isEmpty()) {
            maps.clear();
        }
    }


    @Override
    public void cancel(Object tag) {
        if (maps.isEmpty()) {
            return;
        }
        if (maps.get(tag) == null) {
            return;
        }
        if (!maps.get(tag).isUnsubscribed()) {
            maps.get(tag).unsubscribe();
            maps.remove(tag);
        }
    }

    @Override
    public void cancelAll() {
        if (maps.isEmpty()) {
            return;
        }
        Set<Object> keys = maps.keySet();
        for (Object apiKey : keys) {
            cancel(apiKey);
        }
    }
}
调用方法是:

先添加请求到Map集合内

 RxApiManager.get().add(this, subscription);

 RxApiManager.get().cancel(this);

四、结束语

关于Retrofit常用的方法基本上已经介绍完了,有些请求由于工作保密性的原因,所以就没有放出来,但是基本的方法和操作都是有的,仿照文中提到的代码就可以实现你想要的功能。参考了网络一些retrofit相关资料。由于本人能力有限,有错误或者表述不准确的地方还望多多留言指正。
















  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值