如何应对Android面试官->Retrofit中注解、反射、动态代理、手写实现

注解 反射 可以查看上一章的讲解 如何应对Android面试官->Java中的注解、反射

代理模式

静态代理

代理模式类图

静态代理主要分三个角色:抽象角色、真实角色、代理角色

抽象角色:指代理角色和真实角色对外提供的公共方法,一般为一个接口;

csharp 复制代码
public interface Message { 
    void message(); 
} 

真实角色:需要实现抽象角色接口,定义了真实角色索要实现的业务逻辑,以便代理角色调用,也是真正的业务逻辑在此;

typescript 复制代码
 public class Alvin implements Message { 
    public void message() { // } 
} 

代理角色:需要实现抽象角色接口,是真实角色的代理,通过真实角色的业务逻辑来实现抽象方法,并可以附加自己的操作,将统一流程控制放到代理角色中处理;

通过代理,把调用者对真实对象的调用隔离了起来;

csharp 复制代码
public class Agent implements Message() { 
    private Message message; 
    public Agent(Message message) { 
        this.message = message; 
    } 
    public void message() { 
        // before(); 
        message.message(); 
        after(); 
    } 
    private void before() { } 
    private void after() { } 
} 

静态代理模式的实际应用

网络请求框架的封装

隔离层就是我们的代理对象,而真实对象就是我们的封装层,当我们发起网络请求的时候,业务并不需要关心具体使用的是哪个网络框架来发起请求,只需要操作隔离层的 HttpRequest 就行,当我们需要用 Volley 的时候,就只需要把我们的 Volley 的封装层实现赋值给隔离层的 HttpRequest 即可,那么就会使用 Volley 发起网络请求,如果要切换到 OkHttp,那么就只需要创建 OKHttp 的实现并赋值给隔离层的 HttpReqeust 即可;

动态代理

通过一个代理类,来实现全部的代理功能;在运行时再创建代理类和其实例,因此显然效率更低。要完成这个场景,需要在运行期动态创建一个Class。JDK提供了Proxy 来完成这件事;

基本使用

抽象角色

arduino 复制代码
interface Api {
    void test(String passName);
}

真实角色

typescript 复制代码
class ApiImpl implements Api {
    @Override    public void test(String passName) {
        System.out.println("真实实现:" + passName);
    }
}

创建真实实例角色

ini 复制代码
ApiImpl impl = new ApiImpl();

JDK动态代理实现

typescript 复制代码
Proxy.newProxyInstance(getClass().getClassLoader(), new Class[] {Api.class}, new InvocationHandler() {
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) {
        return Method.invoke(impl, args);
    }
})

实际上, Proxy.newProxyInstance 会创建一个 Class,与静态代理不同,这个 Class 不是由具体的 .java 源文件编译而来,即没有真正的文件,只是在内存中按照 Class 格式生成了一个Class。

动态代理的原理

Android SDK 中的 Proxy.java 中的具体实现

ini 复制代码
final Class<?> [] intfs = interfaces.clone(); // 对接口进行了一个拷贝,获取了一个 Class 对象的集合;
Class<?> cl = getProxyClass0(loader, intfs); // 获取一个 class
final Constructor<?> cons = cl.getConstuctor(constructorParams); // 获取这个 class 的构造函数
cons.newInstance(new Object[]{h}); // 对这个 class 进行实例化

所以:newProxyInstance() 返回的是一个 class 对象;

那么,这个 Class<?> cl 是怎么来的? 它里面的代码是什么样的呢?

那么这个 .class 的实例对象是怎么生成的?

这里要额外提到一点 类的生命周期

class 是由 javac 把 .java 文件编译成 .class,是一个实实在在存在的文件,存放在我们的硬盘中,而 动态代理 帮我们生成了一个 .class 是没有这个文件的,而是直接在内存中 一份实例化对象。

通过 ProxyGenerator.generateProxyClass 生成

