代理模式实现Android路由框架

Tags
Star
#Android
在项目的开发过程中,我们可能会遇到一些很重的App,涉及到很多业务线,N个团队的共同开发。这个时候,如果还是单app的开发方式的话,可能会导致开发中的编译时间很长,每个团队之间的代码互相存在干扰。这个时候,可以考虑组件化开发。
所谓组件化开发,其实主要就是将一个module拆分成多个module,每个团队负责自己的module,在发布时再将所有的module合并到一起发布。
这种分module的方式比起普通的分模块的方式的好处就在于:不同的module之间是互相隔离的,只能通过接口访问,能更好的保证:高内聚低耦合。开发时,每个module都可以独立打包安装,编译时间短。各个module的res资源是隔离的,不像以前都是混在一起,一旦哪个module不需要了,很方便将代码和资源文件一起干掉,避免代码腐烂。
上面说了组件化开发,那路由框架和组件化开发有什么关系呢?上面我们知道,不同的组件之间是互相隔离的,那当需要从一个组件的页面跳转到另一个组件的页面时,该怎么办呢?这个时候就需要路由框架了,组件之间不直接打交道,而是通过路由来转发。这样方便我们实行动态跳转、拦截、统一转发等额外操作。
下面是我模仿Retrofit的代理模式来实现的一套简单的Android路由框架。主要是参考的Android轻量级路由框架LiteRouter,不过个别地方根据自己的习惯做了点改动。虽然是模仿,但在自己敲的过程中,对Retrofit的代理模式又有了更深的理解,同时,LiteRouter的拦截器很简单,只有一个,我这里又模仿OkHttp的责任链模式对它的拦截器做了简单的拓展,支持多个拦截器,在实现的过程中,又过了一遍OkHttp的源码,受益匪浅。不得不说,多看源码真的挺有好处的,大家不妨也去看一看,第一次看不懂很正常,多看几次就有感觉了,码读百遍,其意自现。

使用方法

1、定义接口

/**
 * 定义Intent之间跳转的协议
 * <p>
 * 作者:余天然 on 2017/5/25 上午11:21
 */
public interface IntentService {

    @ClassName("com.soubu.routerdemo.Demo1Activity")
    void gotoDemo1(Context context);

    @ClassName("com.soubu.routerdemo2.Demo2Activity")
    void gotoDemo2(Context context, @Key("request") String request, @Key("response") String response);

}

2、创建Router实例,可以按需要添加拦截器

package com.soubu.routerhost;

import com.soubu.router.IntentRouter;

/**
 * 作者:余天然 on 2017/5/25 下午5:43
 */
public class IntentClient {

    public static IntentClient instance;

    private IntentRouter intentRouter;

    /**
     * 单例
     */
    public static IntentClient getInstance() {
        if (instance == null) {
            instance = new IntentClient();
        }
        return instance;
    }

    /**
     * 创建页面跳转辅助类
     */
    private IntentClient() {
        //通过动态代理,获得具体的跳转协议实现类,可自定义拦截器
        intentRouter = new IntentRouter.Builder()
                .addInterceptor(new Interceptor1())
                .addInterceptor(new Interceptor2())
                .addInterceptor(new Interceptor3())
                .build();
    }

    /**
     * 创建各个模块的页面接口
     */
    public IntentService intentService() {
        return intentRouter.create(IntentService.class);
    }

}
拦截器,可以统一对Intent做一些额外的处理
/**
 * Intent拦截器
 * 
 * 作者:余天然 on 2017/5/25 下午4:05
 */
public class Interceptor1 implements Interceptor {
    @Override
    public IntentWrapper intercept(Chain chain) {
        IntentWrapper request = chain.request();
        LogUtil.print("处理请求-1");
        Bundle reqExtras = request.getIntent().getExtras();
        reqExtras.putString("request", "1");
        request.getIntent().putExtras(reqExtras);
        IntentWrapper response = chain.process(request);
        LogUtil.print("处理响应-1");
        Bundle respExtras = response.getIntent().getExtras();
        respExtras.putString("response", "1");
        response.getIntent().putExtras(respExtras);
        return response;
    }
}

3、在每个module中,通过Router进行跳转

IntentClient.getInstance()
        .intentService()
        .gotoDemo1(activity);

源码流程

在创建Router实例时,通过动态代理创建了我们定义的跳转接口的实现类。
/**
 * 作者:余天然 on 2017/5/25 上午11:06
 */
public class IntentRouter {

    private List<Interceptor> interceptors;

    IntentRouter(List<Interceptor> interceptors) {
        this.interceptors = interceptors;
    }

