【Retrofit】Retrofit原理解析之使用篇

学而不思则罔,思而不学则殆


Retrofit系列文章

【Retrofit】Retrofit原理解析之使用篇
【Retrofit】Retrofit原理解析之原理篇
【Retrofit】Retrofit原理解析之注解详解篇
【Retrofit】Retrofit原理解析之设计模式总结篇

引言

最近在学习使用Retrofit框架进行网络请求,特整理一下使用总结记录下来!


Retrofit本质是根据OkHttp进行二次封装的网络请求框架。
在这里插入图片描述
接下来我们就来讲一下Retrofit具体使用
源码下载小技巧,由于国内网络原因,直接从Github上下载源码可能很慢,当然网络很好的不用考虑了。方法是从Github上克隆一份源码到Gitee上,再从Gitee上clone到本地就会非常快。

//Github
https://github.com/square/retrofit
//Gitee
https://gitee.com/zhangyuwxf/retrofit.git

准备工作,测试的服务端:
用于接收测试客户端发送的请求

//服务端
https://github.com/aJanefish/ZyServer

常见使用

普通GET请求

请求所有的信息

http://localhost:3434/retrofit/all.do

使用curl模拟请求数据,情况如下:

86183@DESKTOP-MKB5ERF MINGW64 ~/Desktop
$ curl http://localhost:3434/retrofit/all.do
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100  1571    0  1571    0     0  98187      0 --:--:-- --:--:-- --:--:-- 98187{"code":200,"msg":"OK","des":"all.do","data":[{"id":1,"date":"2020-11-15 10:39:13","author":"fish1","title":"retrofit1","address":"https://gitee.com/zhangyuwxf/retrofit.git","content":"http test1"},{"id":2,"date":"2020-11-15 10:39:13","author":"fish2","title":"retrofit2","address":"https://gitee.com/zhangyuwxf/retrofit.git","content":"http test2"},{"id":3,"date":"2020-11-15 10:39:13","author":"fish3","title":"retrofit3","address":"https://gitee.com/zhangyuwxf/retrofit.git","content":"http test3"},{"id":4,"date":"2020-11-15 10:39:13","author":"fish4","title":"retrofit4","address":"https://gitee.com/zhangyuwxf/retrofit.git","content":"http test4"},{"id":5,"date":"2020-11-15 10:39:13","author":"fish5","title":"retrofit5","address":"https://gitee.com/zhangyuwxf/retrofit.git","content":"http test5"},{"id":6,"date":"2020-11-15 10:39:13","author":"fish6","title":"retrofit6","address":"https://gitee.com/zhangyuwxf/retrofit.git","content":"http test6"},{"id":7,"date":"2020-11-15 10:39:13","author":"fish7","title":"retrofit7","address":"https://gitee.com/zhangyuwxf/retrofit.git","content":"http test7"},{"id":8,"date":"2020-11-15 10:39:13","author":"fish8","title":"retrofit8","address":"https://gitee.com/zhangyuwxf/retrofit.git","content":"http test8"},{"id":9,"date":"2020-11-15 10:39:13","author":"fish9","title":"retrofit9","address":"https://gitee.com/zhangyuwxf/retrofit.git","content":"http test9"},{"id":10,"date":"2020-11-15 10:39:13","author":"fish10","title":"retrofit10","address":"https://gitee.com/zhangyuwxf/retrofit.git","content":"http test10"}]}

使用Retrofit发送请求:
retrofit在使用的过程中,需要定义一个接口对象,我们首先演示一个最简单的get请求,接口如下所示:

    interface BlogService {
        @GET("retrofit/all.do")
        Call<Blog> getAll();
    }
    //实体类
    static class Blog {
        private String body;

        public Blog(String body) {
            this.body = body;
        }

        @Override
        public String toString() {
            return "Blog{" +
                    "body='" + body + '\'' +
                    '}';
        }
    }

