Retrofit入门

导包

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对象,表示不同的api
GitHubAPI 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);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值