Retrofit基础入门

来源:互联网 发布:ups 寿命 知乎 编辑:程序博客网 时间:2024/06/18 10:29
一、 简介

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>() {@Overridepublic void onResponse(Call<BookSearchResponse> call,        Response<BookSearchResponse> response) {asyncText.setText("异步请求结果: " + response.body().books.get(0).altTitle);}@Overridepublic 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相关资料。由于本人能力有限,有错误或者表述不准确的地方还望多多留言指正。
















原创粉丝点击