定义了一个getAll的方法,通过@GET注解标识为get请求,@GET中所填写的value和baseUrl组成完整的路径,baseUrl在构造retrofit对象时给出。
如下构建普通的

    public static void main(String[] args) {
       //1.生成Retrofit对象,通过构建者模式
        Retrofit retrofit = new Retrofit.Builder()
                .baseUrl(HttpUrl.get("http://localhost:3434/"))
                //自定义了一个数据处理的工厂
                .addConverterFactory(DataAdapter.FACTORY)
                .build();
        //2.生成BlogService对象
        BlogService blogService = retrofit.create(BlogService.class);
        //3.获取Call对象
        Call<Blog> call = blogService.getAll();
        try {
            //4.同步请求也有异步请求
            Response<Blog> response = call.execute();
            //5.获取对象
            Blog blog = response.body();
            System.out.print(blog);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    static final class DataAdapter implements Converter<ResponseBody, Blog> {
        static final Converter.Factory FACTORY =
                new Converter.Factory() {
                    @Override
                    public @Nullable
                    Converter<ResponseBody, ?> responseBodyConverter(
                            Type type, Annotation[] annotations, Retrofit retrofit) {
                        if (type == Blog.class) return new DataAdapter();
                        return null;
                    }
                };

        @Override
        public Blog convert(ResponseBody responseBody) throws IOException {
            String body = responseBody.string();
            //返回的Json字符串没有做处理,直接塞进数据对象中
            // FIXME: 2020/11/16 数据处理
            return new Blog(body);
        }
    }

如上,先是通过构建者模式生成Retrofit对象,该对象携带了处理对象,但是此时我们只是设置最简单的BaseUrl和数据处理的Converter.Factory,他的实现很简单,发现使我们需要处理的类型,就返回具体的输数据处理的对象,处理过程先简化,后面可以具体看一下,不是重点,Retrofit本身提供了很多数据处理默认的Converter.Factory,包括常见的Json数据格式的对象,也可以自定义处理数据。

第二步是生成BlogService对象,此处主要是通过动态代理来获取指定接口的实例,通过Proxy.newProxyInstance()实现,更多详细信息可以查看【设计模式】代理模式(Proxy Pattern).

第三步是调用接口方法生成Call对象实例。这里需要指出的是接口必须返回:

接口中的方法必须有返回值,且比如是Call类型

第四步调用call.execute()同步获取数据,既然有同步,也有异步获取数据(call.enqueue()),其实内部是通过封装OkHttp来实现的同步和异步的。

客户端和服务端请求数据如下

GET http://localhost:3434/retrofit/all.do HTTP/1.1
Accept-Encoding: gzip
Connection: keep-alive
Host: localhost:3434
User-Agent: okhttp/3.14.9
Blog{body='{"code":200,"msg":"OK","des":"all.do","data":[{"id":1,"date":"2020-11-15 10:39:13","author":"fish1","title":"retrofit1","address":"https://gitee.com/zhangyuwxf/retrofit.git","content":"http test1"},{"id":2,"date":"2020-11-15 10:39:13","author":"fish2","title":"retrofit2","address":"https://gitee.com/zhangyuwxf/retrofit.git","content":"http test2"},{"id":3,"date":"2020-11-15 10:39:13","author":"fish3","title":"retrofit3","address":"https://gitee.com/zhangyuwxf/retrofit.git","content":"http test3"},{"id":4,"date":"2020-11-15 10:39:13","author":"fish4","title":"retrofit4","address":"https://gitee.com/zhangyuwxf/retrofit.git","content":"http test4"},{"id":5,"date":"2020-11-15 10:39:13","author":"fish5","title":"retrofit5","address":"https://gitee.com/zhangyuwxf/retrofit.git","content":"http test5"},{"id":6,"date":"2020-11-15 10:39:13","author":"fish6","title":"retrofit6","address":"https://gitee.com/zhangyuwxf/retrofit.git","content":"http test6"},{"id":7,"date":"2020-11-15 10:39:13","author":"fish7","title":"retrofit7","address":"https://gitee.com/zhangyuwxf/retrofit.git","content":"http test7"},{"id":8,"date":"2020-11-15 10:39:13","author":"fish8","title":"retrofit8","address":"https://gitee.com/zhangyuwxf/retrofit.git","content":"http test8"},{"id":9,"date":"2020-11-15 10:39:13","author":"fish9","title":"retrofit9","address":"https://gitee.com/zhangyuwxf/retrofit.git","content":"http test9"},{"id":10,"date":"2020-11-15 10:39:13","author":"fish10","title":"retrofit10","address":"https://gitee.com/zhangyuwxf/retrofit.git","content":"http test10"}]}'}

动态的url访问 @PATH

两个测试接口:

http://localhost:3434/retrofit/user/fish1
http://localhost:3434/retrofit/user/fish2

测试访问接口:

86183@DESKTOP-MKB5ERF MINGW64 ~/Desktop
$ curl http://localhost:3434/retrofit/user/fish1
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100   203    0   203    0     0    863      0 --:--:-- --:--:-- --:--:--   863{"code":200,"msg":"OK","des":"path fish1","data":[{"id":1,"date":"2020-11-15 10:39:13","author":"fish1","title":"retrofit1","address":"https://gitee.com/zhangyuwxf/retrofit.git","content":"http test1"}]}

86183@DESKTOP-MKB5ERF MINGW64 ~/Desktop
$ curl http://localhost:3434/retrofit/user/fish2
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100   203    0   203    0     0   6548      0 --:--:-- --:--:-- --:--:--  6548{"code":200,"msg":"OK","des":"path fish2","data":[{"id":2,"date":"2020-11-15 10:39:13","author":"fish2","title":"retrofit2","address":"https://gitee.com/zhangyuwxf/retrofit.git","content":"http test2"}]}

通过Retrofit访问

    interface BlogService {
        @GET("retrofit/user/{username}")
        Call<Blog> getByName(@Path("username") String username);
    }

可以看到我们定义了一个getByName方法,方法接收一个username参数,并且我们的@GET注解中使用{username}声明了访问路径,这里你可以把{username}当做占位符,而实际运行中会通过@PATH(“username”)所标注的参数进行替换。

            Call<Blog> call = blogService.getByName("fish1");
            Call<Blog> call = blogService.getByName("fish2");

测试结果如下:

GET http://localhost:3434/retrofit/user/fish1 HTTP/1.1
Accept-Encoding: gzip
Connection: keep-alive
Host: localhost:3434
User-Agent: okhttp/3.14.9

GET http://localhost:3434/retrofit/user/fish2 HTTP/1.1
Accept-Encoding: gzip
Connection: keep-alive
Host: localhost:3434
User-Agent: okhttp/3.14.9

可以看到,发起请求的时候把我们传入的参数已拼接到path中。

Blog{body='{"code":200,"msg":"OK","des":"path fish1","data":[{"id":1,"date":"2020-11-15 10:39:13","author":"fish1","title":"retrofit1","address":"https://gitee.com/zhangyuwxf/retrofit.git","content":"http test1"}]}'}
Blog{body='{"code":200,"msg":"OK","des":"path fish2","data":[{"id":2,"date":"2020-11-15 10:39:13","author":"fish2","title":"retrofit2","address":"https://gitee.com/zhangyuwxf/retrofit.git","content":"http test2"}]}'}

查询参数的设置@Query

http://localhost:3434/retrofit/query?id=1
http://localhost:3434/retrofit/query?id=2

测试服务器返回数据:

86183@DESKTOP-MKB5ERF MINGW64 ~/Desktop
$ curl http://localhost:3434/retrofit/query?id=1
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100   201    0   201    0     0    990      0 --:--:-- --:--:-- --:--:--   990{"code":200,"msg":"OK","des":"path fish2","data":{"id":1,"date":"2020-11-15 10:39:13","author":"fish1","title":"retrofit1","address":"https://gitee.com/zhangyuwxf/retrofit.git","content":"http test1"}}

86183@DESKTOP-MKB5ERF MINGW64 ~/Desktop
$ curl http://localhost:3434/retrofit/query?id=4
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100   201    0   201    0     0  13400      0 --:--:-- --:--:-- --:--:-- 13400{"code":200,"msg":"OK","des":"path fish2","data":{"id":4,"date":"2020-11-15 10:39:13","author":"fish4","title":"retrofit4","address":"https://gitee.com/zhangyuwxf/retrofit.git","content":"http test4"}}

即一般的传参,我们可以通过@Query注解方便的完成,我们再次在接口中添加一个方法:
Query注解中的值是参数k,传入的值是参数v.

    interface BlogService {
        @GET("retrofit/query")
        Call<Blog> query(@Query("id") String id);
    }
            Call<Blog> call = blogService.query("2");
            Call<Blog> call = blogService.query("4");

服务端的数据:

GET http://localhost:3434/retrofit/query?id=2 HTTP/1.1
Accept-Encoding: gzip
Connection: keep-alive
Host: localhost:3434
User-Agent: okhttp/3.14.9

GET http://localhost:3434/retrofit/query?id=4 HTTP/1.1
Accept-Encoding: gzip
Connection: keep-alive
Host: localhost:3434
User-Agent: okhttp/3.14.9

客户端请求结果数据:

Blog{body='{"code":200,"msg":"OK","des":"path fish2","data":{"id":2,"date":"2020-11-15 10:39:13","author":"fish2","title":"retrofit2","address":"https://gitee.com/zhangyuwxf/retrofit.git","content":"http test2"}}'}
Blog{body='{"code":200,"msg":"OK","des":"path fish2","data":{"id":4,"date":"2020-11-15 10:39:13","author":"fish4","title":"retrofit4","address":"https://gitee.com/zhangyuwxf/retrofit.git","content":"http test4"}}'}

通过FormUrlEncoded发送表单

关键代码:

        @POST("retrofit/login")
        @FormUrlEncoded
        Call<Blog> login(@Field("username") String username, @Field("password") String password, @Field("tag") String tag);

通过Field字段添加表单键值对

Call<Blog> call = blogService.login("zhangyu", "123456", "tag+" + System.currentTimeMillis());

最终结果:
服务端:

POST http://localhost:3434/retrofit/login?null HTTP/1.1
Accept-Encoding: gzip
Connection: keep-alive
Content-Length: 56
Content-Type: application/x-www-form-urlencoded
Host: localhost:3434
User-Agent: okhttp/3.14.9

username=zhangyu&password=123456&tag=tag%2B1605573357011

客户端

Blog{body='{"code":200,"msg":"OK","des":"LOGIN","data":"LOGIN:username=zhangyu&password=123456&tag=tag%2B1605573357011"}'}

通过@Body上传对象

        @POST("retrofit/add")
        Call<Blog> addBlog(@Body Blog user);

需要设置Blog对象转换为RequestBody的工厂

        Retrofit retrofit = new Retrofit.Builder()
                .baseUrl(HttpUrl.get("http://localhost:3434/"))
                .addConverterFactory(FACTORY)
                .build();
    static final Converter.Factory FACTORY =
            new Converter.Factory() {
                @Override
                public Converter<?, RequestBody> requestBodyConverter(Type type, Annotation[] parameterAnnotations, Annotation[] methodAnnotations, Retrofit retrofit) {
                    if (type == Blog.class) {
                        return new RequestBodyAdapter();
                    }
                    return null;
                }
            };
            
    static final class RequestBodyAdapter implements Converter<Blog, RequestBody> {
        @Override
        public RequestBody convert(Blog blog) throws IOException {
            return RequestBody.create(null, blog.toString());  //构建RequestBody对象
        }
    }

通过@Body设置的网络请求需要实现步骤

  1. 创建Converter.Factory工厂,根据Type类型返回对应的类型转换Converter
  2. 创建T->RequestBody 的类型转化实例
  3. 发起请求

发起请求

Call<Blog> call = blogService.addBlog(new Blog("I am clint Blog!"));

测试结果如下:
服务端:

POST http://localhost:3434/retrofit/add?null HTTP/1.1
Accept-Encoding: gzip
Connection: keep-alive
Content-Length: 29
Host: localhost:3434
User-Agent: okhttp/3.14.9

Blog{body='I am clint Blog!'}

客户端:

Blog{body='{"code":200,"msg":"OK","des":"ADD","data":"ADD"}'}

通过Multipart发送文件

        @Multipart
        @POST("retrofit/register")
        Call<Blog> registerUser(@Part MultipartBody.Part part, @Part("username") RequestBody username, @Part("password") RequestBody password);
            File file = new File("samples/build.gradle");
            RequestBody photoRequestBody = RequestBody.create(MediaType.parse("text/plain"), file);
            MultipartBody.Part photo = MultipartBody.Part.createFormData("fileName", "build.gradle", photoRequestBody);

            Call<Blog> call = blogService.registerUser(photo, RequestBody.create(null, "abc"), RequestBody.create(null, "123"));

测试结果:
服务端:

POST http://localhost:3434/retrofit/register?null HTTP/1.1
Accept-Encoding: gzip
Connection: keep-alive
Content-Length: 1002
Content-Type: multipart/form-data; boundary=10ee6686-40f8-49fa-8075-379e338a9eac
Host: localhost:3434
User-Agent: okhttp/3.14.9

--10ee6686-40f8-49fa-8075-379e338a9eac
Content-Disposition: form-data; name="fileName"; filename="build.gradle"
Content-Type: text/plain
Content-Length: 495

apply plugin: 'java-library'

dependencies {
  implementation project(':retrofit')
  implementation project(':retrofit-mock')
  implementation project(':retrofit-converters:moshi')
  implementation project(':retrofit-converters:gson')
  implementation project(':retrofit-converters:simplexml')
  implementation project(':retrofit-adapters:rxjava')
  implementation deps.mockwebserver
  implementation deps.guava
  implementation deps.jsoup
  compileOnly deps.findBugsAnnotations
}

--10ee6686-40f8-49fa-8075-379e338a9eac
Content-Disposition: form-data; name="username"
Content-Transfer-Encoding: binary
Content-Length: 3

abc
--10ee6686-40f8-49fa-8075-379e338a9eac
Content-Disposition: form-data; name="password"
Content-Transfer-Encoding: binary
Content-Length: 3

123
--10ee6686-40f8-49fa-8075-379e338a9eac--

正文分为三个部分。

客户端

Blog{body='{"code":200,"msg":"OK","des":"REGISTER","data":"REGISTER:--10ee6686-40f8-49fa-8075-379e338a9eac\r\nContent-Disposition: form-data; name=\"fileName\"; filename=\"build.gradle\"\r\nContent-Type: text/plain\r\nContent-Length: 495\r\n\r\napply plugin: 'java-library'\r\n\r\ndependencies {\r\n  implementation project(':retrofit')\r\n  implementation project(':retrofit-mock')\r\n  implementation project(':retrofit-converters:moshi')\r\n  implementation project(':retrofit-converters:gson')\r\n  implementation project(':retrofit-converters:simplexml')\r\n  implementation project(':retrofit-adapters:rxjava')\r\n  implementation deps.mockwebserver\r\n  implementation deps.guava\r\n  implementation deps.jsoup\r\n  compileOnly deps.findBugsAnnotations\r\n}\r\n\r\n--10ee6686-40f8-49fa-8075-379e338a9eac\r\nContent-Disposition: form-data; name=\"username\"\r\nContent-Transfer-Encoding: binary\r\nContent-Length: 3\r\n\r\nabc\r\n--10ee6686-40f8-49fa-8075-379e338a9eac\r\nContent-Disposition: form-data; name=\"password\"\r\nContent-Transfer-Encoding: binary\r\nContent-Length: 3\r\n\r\n123\r\n--10ee6686-40f8-49fa-8075-379e338a9eac--\r\n"}'}

多文件上传@PartMap

        @Multipart
        @POST("retrofit/register")
        Call<Blog> registerUser(@PartMap Map<String, RequestBody> params, @Part("password") RequestBody password);
            RequestBody requestBody1 = RequestBody.create(MediaType.parse("text/plain"), new File("samples/build.gradle"));
            RequestBody requestBody2 = RequestBody.create(MediaType.parse("text/plain"), new File("samples/src/main/java/com/example/test/BodyTest.java"));
            
            HashMap<String, RequestBody> hashMap = new HashMap<>();
            hashMap.put("part1", requestBody1);
            hashMap.put("part2", requestBody2);
            Call<Blog> call = blogService.registerUser(hashMap, RequestBody.create(null, "xyz"));

测试结果:

POST http://localhost:3434/retrofit/register?null HTTP/1.1
Accept-Encoding: gzip
Connection: keep-alive
Content-Length: 3696
Content-Type: multipart/form-data; boundary=b7d6f01e-861c-463e-aef1-fb3e00ad64f4
Host: localhost:3434
User-Agent: okhttp/3.14.9

--b7d6f01e-861c-463e-aef1-fb3e00ad64f4
Content-Disposition: form-data; name="part1"
Content-Transfer-Encoding: binary
Content-Type: text/plain
Content-Length: 495

apply plugin: 'java-library'

dependencies {
  implementation project(':retrofit')
  implementation project(':retrofit-mock')
  implementation project(':retrofit-converters:moshi')
  implementation project(':retrofit-converters:gson')
  implementation project(':retrofit-converters:simplexml')
  implementation project(':retrofit-adapters:rxjava')
  implementation deps.mockwebserver
  implementation deps.guava
  implementation deps.jsoup
  compileOnly deps.findBugsAnnotations
}

--b7d6f01e-861c-463e-aef1-fb3e00ad64f4
Content-Disposition: form-data; name="part2"
Content-Transfer-Encoding: binary
Content-Type: text/plain
Content-Length: 2664

package com.example.test;

import okhttp3.HttpUrl;
import okhttp3.ResponseBody;
import retrofit2.Call;
import retrofit2.Converter;
import retrofit2.Response;
import retrofit2.Retrofit;
import retrofit2.http.*;

import javax.annotation.Nullable;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;

public class BodyTest {
    interface BlogService {
        @GET("retrofit/all.do")
        Call<QueryTest.Blog> getAll();

        @GET("retrofit/user/{username}")
        Call<QueryTest.Blog> getByName(@Path("username") String username);

        @GET("retrofit/query")
        Call<QueryTest.Blog> query(@Query("id") String id);

        @POST("retrofit/add")
        Call<Blog> addBlog(@Body Blog user);
    }


    private static class Blog {
        private String body;

        public Blog(String body) {
            this.body = body;
        }

        @Override
        public String toString() {
            return "Blog{" +
                    "body='" + body + '\'' +
                    '}';
        }
    }

    public static void main(String[] args) {

        Retrofit retrofit = new Retrofit.Builder()
                .baseUrl(HttpUrl.get("http://localhost:3434/"))
                .addConverterFactory(DataAdapter.FACTORY)
                .build();

        BlogService blogService = retrofit.create(BlogService.class);
        try {
            // FIXME: 2020/11/17 �?要自定义参数,工厂,把Blog转化为RequestBody
            Call<Blog> call = blogService.addBlog(new Blog("I am clint Blog!"));
            Response<Blog> response = call.execute();
            Blog blog = response.body();
            System.out.println(blog);
        } catch (IOException e) {
            e.printStackTrace();
        }


    }

    static final class DataAdapter implements Converter<ResponseBody, Blog> {
        static final Factory FACTORY =
                new Factory() {
                    @Override
                    public @Nullable
                    Converter<ResponseBody, ?> responseBodyConverter(
                            Type type, Annotation[] annotations, Retrofit retrofit) {
                        if (type == QueryTest.Blog.class) return new QueryTest.DataAdapter();
                        return null;
                    }
                };

        @Override
        public Blog convert(ResponseBody responseBody) throws IOException {
            String body = responseBody.string();
            // FIXME: 2020/11/16 数据处理
            return new Blog(body);
        }
    }
}

--b7d6f01e-861c-463e-aef1-fb3e00ad64f4
Content-Disposition: form-data; name="password"
Content-Transfer-Encoding: binary
Content-Length: 3

xyz
--b7d6f01e-861c-463e-aef1-fb3e00ad64f4--

客户端:

Blog{body='{"code":200,"msg":"OK","des":"REGISTER","data":"REGISTER:--b7d6f01e-861c-463e-aef1-fb3e00ad64f4\r\nContent-Disposition: form-data; name=\"part1\"\r\nContent-Transfer-Encoding: binary\r\nContent-Type: text/plain\r\nContent-Length: 495\r\n\r\napply plugin: 'java-library'\r\n\r\ndependencies {\r\n  implementation project(':retrofit')\r\n  implementation project(':retrofit-mock')\r\n  implementation project(':retrofit-converters:moshi')\r\n  implementation project(':retrofit-converters:gson')\r\n  implementation project(':retrofit-converters:simplexml')\r\n  implementation project(':retrofit-adapters:rxjava')\r\n  implementation deps.mockwebserver\r\n  implementation deps.guava\r\n  implementation deps.jsoup\r\n  compileOnly deps.findBugsAnnotations\r\n}\r\n\r\n--b7d6f01e-861c-463e-aef1-fb3e00ad64f4\r\nContent-Disposition: form-data; name=\"part2\"\r\nContent-Transfer-Encoding: binary\r\nContent-Type: text/plain\r\nContent-Length: 2664\r\n\r\npackage com.example.test;\r\n\r\nimport okhttp3.HttpUrl;\r\nimport okhttp3.ResponseBody;\r\nimport retrofit2.Call;\r\nimport retrofit2.Converter;\r\nimport retrofit2.Response;\r\nimport retrofit2.Retrofit;\r\nimport retrofit2.http.*;\r\n\r\nimport javax.annotation.Nullable;\r\nimport java.io.IOException;\r\nimport java.lang.annotation.Annotation;\r\nimport java.lang.reflect.Type;\r\n\r\npublic class BodyTest {\r\n    interface BlogService {\r\n        @GET(\"retrofit/all.do\")\r\n        Call<QueryTest.Blog> getAll();\r\n\r\n        @GET(\"retrofit/user/{username}\")\r\n        Call<QueryTest.Blog> getByName(@Path(\"username\") String username);\r\n\r\n        @GET(\"retrofit/query\")\r\n        Call<QueryTest.Blog> query(@Query(\"id\") String id);\r\n\r\n        @POST(\"retrofit/add\")\r\n        Call<Blog> addBlog(@Body Blog user);\r\n    }\r\n\r\n\r\n    private static class Blog {\r\n        private String body;\r\n\r\n        public Blog(String body) {\r\n            this.body = body;\r\n        }\r\n\r\n        @Override\r\n        public String toString() {\r\n            return \"Blog{\" +\r\n                    \"body='\" + body + '\\'' +\r\n                    '}';\r\n        }\r\n    }\r\n\r\n    public static void main(String[] args) {\r\n\r\n        Retrofit retrofit = new Retrofit.Builder()\r\n                .baseUrl(HttpUrl.get(\"http://localhost:3434/\"))\r\n                .addConverterFactory(DataAdapter.FACTORY)\r\n                .build();\r\n\r\n        BlogService blogService = retrofit.create(BlogService.class);\r\n        try {\r\n            // FIXME: 2020/11/17 �?要自定义参数,工厂,把Blog转化为RequestBody\r\n            Call<Blog> call = blogService.addBlog(new Blog(\"I am clint Blog!\"));\r\n            Response<Blog> response = call.execute();\r\n            Blog blog = response.body();\r\n            System.out.println(blog);\r\n        } catch (IOException e) {\r\n            e.printStackTrace();\r\n        }\r\n\r\n\r\n    }\r\n\r\n    static final class DataAdapter implements Converter<ResponseBody, Blog> {\r\n        static final Factory FACTORY =\r\n                new Factory() {\r\n                    @Override\r\n                    public @Nullable\r\n                    Converter<ResponseBody, ?> responseBodyConverter(\r\n                            Type type, Annotation[] annotations, Retrofit retrofit) {\r\n                        if (type == QueryTest.Blog.class) return new QueryTest.DataAdapter();\r\n                        return null;\r\n                    }\r\n                };\r\n\r\n        @Override\r\n        public Blog convert(ResponseBody responseBody) throws IOException {\r\n            String body = responseBody.string();\r\n            // FIXME: 2020/11/16 数据处理\r\n            return new Blog(body);\r\n        }\r\n    }\r\n}\r\n\r\n--b7d6f01e-861c-463e-aef1-fb3e00ad64f4\r\nContent-Disposition: form-data; name=\"password\"\r\nContent-Transfer-Encoding: binary\r\nContent-Length: 3\r\n\r\nxyz\r\n--b7d6f01e-861c-463e-aef1-fb3e00ad64f4--\r\n"}'}

参考

大神 Retrofit2 完全解析 探索与okhttp之间的关系

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值