Android模拟简单的网络请求框架Retrofit实现

发布于:2025-09-08 ⋅ 阅读:(21) ⋅ 点赞:(0)

1.静态代理

代理默认给某一个对象提供一个代理对象,并由代理对象控制对原对象的引用。通俗来讲,代理模式就是我们生活中常见的中介。比如你按照小卡片上的电话打过去寻求服务,一般不是由本人,可能是一个成年雄性接听电话,然后真正做事情的可能就是另一个小姐姐了。

目的:

  1. 通过引入代理对象的方式来间接访问目标对象,防止直接访问目标对象给系统带来的不必要复杂性;
  2. 通过代理对象来访问控制;

在这里插入图片描述
代理模式一般会有三个角色:
抽象角色:指代理角色和真实角色对外提供的公共方法,一般为一个接口
真实角色:需要实现抽象角色接口,定义了真实角色所实现的业务逻辑,以便供代理角色调用。也就是真正的业务逻辑。
代理角色:需要实现抽象角色接口,是真实角色的代理,通过真实角色的业务逻辑方法来实现抽象方法,并可以附加自己的操作。将统一的流程控制都放到代理角色中处理。

比如:抽象角色
创建一个接口

public interface Image {
   void display();
}

代理角色:

public class ProxyImage implements Image{
 
   private RealImage realImage;
   private String fileName;
 
   public ProxyImage(String fileName){
      this.fileName = fileName;
   }
 
   @Override
   public void display() {
      if(realImage == null){
         realImage = new RealImage(fileName);
      }
      realImage.display();
   }
}

真实角色:

public class RealImage implements Image {
 
   private String fileName;
 
   public RealImage(String fileName){
      this.fileName = fileName;
      loadFromDisk(fileName);
   }
 
   @Override
   public void display() {
      System.out.println("Displaying " + fileName);
   }
 
   private void loadFromDisk(String fileName){
      System.out.println("Loading " + fileName);
   }
}

当被请求的时候,则使用代理类获取来获取真实对象

public class ProxyPatternDemo {
   
   public static void main(String[] args) {
      Image image = new ProxyImage("test_10mb.jpg");
 
      // 图像将从磁盘加载
      image.display(); 
      System.out.println("");
      // 图像不需要从磁盘加载
      image.display();  
   }
}

打印:

Loading test_10mb.jpg
Displaying test_10mb.jpg

Displaying test_10mb.jpg

优点:

  • 可以在不修改目标对象的前提下,增加功能
  • 保护目标对象,客户端不直接访问真实对象

缺点:
一个代理类只能代理一个接口/类,代码冗余严重
比如你还要代理一个其他类,就要再写一个对应的代理类,重复代码

举个例子来对比:

静态代理:新增接口 ->必须改代码,写新代理类
动态代理:新增接口 ->完全不用改代码,直接支持

假设有一个服务平台,现在有两类服务:
抽象角色类:

// 发送短信
public interface SmsService {
    void sendSms(String msg);
}

// 发送邮件
public interface EmailService {
    void sendEmail(String to, String subject);
}

真实实现类:

public class SmsServiceImpl implements SmsService {
    @Override
    public void sendSms(String msg) {
        System.out.println("发送短信: " + msg);
    }
}

public class EmailServiceImpl implements EmailService {
    @Override
    public void sendEmail(String to, String subject) {
        System.out.println("发送邮件到 " + to + ",主题: " + subject);
    }
}

静态代理的痛苦,每新增一个接口就要写一个代理类
起初只有SmsService,写了一个代理

public class SmsServiceProxy implements SmsService {

    private final SmsService smsService;

    public SmsServiceProxy(SmsService smsService) {
        this.smsService = smsService;
    }

    @Override
    public void sendSms(String msg) {
        System.out.println("【日志】准备发送短信...");
        smsService.sendSms(msg);
        System.out.println("【日志】短信发送完成!");
    }
}

现在要加EmailService,必须再写一个代理类

public class EmailServiceProxy implements EmailService {