ini 复制代码
String var23 = var16 + "$Proxy" + var19;
byte[] var22 = ProxyGenerator.generateProxyClass(var23, var2, var17);
try {    
    return Proxy.defineClass0(var1, var23, var22, 0, var22.length);
} catch (ClassFormatError var14) {    
    throw new IllegalArgumentException(var14.toString());
}

也就是说,我们将 byte[] var22 这个数组写入到一个文件中,就可以看到 动态代理 帮我们生成的 class 实例对象 都有哪些内容

ini 复制代码
String name = Api.class.getName()+"$Proxy0";
//生成代理指定接口的Class数据
byte[] bytes = ProxyGenerator.generateProxyClass(name, new Class[]{Api.class});
FileOutputStream fos = new FileOutputStream("lib/" + name+".class");
fos.write(bytes);fos.close();

生成的文件中可以查看我们的代理类

在初始化时,获得method 备用。而这个代理类中所有方法的实现变为:

这里的 h 其实就是 InvocationHandler 接口,所以我们在使用动态代理时,传递的InvocationHandler 就是一个监听,在代理对象上执行方法,都会由这个监听回调出来。

手写Retrofit实现

Retrofit 核心实现主要分为四部分注解、Retrofit、ServiceMethod、Parameter

Retrofit的核心实现主要是 动态代理、注解解析、构建请求参数、封装Call.Factory发起网络请求。

Retorfit时序图如下:

注解

手写只实现了 @POST、@GET、@QUERY、@FIELD

java 复制代码
@Target(METHOD)
@Retention(RUNTIME)
public @interface POST {    
    String value() default "";
}

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

@Target(PARAMETER)
@Retention(RUNTIME)
public @interface Query {    
    String value();
}

@Target(PARAMETER)
@Retention(RUNTIME)
public @interface Field {    
    String value();
}

动态代理 MyRetrofit

OKHttp 发起网络请求,主要依靠的就是 Call.Factory HttpUrl

Call.Factory 提供了newCall方法用来发起请求,HttpUrl 用来构建请求参数和请求方式

MyRetrofit 也参考 Retorfit 采用 建造者模式实现,主要用来让调用方传递构建的 Call.Factory 和 HttpUrl,以及动态代理创建代理对象,调用 ServiceMethod 用来解析注解并封装请求参数;

kotlin 复制代码
public class MyRetrofit {    
    // 参考 Retrofit 的实现,也增加一层缓存
    final Map<Method, ServiceMethod> serviceMethodCache = new ConcurrentHashMap<>();    
    // 使用 OKhttp 提供的 Call.Factory
    final Call.Factory callFactory;  
    // 使用 OKHttp 提供的 HttpUrl      
    final HttpUrl baseUrl;
    
    MyRetrofit(Call.Factory callFactory, HttpUrl baseUrl) {                this.callFactory = callFactory;        
        this.baseUrl = baseUrl;    
    }
    
    // 参考 Retrofit 的动态代理  
    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;        
        //Okhttp->OkhttClient        
        private okhttp3.Call.Factory callFactory;  
        //null        
        public Builder callFactory(okhttp3.Call.Factory factory) {            
            this.callFactory = factory;            
            return this;        
        }        
        
        public Builder baseUrl(String baseUrl) {            
            this.baseUrl = HttpUrl.get(baseUrl);            
            return this;        
        }        
        
        public MyRetrofit build() {            
            if (baseUrl == null) {                
                throw new IllegalStateException("Base URL required.");            
            }            
            okhttp3.Call.Factory callFactory = this.callFactory;            
            if (callFactory == null) {                
                callFactory = new OkHttpClient();            
            }            
            return new EnjoyRetrofit(callFactory, baseUrl);        
        }    
    }
}

ServiceMethod

用来解析注解,记录请求类型,参数、完整地址