    /**
     * create router class service
     *
     * @param service router class
     * @param <T>
     * @return
     */
    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, final Method method, final Object... args) throws Throwable {
                        //责任链模式,方便定制拦截器
                        Parser parser = new Parser();
                        IntentWrapper request = parser.parse(method, args);
                        Interceptor.Chain chain = new RealInterceptorChain(request, interceptors, 0);
                        IntentWrapper response = chain.process(request);
                        response.start();
                        //设置返回值,不过感觉没卵用
                        Class returnTYpe = method.getReturnType();
                        if (returnTYpe == void.class) {
                            return null;
                        } else if (returnTYpe == IntentWrapper.class) {
                            return response;
                        }
                        throw new RuntimeException("method return type only support 'void' or 'IntentWrapper'");
                    }
                });
    }

    public static final class Builder {
        private List<Interceptor> interceptors;

        public Builder() {
            this.interceptors = new ArrayList<>();
        }

        public Builder addInterceptor(Interceptor interceptor) {
            this.interceptors.add(interceptor);
            return this;
        }

        public IntentRouter build() {
            interceptors.add(new DefaultInterceptor());
            
在动态代理的invoke方法中,通过一个反射解析器来获取接口的方法的注解信息,根据这些注解自动帮其创建Intent,避免重复的体力活。
/**
 * 作者:余天然 on 2017/5/25 上午11:56
 */
public class Parser {

    private int requestCode;

    private String className;
    private int mFlags;
    private Bundle bundleExtra;

    private Context context;

    public Parser() {

    }

    public Parser addFlags(int flags) {
        mFlags |= flags;
        return this;
    }

    public IntentWrapper parse(Method method, Object... args) {
        // 解析方法注解
        parseMethodAnnotations(method);
        // 解析参数注解
        parseParameterAnnotations(method, args);
        //创建Intent
        Intent intent = new Intent();
        intent.setClassName(context, className);
        intent.putExtras(bundleExtra);
        intent.addFlags(mFlags);
        requestCode = method.isAnnotationPresent(RequestCode.class) ? requestCode : -1;
        return new IntentWrapper(context, intent, requestCode);
    }

    /**
     * 解析参数注解
     */
    private void parseParameterAnnotations(Method method, Object[] args) {
        //参数类型
        Type[] types = method.getGenericParameterTypes();
        // 参数名称
        Annotation[][] parameterAnnotationsArray = method.getParameterAnnotations();
        bundleExtra = new Bundle();
        for (int i = 0; i < types.length; i++) {
            // key
            String key = null;
            Annotation[] parameterAnnotations = parameterAnnotationsArray[i];
            for (Annotation annotation : parameterAnnotations) {
                if (annotation instanceof Key) {
                    key = ((Key) annotation).value();
                    break;
                }
            }
            parseParameter(bundleExtra, types[i], key, args[i]);
        }
        if (context == null) {
            throw new RuntimeException("Context不能为空");
        }
    }

    /**
     * 解析方法注解
     */
    void parseMethodAnnotations(Method method) {
        Annotation[] methodAnnotations = method.getAnnotations();
        for (Annotation annotation : methodAnnotations) {
            if (annotation instanceof Clas
    /**
     * 获取返回类型
     *
     * @param type
     * @return
     */
    Class<?> getRawType(Type type) {
        if (type == null) throw new NullPointerException("type == null");

        if (type instanceof Class<?>) {
            // Type is a normal class.
            return (Class<?>) type;
        }
        if (type instanceof ParameterizedType) {
            ParameterizedType parameterizedType = (ParameterizedType) type;

            // I'm not exactly sure why getRawType() returns Type instead of Class. Neal isn't either but
            // suspects some pathological case related to nested classes exists.
            Type rawType = parameterizedType.getRawType();
            if (!(rawType instanceof Class)) throw new IllegalArgumentException();
            return (Class<?>) rawType;
        }
        if (type instanceof GenericArrayType) {
            Type componentType = ((GenericArrayType) type).getGenericComponentType();
            return Array.newInstance(getRawType(componentType), 0).getClass();
        }
        if (type instanceof TypeVariable) {
            // We could use the variable's bounds, but that won't work if there are multiple. Having a raw
            // type that's more general than necessary is okay.
            return Object.class;
        }
        if (type instanceof WildcardType) {
            return getRawType(((WildcardType) type).getUpperBounds()[0]);
        }

        throw new IllegalArgumentException("Expected a Class, ParameterizedType, or "
                + "GenericArrayType, but <" + type + "> is of type " + type.getClass().getName());
    }
这里的Intent我是用一个IntentWrapper包装了一下,主要就是传入了context对象,方便startActivity等方法的统一调用
public class IntentWrapper {

    private Context context;
    private Intent intent;
    private int requestCode = -1;

    public IntentWrapper(Context context, Intent intent, int requestCode) {
        this.context = context;
        this.intent = intent;
        this.requestCode = requestCode;
    }

    public Intent getIntent() {
        return intent;
    }

    public Context getContext() {
        return context;
    }

    public int getRequestCode() {
        return requestCode;
    }

    public void start() {
        if (requestCode == -1) {
            startActivity();
        } else {
            startActivityForResult(requestCode);
        }
    }

    public void startActivity() {
        if (!(context instanceof Activity)) {
            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        }
        context.startActivity(intent);
    }

    public void startActivityForResult(int requestCode) {
        if (!(context instanceof Activity)) {
            throw new RuntimeException("startActivityForResult only works for activity context");
        }
        ((Activity) context).startActivityForResult(intent, requestCode);
    }

}
其实不要拦截器,也是完全可以的。但是为了拥抱变化(其实是为了装逼),我们对Parser创建的IntenWrapper又做了一点点加工,在中间添加了一道责任链,虽然这里输入的是IntenWrapper,输出的也是IntenWrapper,但是,这中间我们是可以自己做一点额外的处理的,比如给每个Intent添加一个额外的参数、统一修改Intent的信息。
为了大家更对OkHttp中的责任链有一个更直观的感受,我这里插播一张图
notion image
看上图,我们可以定义多个拦截器:RetryAndFollowupInterceptor来负责失败重试和重定向、BridgeInterceptor来处理request和response的header里面的信息、CacheInterceptor来实现各种缓存策略、ConnectInterceptor来建立与服务器的连接、CallServerInterceptor来与服务器交互数据。这几个其实是OkHttp自带的拦截器,其实我们在开发时,通常会额外的加几个自定义的拦截器,比如对token的统一处理啊、网络日志的打印啊等等。
拦截器的设计,可以像工厂流水线一样,传递用户发起的请求 Request,每一个拦截器完成相应的功能,从失败重试和重定向实现、请求头的修改和Cookie 的处理,缓存的处理,建立 TCP 和 SSH 连接,发送 Request 和读取 Response,每一个环节由专门的 Interceptor 负责。
定义Interceptor和Chain的接口。
public interface Interceptor {
    IntentWrapper intercept(Chain chain);

    interface Chain {
        IntentWrapper request();

        IntentWrapper process(IntentWrapper request);
    }
}
自定义RealInterceptorChain实现Chain接口,其实就是内部维护了一个List,源码比较简单,主要功能就是将多个Interceptor合并成一串。
/**
 * 作者:余天然 on 2017/5/25 下午4:35
 */
public class RealInterceptorChain implements Interceptor.Chain {

    private IntentWrapper originalRequest;
    private List<Interceptor> interceptors;
    private int index;

    public RealInterceptorChain(IntentWrapper request, List<Interceptor> interceptors, int index) {
        this.originalRequest = request;
        this.interceptors = interceptors;
        this.index = index;
    }

    @Override
    public IntentWrapper request() {
        return originalRequest;
    }

    @Override
    public IntentWrapper process(IntentWrapper request) {
        Interceptor interceptor = interceptors.get(index);
        RealInterceptorChain next = new RealInterceptorChain(request, interceptors, index + 1);
        IntentWrapper response = interceptor.intercept(next);
        return response;
    }
}
默认的拦截器,其实啥都没做,主要就是方便RealInterceptorChain中至少有一个拦截器可以传输数据,但是总觉得这里有点怪,大家要是有更好的方法的话可以省掉这个类的话,欢迎提出来。
public class DefaultInterceptor implements Interceptor {

    @Override
    public IntentWrapper intercept(Chain chain) {
        return chain.request();
    }

}
在我们定义接口的时候,其实还用到了几个注解:@ClassName、@Key、@RequestCode。
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ClassName {
    String value();
}
这里只发一个吧,另外两个和它除了名字不一样都是一样的,就不发了,免得大家说我凑字数。
以上就是这个路由框架的实现思路了,其实这个框架的实用性不是很大,因为现在阿里巴巴已经开源了一个ARouter,这个比我们这个更好一点,它没有反射,也没有接口定义类,而是在每个Activity上添加注解,然后通过apt在编译时获取这些注解信息,动态创建一个辅助类来实现路由的。我们这个框架是用的运行时注解+反射,性能上比不上编译时注解,并且,我们的接口定义类不知道放在哪个module比较好,它依赖了router框架,同时又被所有的module都依赖。
本文档由瓦雀创建
 

© fishyer 2022