    private final EmailService emailService;

    public EmailServiceProxy(EmailService emailService) {
        this.emailService = emailService;
    }

    @Override
    public void sendEmail(String to, String subject) {
        System.out.println("【日志】准备发送邮件...");
        emailService.sendEmail(to, subject);
        System.out.println("【日志】邮件发送完成!");
    }
}

你会发现:两个代理类的逻辑一模一样,只是接口不同,但你必须要写两个类,否则无法代理。
如果再加WeChatService、PushService…则需要写WeChatServiceProxy、PushServiceProxy…

当然,也可以代理类和实现类同时实现两个接口

// 手动实现两个接口
public class NotificationProxy implements SmsService, EmailService {

    private final NotificationServiceImpl target;

    public NotificationProxy(NotificationServiceImpl target) {
        this.target = target;
    }

    @Override
    public void sendSms(String msg) {
        System.out.println("【静态代理】准备发短信...");
        target.sendSms(msg);
        System.out.println("【静态代理】短信发送完成");
    }

    @Override
    public void sendEmail(String to, String subject) {
        System.out.println("【静态代理】准备发邮件...");
        target.sendEmail(to, subject);
        System.out.println("【静态代理】邮件发送完成");
    }
}
public class NotificationServiceImpl implements SmsService, EmailService {

    @Override
    public void sendSms(String msg) {
        System.out.println("发送短信: " + msg);
    }

    @Override
    public void sendEmail(String to, String subject) {
        System.out.println("发送邮件到: " + to + ",主题: " + subject);
    }
}
NotificationProxy proxy = new NotificationProxy(new NotificationServiceImpl());

SmsService smsProxy = proxy;
smsProxy.sendSms("你好");

EmailService emailProxy = proxy;
emailProxy.sendEmail("user@abc.com", "测试");

但这样的话,代码重复,维护困难
每个方法都要手动写一遍代码逻辑
如果新增WeChatService接口,你必须

  • 修改NotificationProxy类
  • 添加implements WeChatService
  • 实现sendWeChat()方法

2.动态代理

在运行时再创建代理类和其实例,因此显然效率更低。要完成这个场景,需要在运行期间动态创建一个Class。JDK提供了Proxy来完成这件事情。基本使用如下:
比如动态代理可以同时代理两个接口

NotificationServiceImpl target = new NotificationServiceImpl();

// 创建代理,让它同时实现两个接口
Object proxy = Proxy.newProxyInstance(
    Test.class.getClassLoader(),
    new Class[]{SmsService.class, EmailService.class},  // 同时代理两个接口
    new InvocationHandler() {
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            System.out.println("【日志】调用方法: " + method.getName() + " 开始...");
            Object result = method.invoke(target, args);
            System.out.println("【日志】调用方法: " + method.getName() + " 结束.");
            return result;
        }
    }
);
// 当作 SmsService 使用
SmsService smsProxy = (SmsService) proxy;
smsProxy.sendSms("你好啊");

// 当作 EmailService 使用
EmailService emailProxy = (EmailService) proxy;
emailProxy.sendEmail("user@abc.com", "订单确认");

输出结果

【日志】调用方法: sendSms 开始...
发送短信: 你好啊
【日志】调用方法: sendSms 结束.

【日志】调用方法: sendEmail 开始...
发送邮件到: user@abc.com,主题: 订单确认
【日志】调用方法: sendEmail 结束.

动态代理也可以需要一个通用的处理器,就能代理任意接口

public class LogInvocationHandler implements InvocationHandler {

    private final Object target;//真实对象

    public LogInvocationHandler(Object target){
        this.target = target;
    }


    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("日志调用方法:"+method.getName()+"开始...");
        Object result = method.invoke(target,args);//调用真实方法
        System.out.println("日志调用方法:"+method.getName()+"结束...");

        return result;
    }
}