ini 复制代码
public class ServiceMethod {    
    private final Call.Factory callFactory;    
    private final String relativeUrl;    
    private final boolean hasBody;    
    private final ParameterHandler[] parameterHandler;    
    private FormBody.Builder formBuild;    
    HttpUrl baseUrl;    
    String httpMethod;    
    HttpUrl.Builder urlBuilder;    
    public ServiceMethod(Builder builder) {        
        baseUrl = builder.enjoyRetrofit.baseUrl;        
        callFactory = builder.enjoyRetrofit.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) {        
        /**         
          * 1  处理请求的地址与参数         
          */        
        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);        
        }        
        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 enjoyRetrofit;        
        private final Annotation[] methodAnnotations;        
        private final Annotation[][] parameterAnnotations;        
        ParameterHandler[] parameterHandler;        
        private String httpMethod;        
        private String relativeUrl;        
        private boolean hasBody;        
        
        public Builder(MyRetrofit enjoyRetrofit, Method method) {            
            this.enjoyRetrofit = enjoyRetrofit;            
            //获取方法上的所有的注解            
            methodAnnotations = method.getAnnotations();            
            //获得方法参数的所有的注解 (一个参数可以有多个注解,一个方法又会有多个参数)            
            parameterAnnotations = method.getParameterAnnotations();        
        }        

        public ServiceMethod build() {            
            /**             
              * 1 解析方法上的注解, 只处理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;                
                }
                // 待处理其他注解            
            }            
            /**             
              * 2 解析方法参数的注解             
              */            
            int length = parameterAnnotations.length;            
            parameterHandler = new ParameterHandler[length];            
            for (int i = 0; i < length; i++) {                
                // 一个参数上的所有的注解                
                Annotation[] annotations = parameterAnnotations[i];                
                // 处理参数上的每一个注解                
                for (Annotation annotation : annotations) {                    
                    //todo 可以加一个判断:如果httpMethod是get请求,现在又解析到 Filed 注解,可以提示使用者使用 Query 注解                    
                    if (annotation instanceof Field) {                        
                        //得到注解上的value: 请求参数的key                        
                        String value = ((Field) annotation).value();                        
                        parameterHandler[i] = new ParameterHandler.FiledParameterHandler(value);                    
                    } else if (annotation instanceof Query) {                        
                        String value = ((Query) annotation).value();                        
                        parameterHandler[i] = new ParameterHandler.QueryParameterHandler(value);                    
                    }                
                }            
            }            
            return new ServiceMethod(this);        
        }    
    }
}

ParameterHandler

QUERY、FIELD 请求参数封装,桥接到 HttpUrl 的addQueryParameter formBuild.add

scala 复制代码
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;        
        }        
        //serviceMethod: 回调        
        @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);        
        }    
     }
}

简历润色

简历中可写上:深度理解 Retofit 实现原理,理解 注解、反射、动态代理,可手写 Retofit 核心实现

相关推荐
太空漫步112 小时前
android社畜模拟器
android
海绵宝宝_4 小时前
【HarmonyOS NEXT】获取正式应用签名证书的签名信息
android·前端·华为·harmonyos·鸿蒙·鸿蒙应用开发
凯文的内存6 小时前
android 定制mtp连接外设的设备名称
android·media·mtp·mtpserver
天若子6 小时前
Android今日头条的屏幕适配方案
android
林的快手8 小时前
伪类选择器
android·前端·css·chrome·ajax·html·json
望佑8 小时前
Tmp detached view should be removed from RecyclerView before it can be recycled
android
xvch10 小时前
Kotlin 2.1.0 入门教程(二十四)泛型、泛型约束、绝对非空类型、下划线运算符
android·kotlin
人民的石头14 小时前
Android系统开发 给system/app传包报错
android
yujunlong391914 小时前
android,flutter 混合开发,通信,传参
android·flutter·混合开发·enginegroup
rkmhr_sef14 小时前
万字详解 MySQL MGR 高可用集群搭建
android·mysql·adb