通过二次封装 Intent 了解 Retrofit 原理

2,204 阅读6分钟

前言

网上有很多流水线式的分析 Retrofit 原理,个人感觉比较枯燥乏味

所以这次通过二次封装 Intent 实现一个简单路由来学习 Retrofit

阅读这篇你能获得什么

  • 通过实践进一步的了解 Retrofit 原理
  • 运用设计模式封装,重复且无技术含量的代码

正文

一、基本使用流程

1、定义意图 API,描述意图跳转(注意:接口上面的注解表示是否检查登录状态如:未登录跳转至登录页面,方法上面的注解表示意图的目标页面,返回类型默认为 IntentCall,方法的参数即是意图的参数)

@CheckLogin
public interface CheckLoginStartActivityApi {

    @TargetPage(PayActivity.class)
    IntentCall startPayActivity(@Extra("key") String key);
}

2、创建 ERouter 并生成 API 的实现

// ERouter构建过程
ERouter router = new ERouter.Builder()
        .setLoginActivityClass(LoginActivity.class)
        .setLoginLogic(() -> LoginStatus.getLoginStatus())
        .addRouterAdapterFactory(new MyRouterAdapterFactory())
        .build();
        
// 动态代理的方式创建接口实现类过程  
CheckLoginStartActivityApi startActivity = router.create(CheckLoginStartActivityApi.class);

3、调用 API 方法,生成 IntentCall,执行

IntentCall intentCall = startActivity.startPayActivity("name");
// 获取 Intent
intentCall.getIntent();
// 启动 Intent
intentCall.startIntent();

//PayActivity.Class
textView.setText(getIntent().getStringExtra("key"));

二、ERouter 动态代理创建接口实现类过程

1、create 方法

public <T> T create(final Class<T> service) {

    // 解析接口上面的注解
    parseTypeAnnotations(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 就是通过动态代理对象经过反射拿到 CheckLoginStartActivityApi.startPayActivity 方法对象
            // 调用 loadStartActivityMethod 解析 method 对象方法上的注解和参数注解
            StartActivityMethod startActivityMethod = loadStartActivityMethod(method);

            return startActivityMethod.invoke(getTypeParameter(proxy), args);
        }
    });
}

2、Proxy.newProxyInstance 方法

Proxy.newProxyInstance 源码其实比较有意思

在 Android SDK 中是通过调用 native 方法,动态生成代理类性能会比较高效

在 Java 8 源码中使用的是 ProxyGenerator.generateProxyClass 生成代理类

//Java 8 源码
//Proxy 的内部类 ProxyClassFactory
@Override
public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {

    ...

    /*
     * Choose a name for the proxy class to generate.
     */
    long num = nextUniqueNumber.getAndIncrement();
    String proxyName = proxyPkg + proxyClassNamePrefix + num;

    // 重点,生成一个代理类
    byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
            proxyName, interfaces, accessFlags);
    try {
        return defineClass0(loader, proxyName,
                proxyClassFile, 0, proxyClassFile.length);
    } catch (ClassFormatError e) {
        /*
         * A ClassFormatError here means that (barring bugs in the
         * proxy class generation code) there was some other
         * invalid aspect of the arguments supplied to the proxy
         * class creation (such as virtual machine limitations
         * exceeded).
         */
        throw new IllegalArgumentException(e.toString());
    }
}

可以发现在 ProxyClassFactory 类中的 apply 方法调用了 ProxyGenerator.generateProxyClass 返回了一个 byte[] proxyClassFile ,这个变量存的就是代理类字节码,直接祭出输出流

// 代理类的庐山真面目
// 稍微改动了一下,便于阅读
public final class CheckLoginStartActivityApiImpl extends Proxy implements CheckLoginStartActivityApi {

    // 反射获取的方法
    private static Method startPayActivity;

    public CheckLoginStartActivityApiImpl(InvocationHandler invocationHandler)  {
        super(invocationHandler);
    }