public class Test {
    public static void main(String[] args) {
        //代理SmsService
        SmsService sms = new SmsServiceImpl();
        SmsService smProxy = (SmsService) Proxy.newProxyInstance(
                Test.class.getClassLoader(),
                new Class[]{SmsService.class},
                new LogInvocationHandler(sms)
        );
        smProxy.sendSms("你好啊");
        //代理EmailService
        EmailService email = new EmailServiceImpl();
        EmailService emailProxy = (EmailService)Proxy.newProxyInstance(
                Test.class.getClassLoader(),
                new Class[]{EmailService.class},
                new LogInvocationHandler(email)
        );
        emailProxy.sendEmail("user@abc.com","订单确认");
    }
}

输出结果

日志调用方法:sendSms开始...
发送短信:你好啊
日志调用方法:sendSms结束...
日志调用方法:sendEmail开始...
发送邮件到 user@abc.com,主题:订单确认
日志调用方法:sendEmail结束...

注意要避免的问题

public class LogInvocationHandler implements InvocationHandler {

    private final Object target;//真实对象

    public LogInvocationHandler(Object target){
        this.target = target;
    }


    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println(proxy);
        System.out.println("日志调用方法:"+method.getName()+"开始...");
        Object result = method.invoke(target,args);//调用真实方法
        System.out.println("日志调用方法:"+method.getName()+"结束...");

        return result;
    }
}

如果你在动态代理的InvocationHandler 里的invoke方法打印一下proxy,你会发现报了栈溢出的错
在这里插入图片描述
因为你打印proxy其实默认会走proxy的toString()方法也会回到到invoke方法造成栈溢出
我们也可以看一下Retrofit的代码,可以看到当使用到Object的方法的时候,传递的是this,而不是proxy代理对象。如果你写 method.invoke(proxy, args),可能会再次触发 invoke(),导致无限递归!所以 Retrofit 选择调用 this,避免循环。
在这里插入图片描述
在生成Class文件的时候,有两种方式一种是.java源文件通过javac编译生成的.class文件,再通过类加载生成Class对象,这种方式生成的class文件来源是在硬盘。而动态代理生成的class文件来源在内存生成的。
动态代理生成的源代码

 @CallerSensitive
    public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)
        throws IllegalArgumentException
    {
        Objects.requireNonNull(h);

        final Class<?>[] intfs = interfaces.clone();
        // Android-removed: SecurityManager calls
        /*
        final SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
        }
        */

        /*
         * Look up or generate the designated proxy class.
         */
        Class<?> cl = getProxyClass0(loader, intfs);

        /*
         * Invoke its constructor with the designated invocation handler.
         */
        try {
            // Android-removed: SecurityManager / permission checks.
            /*
            if (sm != null) {
                checkNewProxyPermission(Reflection.getCallerClass(), cl);
            }
            */

            final Constructor<?> cons = cl.getConstructor(constructorParams);
            final InvocationHandler ih = h;
            if (!Modifier.isPublic(cl.getModifiers())) {
                // BEGIN Android-removed: Excluded AccessController.doPrivileged call.
                /*
                AccessController.doPrivileged(new PrivilegedAction<Void>() {
                    public Void run() {
                        cons.setAccessible(true);
                        return null;
                    }
                });
                */

                cons.setAccessible(true);
                // END Android-removed: Excluded AccessController.doPrivileged call.
            }
            return cons.newInstance(new Object[]{h});
        } catch (IllegalAccessException|InstantiationException e) {
            throw new InternalError(e.toString(), e);
        } catch (InvocationTargetException e) {
            Throwable t = e.getCause();
            if (t instanceof RuntimeException) {
                throw (RuntimeException) t;
            } else {
                throw new InternalError(t.toString(), t);
            }
        } catch (NoSuchMethodException e) {
            throw new InternalError(e.toString(), e);
        }
    }
Class<?> cl = getProxyClass0(loader, interfaces); 
  ↓
JVM 调用 ProxyGenerator 生成字节码
  ↓
ProxyGenerator.generateProxyClass(...) 返回 byte[],生成的Class数据的byte数组
  ↓
