Retrofit入门

来源:互联网 发布:mysql left join 优化 编辑:程序博客网 时间:2024/06/06 02:09

导包

module:    //retrofitGson转换,由于retrofit是基于okhttp所以,还需要添加okhttp的依赖    compile 'com.squareup.retrofit2:retrofit:2.1.0'    compile 'com.squareup.retrofit2:converter-gson:2.1.0'    compile 'com.squareup.okhttp3:okhttp:3.4.1'

基本使用+Gson转换

api:https://api.github.com/users/baiiu
Json解析的model类,这里没有写出get set方法:public class User {    private String login;    private int id;    private String avatar_url;    private String gravatar_id;    private String url;    private String html_url;    private String followers_url;    private String following_url;    private String gists_url;    private String starred_url;    private String subscriptions_url;    private String organizations_url;    private String repos_url;    private String events_url;    private String received_events_url;    private String type;    private boolean site_admin;    private String name;    private Object company;    private String blog;    private String location;    private Object email;    private boolean hireable;    private String bio;    private int public_repos;    private int public_gists;    private int followers;    private int following;    private String created_at;    private String updated_at;}
定义接口public interface GitHubAPI {    //get请求,请求接口为 baseurl(在代码中定义)+user/{user}({user}为在请求时传入的字符串)    @GET("users/{user}")    Call<User> userInfo(@Path("user") String user);}
使用方法//以okhttp为client对象OkHttpClient client = new OkHttpClient();//创建retrofit对象        Retrofit retrofit = new Retrofit.Builder()                //设置OKHttpClient                .client(client)                //设置baseUrl,注意,baseUrl必须以后缀"/"结尾,否则报错                .baseUrl("https://api.github.com/")                //添加Gson转换器,将接口返回的数据转换为jsonmodel对象                .addConverterFactory(GsonConverterFactory.create())                .build();//API对象,表示不同的apiGitHubAPI gitHubAPI = retrofit.create(GitHubAPI.class);//创建call对象,表示请求<User> userCall = gitHubAPI.userInfo("baiiu");//异步请求,也就是在不同线程        userCall.enqueue(new Callback<User>() {            @Override            public void onResponse(Call<User> call, Response<User> response) {                User body = response.body();                Log.v("meee", getClass() + ":\n" + "body:" + body.toString());                //但callback是在主线程中的                Log.v("meee",getClass()+":\n"+"线程:"+Thread.currentThread().getName());            }            @Override            public void onFailure(Call<User> call, Throwable t) {                if (call.isCanceled()) {                    Log.v("meee",getClass()+":\n"+"请求被终止");                } else {                    Log.v("meee",getClass()+":\n"+""+t.getMessage());                }            }        });//同步请求,与发起请求在同一个线程        //因为每一个call对象只能被使用一次,而userCall在上面已经被使用过了,所以克隆一个请求        Call<User> userCallClone = userCall.clone();        Response<User> response = userCallClone.execute();//需要处理io异常        response.body().toString();//因为在主线程调用,所以会报错

不使用Gson转换

public interface GitHubAPI {//直接返回okhttp的ResponseBody       @GET    Call<ResponseBody> raw(@Url String url);}

完整网址的请求

因为总有api是不同的,有时候为其生成独立api很麻烦,所以我们可以直接传入全路径网址来进行请求api接口中添加一个传入全路径url的接口方法public interface GitHubAPI {    /*https://api.github.com/users/baiiu*/    @GET("users/{user}")    Call<User> userInfo(@Path("user") String user);    //该方法会跳过BasrUrl    @GET    Call<User> url(@Url String url);}//在请求时传入完整的网址        Call<User> url = gitHubAPI.url("https://api.github.com/users/baiiu");        url.enqueue(new Callback<User>() {            @Override            public void onResponse(Call<User> call, Response<User> response) {                User body = response.body();                Log.v("meee", getClass() + ":\n" + "url:" + body.toString());            }            @Override            public void onFailure(Call<User> call, Throwable t) {            }        });

Post请求

public interface MallAPI {    //使用@Field注解时一定要添加@FormUrlEncoded注解,否则报错    @FormUrlEncoded    //post不能和@Url注解同时使用    @POST("login")    Call<ResponseBody> login(@Field("username")String username, @Field("pwd") String pwd);}
        MallAPI mallAPI = retrofit2.create(MallAPI.class);        Call<ResponseBody> loginCall = mallAPI.login("123","123456");        loginCall.enqueue(new Callback<ResponseBody>() {            @Override            public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {                ResponseBody body = response.body();                try {                    Log.v("meee",getClass()+":\n"+""+body.string());                } catch (IOException e) {                }            }            @Override            public void onFailure(Call<ResponseBody> call, Throwable t) {            }        });
也可以使用属性map来传递参数public interface MallAPI {    @FormUrlEncoded    @POST("login")    Call<ResponseBody> login(@FieldMap Map<String,String>map);}//创建call对象Call<ResponseBody> loginCall = mallAPI.login(map);

添加请求头

public interface GitHubAPI {    //静态设置请求头,同一个请求的同一个请求头在不同地方的设置不会被覆盖,而是会被全部添加进请求头中    @Headers({            "Accept: application/vnd.github.v3.full+json",            "User-Agent: Retrofit-Sample-App"    })    @GET    Call<User> urlWithHeaderStatic(@Url String url);    //动态设置请求头    Call<User> urlWithHeaderDynamic(@Url String url,@Header("Authorization") String authorization);}

Interceptors的使用

导包,因为retrofit底层是使用okhttp,所以使用okhttp的Interceptors    //okhttp拦截请求日志    compile 'com.squareup.okhttp3:logging-interceptor:3.4.1'
    //创建Interceptor    public final class UserAgentInterceptor implements Interceptor {        private static final String USER_AGENT_HEADER_NAME = "User-Agent";        private final String userAgentHeaderValue;        public UserAgentInterceptor(String userAgentHeaderValue) {            this.userAgentHeaderValue = userAgentHeaderValue;        }        @Override public okhttp3.Response intercept(Chain chain) throws IOException {            final Request originalRequest = chain.request();            final Request requestWithUserAgent = originalRequest.newBuilder()                    //移除先前默认的UA                    .removeHeader(USER_AGENT_HEADER_NAME)                    //设置UA                    .addHeader(USER_AGENT_HEADER_NAME, userAgentHeaderValue)                    .build();            return chain.proceed(requestWithUserAgent);        }    }
        //在创建client时,传入创建的Interceptor        OkHttpClient okHttpClient = new OkHttpClient.Builder()                //添加UA                .addInterceptor(new UserAgentInterceptor("header"))                //失败重连                .retryOnConnectionFailure(true)                //time out                .readTimeout(10, TimeUnit.SECONDS)                .connectTimeout(10, TimeUnit.SECONDS)                .build();

混淆规则

/** -dontwarn retrofit2.*** -keep class retrofit2.** { *; }* -keepattributes Signature* -keepattributes Exceptions* */

注解详解

http类:

注解

其中http注解可以代替以上7中的http标准注解public interface BlogService {    /**     * method 表示请求的方法,区分大小写     * path表示路径     * hasBody表示是否有请求体     */    @HTTP(method = "GET", path = "blog/{id}", hasBody = false)    Call<ResponseBody> getBlog(@Path("id") int id);}

标记类

这里写图片描述

    public interface BlogService {        /**         * {@link FormUrlEncoded} 表明是一个表单格式的请求(Content-Type:application/x-www-form-urlencoded)         * <code>Field("username")</code> 表示将后面的 <code>String name</code> 中name的取值作为 username 的值         */        @POST("/form")        @FormUrlEncoded        Call<ResponseBody> testFormUrlEncoded1(@Field("username") String name, @Field("age") int age);        /**         * Map的key作为表单的键         */        @POST("/form")        @FormUrlEncoded        Call<ResponseBody> testFormUrlEncoded2(@FieldMap Map<String, Object> map);        /**         * {@link Part} 后面支持三种类型,{@link RequestBody}、{@link okhttp3.MultipartBody.Part} 、任意类型         * 除 {@link okhttp3.MultipartBody.Part} 以外,其它类型都必须带上表单字段({@link okhttp3.MultipartBody.Part} 中已经包含了表单字段的信息),         */        @POST("/form")        @Multipart        Call<ResponseBody> testFileUpload1(@Part("name") RequestBody name, @Part("age") RequestBody age, @Part MultipartBody.Part file);        /**         * PartMap 注解支持一个Map作为参数,支持 {@link RequestBody } 类型,         * 如果有其它的类型,会被{@link retrofit2.Converter}转换,如后面会介绍的 使用{@link com.google.gson.Gson} 的 {@link retrofit2.converter.gson.GsonRequestBodyConverter}         * 所以{@link MultipartBody.Part} 就不适用了,所以文件只能用<b> @Part MultipartBody.Part </b>         */        @POST("/form")        @Multipart        Call<ResponseBody> testFileUpload2(@PartMap Map<String, RequestBody> args, @Part MultipartBody.Part file);        @POST("/form")        @Multipart        Call<ResponseBody> testFileUpload3(@PartMap Map<String, RequestBody> args);    }    public static void main(String[] args) {        Retrofit retrofit = new Retrofit.Builder()                .baseUrl("http://localhost:4567/")                .build();        BlogService service = retrofit.create(BlogService.class);        // 演示 @FormUrlEncoded 和 @Field        Call<ResponseBody> call1 = service.testFormUrlEncoded1("怪盗kidou", 24);        ResponseBodyPrinter.printResponseBody(call1);        //===================================================        // 演示 @FormUrlEncoded 和 @FieldMap        // 实现的效果与上面想同        Map<String, Object> map = new HashMap<>();        map.put("username", "怪盗kidou");        map.put("age", 24);        Call<ResponseBody> call2 = service.testFormUrlEncoded2(map);        ResponseBodyPrinter.printResponseBody(call2);        //===================================================        MediaType textType = MediaType.parse("text/plain");        RequestBody name = RequestBody.create(textType, "怪盗kidou");        RequestBody age = RequestBody.create(textType, "24");        RequestBody file = RequestBody.create(MediaType.parse("application/octet-stream"), "这里是模拟文件的内容");        // 演示 @Multipart 和 @Part        MultipartBody.Part filePart = MultipartBody.Part.createFormData("file", "test.txt", file);        Call<ResponseBody> call3 = service.testFileUpload1(name, age, filePart);        ResponseBodyPrinter.printResponseBody(call3);        //===================================================        // 演示 @Multipart 和 @PartMap        // 实现和上面同样的效果        Map<String, RequestBody> fileUpload2Args = new HashMap<>();        fileUpload2Args.put("name", name);        fileUpload2Args.put("age", age);        //这里并不会被当成文件,因为没有文件名(包含在Content-Disposition请求头中),但上面的 filePart 有        //fileUpload2Args.put("file", file);        Call<ResponseBody> call4 = service.testFileUpload2(fileUpload2Args, filePart); //单独处理文件        ResponseBodyPrinter.printResponseBody(call4);        //===================================================        // 还有一种比较hack的方式可以实现文件上传,        // 上面说过被当成文件上传的必要条件就是 Content-Disposition 请求头中必须要有 filename="xxx" 才会被当成文件        // 所有我们在写文件名的时候可以拼把 filename="XXX" 也拼接上去,        // 即文件名变成  表单键名"; filename="文件名  (两端的引号会自动加,所以这里不加)也可以实现,但是不推荐方式        Map<String, RequestBody> fileUpload3Args = new HashMap<>();        fileUpload3Args.put("name",name);        fileUpload3Args.put("age",age);        fileUpload3Args.put("file\"; filename=\"test.txt",file);        Call<ResponseBody> testFileUpload3 = service.testFileUpload3(fileUpload3Args);        ResponseBodyPrinter.printResponseBody(testFileUpload3);    }

参数类

这里写图片描述

{占位符}和PATH尽量只用在URL的path部分,url中的参数使用Query和QueryMap 代替,保证接口定义的简洁Query、Field和Part这三者都支持数组和实现了Iterable接口的类型,如ListSet等,方便向后台传递数组。demo:Call<ResponseBody> foo(@Query("ids[]") List<Integer> ids);//结果:ids[]=0&ids[]=1&ids[]=2

RxJava和CallAdapter

Converter是对于Call<T>中T的转换,而CallAdapter则可以对Call转换;

导包

compile 'com.squareup.retrofit2:adapter-rxjava2:2.3.0'
public interface MallAPI {    //将api接口中返回的类型从Call<T>改成Observable<T>,注意不要导成database的包    @FormUrlEncoded    @POST("login")    Observable<ResponseBody> login2(@FieldMap Map<String, String> map);}
        Retrofit retrofit3 = new Retrofit.Builder()                .client(client)                .baseUrl("url")                .addConverterFactory(GsonConverterFactory.create())                //添加calladapter支持                .addCallAdapterFactory(RxJava2CallAdapterFactory.create())                .build();        MallAPI mallAPI2 = retrofit3.create(MallAPI.class);        //RxJava走起        Observable<ResponseBody> responseBodyObservable = mallAPI2.login2(map);        responseBodyObservable                .subscribeOn(Schedulers.newThread())                .subscribe(new Consumer<ResponseBody>() {                    @Override                    public void accept(ResponseBody responseBody) throws Exception {                        Log.v("meee",getClass()+":\n"+"responseBody:"+responseBody.string());                    }                });

Retrofit的Url组合规则

公式:BaseUrl + URL有关的注解中的内容 = 组合结果"http://localhost:4567/path/to/other/" + "/post" = "http://localhost:4567/post""http://localhost:4567/path/to/other/" + "post" = "http://localhost:4567/path/to/other/post""http://localhost:4567/post"  + "https://github.com/ikidou"="https://github.com/ikidou"

小技巧

在取得结果的地方可以添加public interface MallAPI {    @FormUrlEncoded    @POST("login")    Observable<User> login2(@FieldMap Map<String, String> map);}可以改成public interface MallAPI {    @FormUrlEncoded    @POST("login")    Observable<Response<User>> login2(@FieldMap Map<String, String> map);}