    public final IntentCall startPayActivity(String key)   {
        try {
            // h = invocationHandler 就是之前 Proxy.newProxyInstance 传入的 new InvocationHandler()
            // 外部调用startPayActivity 方法就会回调给 InvocationHandler 接口的 invoke 方法
            // 有没有一种切面(AOP)的感觉?
            return (IntentCall)super.h.invoke(this, startPayActivity, new Object[]{key});
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }
    
    static {
        try {
            // 反射获取方法
            startPayActivity = Class.forName("com.dongchao.sample.CheckLoginStartActivityApi").getMethod("startPayActivity", Class.forName("java.lang.String"));
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}

3、代码执行时序图

// 动态代理的方式创建接口实现类过程 
CheckLoginStartActivityApi startActivity = router.create(CheckLoginStartActivityApi.class);
IntentCall intentCall = startActivity.startPayActivity("name");

动态代理流程图.png

4、小结

router.create 方法中调用 Proxy.newProxyInstance 动态创建代理类 CheckLoginStartActivityApiImpl 加载到内存,接着获取它的 Class 反射调用它构造函数传入 InvocationHandler

startActivity.startPayActivity = CheckLoginStartActivityApiImpl.startPayActivity 执行 super.h.invoke 回调接口到 InvocationHandler.invoke 然后执行 loadStartActivityMethod.invoke 返回值就是接口定义的返回类型

特别感谢能看到这里的大佬们,如果有什么问题欢迎评论指出

bq.jpeg

三、ERouter 注解解析

synchronized 的使用

程序中合理使用 synchronized 能有效提升程序执行效率

StartActivityMethod loadStartActivityMethod(Method method) {

    // 获取缓存中已经解析过注解的方法,这也是 Retrofit 性能高效的原因
    StartActivityMethod result = startActivityMethodCache.get(method);
    // 避免非必要加锁,提示执行效率
    if (result != null) {
        return result;
    }

    // 这里加锁的意义,为了避免多线程下 StartActivityMethod.parseAnnotations 多次执行,影响性能(反射比较耗时)
    synchronized (startActivityMethodCache) {
        result = startActivityMethodCache.get(method);
        if (result == null) {
            result = StartActivityMethod.parseAnnotations(this, method);
            startActivityMethodCache.put(method, result);
        }
    }

    return result;
}

接着看 StartActivityMethod.parseAnnotations 内部

// StartActivityMethod.class
static StartActivityMethod parseAnnotations(ERouter router, Method method) {
    
    // 真正去做解析的类
    RequestParameterFactory requestBody = RequestParameterFactory.parseAnnotations(router, method);

    return RouterStartActivityMethod.parseAnnotations(router, method, requestBody);
}

// RequestParameterFactory.class
static RequestParameterFactory parseAnnotations(ERouter router, Method method) {
    return new Builder(router, method).build();
}

继续看 new Builder(router, method).build()

RequestParameterFactory build() {
    //方法注解解析
    for (Annotation annotation : methodAnnotations) {
        if (annotation instanceof TargetPage) {
            TargetPage targetPage = (TargetPage) annotation;
            // 获取到方法上 TargetPage 注解的值
            targetClass = targetPage.value();
            checkLogin = targetPage.checkLogin();
        }
    }

    //参数注解解析
    for (int p = 0; p < parameterCount; p++) {
        parameterKeys[p] = parseParameter(parameterAnnotationsArray[p]);
    }
    return new RequestParameterFactory(this);
}

//参数注解解析
String parseParameter(Annotation[] annotations) {
    String result = null;
    if (annotations != null) {
        // 如果方法参数没有用注解那么 annotations 为空数组
        for (Annotation annotation : annotations) {
            String key = parseParameterAnnotation(annotation);
            if (TextUtils.isEmpty(key)) {
                continue;
            }

            if (!TextUtils.isEmpty(result)) {
                throw new RuntimeException("当前参数有多个注解, 只允许存在一个");
            }
            result = key;
        }
    }

    if (TextUtils.isEmpty(result)) {
        throw new RuntimeException("参数无注解或注解内容为空");
    }
    return result;
}

String parseParameterAnnotation(Annotation annotation) {
    if (annotation instanceof Extra) {
        Extra extra = (Extra) annotation;
        // 获取到参数 Extra 注解上的值
        return extra.value();
    }
    return null;
}
解析接口方法上的注解和参数上的注解和 Retrofit 解析注解原理基本无差别

解析结束后返回 RequestParameterFactory 对象 接着看 RouterStartActivityMethod.parseAnnotations

// RouterStartActivityMethod.class
static RouterStartActivityMethod parseAnnotations(ERouter router, Method method,
                                                  RequestParameterFactory requestBody) {

    // 通过返回值类型,遍历符合要求的适配器
    RouterAdapter routerAdapter = router.routerAdapter(method.getReturnType());
    // 创建 RouterStartActivityMethod 的子类,传入解析的注解数据和适配器
    return new IntentAdapted(requestBody, routerAdapter);
}

public RouterAdapter<?> routerAdapter(Type returnType) {
    for (int i = 0; i < routerAdapterFactories.size(); i++) {
        RouterAdapter<?> adapter = routerAdapterFactories.get(i).get(returnType);
        if (adapter != null) {
            return adapter;
        }
    }
    throw new IllegalArgumentException("没有合适的 routerAdapter");
}

最终返回 IntentAdapted 实例

小结

原理就是构建一个 StartActivityMethod(IntentAdapted)子类实例,有两个成员变量 RequestParameterFactory 和 RouterAdapter

前者保存着解析出的注解信息,后者负责创建接口方法返回值也就是 IntentCall 的实现类(ExecutorIntentCall)

四、ERouter 构建 Intent

StartActivityMethod.invoke 方法解析

// startActivityMethod = IntentAdapted 实例
startActivityMethod.invoke(getTypeParameter(proxy), args)

// RouterStartActivityMethod.class
// 执行 RouterStartActivityMethod.invoke 方法,RouterStartActivityMethod 是 IntentAdapted 的父类
@Override
final ReturnT invoke(TypeParameter typeParameter, Object[] args) {
    // 里面通过建造者模式创建 JumpIntent
    JumpIntent jumpIntent = requestBody.createJumpIntent(typeParameter, args);
    return adapt(jumpIntent);
}

// RequestParameterFactory.class
JumpIntent createJumpIntent(TypeParameter typeParameter, Object[] args) {

    boolean isCheckLogin;

    if (typeParameter != null) {
        isCheckLogin = typeParameter.isCheckLogin ? true : checkLogin;
    } else {
        isCheckLogin = checkLogin;
    }

    int argumentCount = args == null ? 0 : args.length;

    if (parameterKeys.length != argumentCount) {
        throw new IllegalArgumentException("注解和方法参数不匹配");
    }
    Map<String, Object> parameterMap = new ArrayMap(argumentCount);

    // 注解 key 和 value 参数放入 ArrayMap 
    // 在 JumpIntent.putExtra 方法中实现 Intent 传递参数
    for (int i = 0; i < argumentCount; i++) {
        parameterMap.put(parameterKeys[i], args[i]);
    }

    return new JumpIntent.Builder()
            .setLoginActivityClass(loginActivityClass)
            .setTargetClass(targetClass)
            .setCheckLogin(isCheckLogin)
            .setLoginLogic(loginLogic)
            .setParameterMap(parameterMap)
            .build();
}

JumpIntent 是 IntentCall 的实现类

public class JumpIntent implements IntentCall {

    ...

    public Intent getIntent() {
        if (currentContext == null) {
            return null;
        }

        if (intent == null) {
            if (isCheckLogin) {
                intent = getIntentLoginOrTarget();
            } else {
                intent = new Intent(currentContext, targetClass);
            }
            //设置参数
            putExtra(intent);
        }
        return intent;
    }

    @Override
    public boolean startIntent() {
        Intent intent = getIntent();
        if (intent == null) {
            return false;
        }
        currentContext.startActivity(intent);
        return true;
    }
}

代码比较简单,就贴稍微重点一些的

接下来看 IntentAdapted.adapt 方法


static final class IntentAdapted<ReturnT> extends RouterStartActivityMethod<ReturnT> {

    ...
    
    @Override
    protected ReturnT adapt(IntentCall intentCall) {
        // 把 JumpIntent 传入适配器
        return (ReturnT) routerAdapter.adapt(intentCall);
    }
}


public class DefaultRouterAdaptedFactory extends RouterAdapter.Factory {

    @Override
    public RouterAdapter get(Type returnType) {
        if (getRawType(returnType) == IntentCall.class) {
            return new RouterAdapter() {
                // routerAdapter.adapt(intentCall) 其实就是走到了这里
                @Override
                public IntentCall adapt(IntentCall intentCall) {
                    // intentCall 就是 JumpIntent 对象
                    return new ExecutorIntentCall(intentCall);
                }
            };
        }
        return null;
    }

    // 静态代理对象
    static final class ExecutorIntentCall implements IntentCall {

        private static final String TAG = "ExecutorIntentCall";
        //目标对象 IntentCall = JumpIntent
        private final IntentCall delegate;
        private static Handler mHandler;

        public ExecutorIntentCall(IntentCall delegate) {
            this.delegate = delegate;
            this.mHandler = new Handler(Looper.getMainLooper());
        }

        @Override
        public Intent getIntent() {
            AppLog.i(TAG, "delegate getIntent start");
            // 在调用目标对象方法前可以做一些业务如:埋点、快速点击的逻辑判断,防止创建多个 Activity 实例的问题
            Intent intent = delegate.getIntent();
            AppLog.i(TAG, "delegate getIntent end");
            return intent;
        }

        @Override
        public boolean startIntent() {
            AppLog.i(TAG, "delegate startIntent start");
            runInMainThread(() -> delegate.startIntent());
            AppLog.i(TAG, "delegate startIntent end");
            return true;
        }

        private void runInMainThread(Runnable runnable) {
            if (Looper.getMainLooper().getThread() != Thread.currentThread()) {
                mHandler.post(runnable);
            } else {
                runnable.run();
            }
        }

    }

routerAdapter.adapt(intentCall) 的 routerAdapter 就是 DefaultRouterAdaptedFactory.get 通过工程模式创建的 RouterAdapter 对象实例,然后调用它的 adapt 创建 ExecutorIntentCall 静态代理对象,持有着 intentCall (也就是 JumpIntent 实例) ,为了方便在调用目标对象方法前可以做一些业务如:埋点或快速点击的逻辑判断,防止创建多个 Activity 实例的问题等

截屏2022-07-31 下午11.png

放张图可能更加清晰点

Retrofit 默认的适配器,就是通过静态代理对象把请求结果回调到主线程

我这里通过 Handler 把启动 Activity 放到主线程中执行,算是小小的致敬

总结

到这里也算是结束了

Retrofit 是 Android 最热门的框架,熟悉其原理和流程还是非常有必要的,之前也看过源码,但是没有深入了解他的实现原理。经过实践后发现,不得不佩服 Retrofit 作者为了保证易用性和扩展性而引入大量的思考所设计的框架。

源码地址