JVM defineClass(byte[]) 加载这个类,将这个class数据转成一个Class对象

验证

        String name = SmsService.class.getName()+"$proxy0";
        //两个参数,生成的类名,代理的接口
        byte[] bytes = ProxyGenerator.generateProxyClass(name,new Class[]{SmsService.class});
        FileOutputStream fos = new FileOutputStream("src/"+name+".class");
        fos.write(bytes);
        fos.close();

在这里插入图片描述

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package agent;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;

public final class SmsService$proxy0 extends Proxy implements SmsService {
    private static Method m1;
    private static Method m3;
    private static Method m2;
    private static Method m0;

    public SmsService$proxy0(InvocationHandler var1) throws  {
        super(var1);
    }

    public final boolean equals(Object var1) throws  {
        try {
            return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }

    public final void sendSms(String var1) throws  {
        try {
            super.h.invoke(this, m3, new Object[]{var1});
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }

    public final String toString() throws  {
        try {
            return (String)super.h.invoke(this, m2, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final int hashCode() throws  {
        try {
            return (Integer)super.h.invoke(this, m0, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    static {
        try {
            m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
            m3 = Class.forName("agent.SmsService").getMethod("sendSms", Class.forName("java.lang.String"));
            m2 = Class.forName("java.lang.Object").getMethod("toString");
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}

所以我们使用动态代理,Proxy.newProxyInstance代理SmsService接口生成的.class文件就是上面的样子

SmsService sms = new SmsServiceImpl();
        SmsService smProxy = (SmsService) Proxy.newProxyInstance(
                Test.class.getClassLoader(),
                new Class[]{SmsService.class},
                new LogInvocationHandler(sms)
        );
        smProxy.sendSms("你好啊");
        smProxy.toString();

这里可以看到SmsService$proxy0类里的构造方法可以看到super(var1);会将new LogInvocationHandler(sms)传递进去。
super(var1)进入Proxy.java类的构造方法

    protected Proxy(InvocationHandler h) {
        Objects.requireNonNull(h);
        this.h = h;
    }

可以看到将当前的this.h=h,赋值给当前类的InvocationHandler
而当调用smProxy.sendSms(“你好啊”);的时候就会调用SmsService$proxy0类里sendSms方法

    public final void sendSms(String var1) throws  {
        try {
            super.h.invoke(this, m3, new Object[]{var1});
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }

这个时候调用super.h.invoke(this, m3, new Object[]{var1}),就是将对应的信息回调给LogInvocationHandler里的invoke方法。
this就是当前生成的代理类对象。
m3可以看到Class.forName(“agent.SmsService”).getMethod(“sendSms”, Class.forName(“java.lang.String”))
通过反射的方式生成对应的method对象。

3.实现简单的Retrofit

定义对应的请求注解参数

@Target(PARAMETER)
@Retention(RUNTIME)
public @interface Field {

    String value();
}

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface GET {
    String value() default "";
}

@Target(ElementType.METHOD)//作用在方法上
@Retention(RetentionPolicy.RUNTIME) //保留期在运行期间
public @interface POST {
    String value() default "";
}

@Target(PARAMETER)
@Retention(RUNTIME)
public @interface Query {

    String value();
}

通过动态代理模拟Retrofit的创建

/**
 * 通过动态代理模拟Retrofit的创建
 */
public class MyRetrofit {

    final Map<Method,ServiceMethod> serviceMethodCache = new ConcurrentHashMap<>();
    final Call.Factory callFactory;
    final HttpUrl baseUrl;

    MyRetrofit(Call.Factory callFactory,HttpUrl baseUrl){
        this.callFactory = callFactory;
        this.baseUrl = baseUrl;
    }

    public <T> T create(final Class<T> service){
        return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class[]{service},
                new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        //解析这个method上所有注解
                        ServiceMethod serviceMethod = loadServiceMethod(method);
                        //args就是传递参数的值
                        return serviceMethod.invoke(args);
                    }
                }
        );
    }

    private ServiceMethod loadServiceMethod(Method method) {
        //先不上锁,避免synchronized的性能损失
        ServiceMethod result = serviceMethodCache.get(method);
        if(result!=null) return result;
        //多线程下,避免重复解析
        synchronized (serviceMethodCache){
            result = serviceMethodCache.get(method);
            if(result==null){
                result = new ServiceMethod.Builder(this,method).build();
                serviceMethodCache.put(method,result);
            }
        }
        return result;
    }


    /**
     * 构建者模式,将一个复杂对象的构建和它的表示分离,可以使用者不必知道内部组成的细节
     */
    public static final class Builder{
        private HttpUrl baseUrl;
        private Call.Factory callFactory;

        public Builder callFactory(Call.Factory factory){
            this.callFactory = factory;
            return this;
        }
        public Builder baseUrl(String baseUrl){
            this.baseUrl = HttpUrl.get(baseUrl);
            return this;
        }
        public MyRetrofit build() throws IllegalAccessException {
            if(baseUrl == null){
                throw new IllegalAccessException("Base URL required");
            }
            Call.Factory callFactory = this.callFactory;
            if(callFactory == null){
                callFactory = new OkHttpClient();
            }
            return new MyRetrofit(callFactory,baseUrl);
        }

    }

}

请求参数的处理

public class ServiceMethod {

    private final Call.Factory callFactory;
    private final String relativeUrl;
    private final boolean hasBody;
    private final ParameterHandler[] parameterHandler;
    HttpUrl baseUrl;
    String httpMethod;
    private FormBody.Builder formBuild;
    HttpUrl.Builder urlBuilder;

    public ServiceMethod(Builder builder) {
        baseUrl = builder.myRetrofit.baseUrl;
        callFactory = builder.myRetrofit.callFactory;
        httpMethod = builder.httpMethod;
        relativeUrl = builder.relativeUrl;
        hasBody = builder.hasBody;
        parameterHandler = builder.parameterHandler;
        //如果是由请求体,创建一个okhttp的请求体对象
        if(hasBody){
            formBuild = new FormBody.Builder();
        }
    }

    public Object invoke(Object[] args) {

        /**
         * 处理请求的地址与参数
         * parameterHandler存的key的顺序和args参数的值顺序一一对应
         */
        for (int i = 0; i < parameterHandler.length; i++) {
            ParameterHandler handlers = parameterHandler[i];
            //handler内本来就记录了key,现在给到对应的value
            handlers.apply(this,args[i].toString());
        }

        //获取最终请求地址
        HttpUrl url;
        if(urlBuilder == null){
            urlBuilder = baseUrl.newBuilder(relativeUrl);
        }
        url = urlBuilder.build();

        //请求体
        FormBody formBody = null;
        if(formBuild!=null){
            formBody = formBuild.build();
        }

        Request request = new Request.Builder().url(url).method(httpMethod,formBody).build();
        return callFactory.newCall(request);
    }

    //get请求,把k-v拼到url里面
    public void addQueryParameter(String key, String value) {
        if(urlBuilder == null){
            urlBuilder = baseUrl.newBuilder(relativeUrl);//将baseUrl和relativeUrl拼到一起
        }
        //在url后面加k-v
        urlBuilder.addQueryParameter(key,value);
    }

    //post请求,把k-v放到请求体中
    public void addFiledParameter(String key, String value) {
        formBuild.add(key,value);
    }

    public static class Builder{
        private final MyRetrofit myRetrofit;
        private final Annotation[] methodAnnotations;
        private final Annotation[][] parameterAnnoations;
        private String httpMethod;
        private String relativeUrl;
        private boolean hasBody;
        private ParameterHandler[] parameterHandler;

        public Builder(MyRetrofit myRetrofit, Method method){
            this.myRetrofit = myRetrofit;
            //获取方法上的所有的注解
            methodAnnotations = method.getAnnotations();
            //获得方法参数的所有的注解(一个参数可以有多个注解,一个方法又会有多个参数)
            parameterAnnoations = method.getParameterAnnotations();
        }

        public ServiceMethod build(){

            /**
             * 解析方法上的注解,只处理POST和GET
             */
            for (Annotation methodAnnotation : methodAnnotations) {
                if(methodAnnotation instanceof POST){
                    //记录当前请求方式
                    this.httpMethod = "POST";
                    //记录请求url的path
                    this.relativeUrl = ((POST) methodAnnotation).value();
                    //是否有请求体
                    this.hasBody = true;
                }else if(methodAnnotation instanceof GET){
                    this.httpMethod = "GET";
                    this.relativeUrl = ((GET) methodAnnotation).value();
                    this.hasBody = false;
                }
            }

            /**
             * 解析方法参数的注解
             */
            int length = parameterAnnoations.length;
            parameterHandler = new ParameterHandler[length];
            for(int i =0;i<length;i++){
                //一个参数上的所有注解
                Annotation[] annotations = parameterAnnoations[i];
                //处理参数上的每一个注解
                for (Annotation annotation : annotations) {
                    //可以加一个判断,如果httpMethod是get请求,现在又解析到Filed注解,可以提示使用者使用Query注解
                    if(annotation instanceof Field){
                        String value = ((Field) annotation).value();
                        parameterHandler[i] = new ParameterHandler.FiledParameterHandler(value);//等于在一个新的类中记录key
                    }else if(annotation instanceof Query){
                        String value = ((Query) annotation).value();
                        parameterHandler[i] = new ParameterHandler.QueryParameterHandler(value);
                    }
                }
            }
            return new ServiceMethod(this);
        }

    }

}
public abstract class ParameterHandler {

    abstract void apply(ServiceMethod serviceMethod,String value);

    static class QueryParameterHandler extends ParameterHandler{

        String key;

        public QueryParameterHandler(String key){
            this.key = key;
        }

        @Override
        void apply(ServiceMethod serviceMethod, String value) {
            serviceMethod.addQueryParameter(key,value);
        }
    }

    static class FiledParameterHandler extends ParameterHandler{

        String key;

        public FiledParameterHandler(String key){
            this.key = key;
        }

        @Override
        void apply(ServiceMethod serviceMethod, String value) {
            serviceMethod.addFiledParameter(key,value);
        }
    }


}

定义请求接口

//https://www.wanandroid.com/article/list/0/json?cid=60
public interface MyTestApi {

    @GET("/article/list/0/json")
    Call getTestApi(@Query("cid") String cid);

/*
    @GET("/project/list/1/json")
    Call getWeather(@Query("cid") String cid);
*/

    @POST("/user/login")
    Call postTestApi(@Field("username") String username, @Field("password") String password);

}

测试请求

class MainActivity3 : AppCompatActivity() {




    @SuppressLint("MissingInflatedId", "SetTextI18n")
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main3)
        val myRetrofit = MyRetrofit.Builder().baseUrl("https://www.wanandroid.com").build()
        val weatherApi = myRetrofit.create(MyTestApi::class.java)

        val call = weatherApi.getTestApi("60")
        call.enqueue(object :  Callback {
            override fun onFailure(call: Call, e: IOException) {
                Log.d("cyr", "onFailure: $e")
            }

            override fun onResponse(call: Call, response: Response) {
                Log.d("cyr","onResponse get:"+response.body())
            }

        })


        val postCall = weatherApi.postTestApi("xxxx","xxxx")
        postCall.enqueue(object : Callback{
            override fun onFailure(call: Call, e: IOException) {

            }

            override fun onResponse(call: Call, response: Response) {
                val body = response.body()
                try {
                    val string = body!!.string()
                    Log.i("cyr", "onResponse post: $string")
                } catch (e: IOException) {
                    e.printStackTrace()
                } finally {
                    body!!.close()
                }
            }

        })
    }

}

在这里插入图片描述
可以看到输出了请求成功结果日志。


网站公告

今日签到

点亮在社区的每一天
去签到