利用APT实现android路由框架二

上一篇APT跳转已经完成,这一篇开始处理传值和封装跳转。首先来进行传值的封装。
我们参考下butterknife:
在Activity的onCreate()使用ButterKnife.bind(this);在bind方法中会执行findBindingConstructorForClass()方法,在改方法中会拼接生成MainActivity_ViewBinding,最终赋值。

image.png

  @UiThread
  public MainActivity_ViewBinding(MainActivity target, View source) {
    this.target = target;
    target.bottomView = Utils.findRequiredViewAsType(source, R.id.bottom_view, "field 'bottomView'", BottomNavigationView.class);
    target.tv_iknow = Utils.findRequiredViewAsType(source, R.id.tv_iknow, "field 'tv_iknow'", TextView.class);
    target.layout_shadow = Utils.findRequiredView(source, R.id.layout_shadow, "field 'layout_shadow'");
  }

我们可以参考butterKnife来进行传值,生成类似于MainActivity_ViewBinding的类然后进行赋值,先看一我们想要达到的效果

public class NextActivity$$Parameter implements ParameterData {
  @Override
  public void getParameter(Object targetParameter) {
    NextActivity t = (NextActivity)targetParameter;
    t.numberId = t.getIntent().getStringExtra("numberId");
    t.name = t.getIntent().getStringExtra("name");
    t.age = t.getIntent().getIntExtra("age", t.age);
  }
}

开始撸码
1、先定义注解

@Target(ElementType.FIELD)//
@Retention(RetentionPolicy.CLASS)
public @interface Parameter {
    String name() default "";
}

2、在自己module中使用

@ARouter(path = "/user/User_LoginAcitivty")
public class User_LoginAcitivty extends AppCompatActivity {

    @Parameter
    String numberId;

    @Parameter
    int age;

    @Parameter
    UserBean userBean;

    @Parameter
    List<String> data;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.user_login_layout);

3、定义ParameterProcessor实现AbstractProcessor生成XXXX$$Parameter

@AutoService(Processor.class)
@SupportedSourceVersion(SourceVersion.RELEASE_8)
@SupportedAnnotationTypes(ProcessorConfig.PARAMETER_PACKAGE)
public class ParameterProcessor extends AbstractProcessor {

进行初始化

 @Override
    public synchronized void init(ProcessingEnvironment processingEnvironment) {
        super.init(processingEnvironment);
        elementTool = processingEnvironment.getElementUtils();
        filer = processingEnvironment.getFiler();
        messager = processingEnvironment.getMessager();
        typeTool = processingEnvironment.getTypeUtils();
    }

在process方法中生成文件

@Override
    public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
        if (!ProcessorUtils.isEmpty(set)) {
            Set<? extends Element> elements = roundEnvironment.getElementsAnnotatedWith(Parameter.class);
            if (!elements.isEmpty()) {//注解式作用在变量上,所以集合里存放的是声明的变量
                for (Element element : elements) {
                    TypeElement enclosingElement = (TypeElement) element.getEnclosingElement();//获取父节点 MainActivity
                    if (mParameterMap.containsKey(enclosingElement)) {
                        mParameterMap.get(enclosingElement).add(element);
                    } else {
                        List<Element> fieldsList = new ArrayList<>();
                        fieldsList.add(element);
                        mParameterMap.put(enclosingElement, fieldsList);
                    }
                }
                if (mParameterMap.isEmpty()) return true;
                TypeElement activityType = elementTool.getTypeElement(ProcessorConfig.ACTIVITY_PACKAGE);
                TypeElement parameterType = elementTool.getTypeElement(ProcessorConfig.AROUTER_AIP_PARAMETER_DATA);//com.htf.arouter_api.ParameterData
                /**
                 * 生成方法
                 * @Override
                 * public void getParameter(Object targetParameter) {
                 */
                ParameterSpec parameterSpec = ParameterSpec.builder(TypeName.OBJECT, ProcessorConfig.PARAMETER_NAME).build();
                /**
                 * 方法里的内容
                 */
                for (Map.Entry<TypeElement, List<Element>> entry : mParameterMap.entrySet()) {
                    TypeElement typeElement = entry.getKey();
                    //先判断是不是在Activity
                    if (!typeTool.isSubtype(typeElement.asType(), activityType.asType())) {
                        throw new RuntimeException("@Parameter注解目前仅限用于Activity类之上");
                    }

                    ClassName className = ClassName.get(typeElement);
                    MethodSpec.Builder methodBuilder = MethodSpec.methodBuilder(ProcessorConfig.PARAMETER_METHOD_NAME)
                            .addModifiers(Modifier.PUBLIC)
                            .addAnnotation(Override.class)
                            .addParameter(parameterSpec);
                    ParameterFactory parameterFactory = new ParameterFactory.Builder(methodBuilder)
                            .setClassName(className)
                            .setMessager(messager)
                            .setElementTool(elementTool)
                            .setTypeTool(typeTool)
                            .build();

                    parameterFactory.addFirstStatement();

                    for (Element element : entry.getValue()) {
                        parameterFactory.buildStatement(element);
                    }

                    /**
                     * 生成文件
                     */
                    String finalClassName = typeElement.getSimpleName().toString() + ProcessorConfig.PARAMETER_FILE_NAME;
                    try {
                        JavaFile.builder(className.packageName(),
                                TypeSpec.classBuilder(finalClassName)
                                        .addSuperinterface(ClassName.get(parameterType))
                                        .addModifiers(Modifier.PUBLIC)
                                        .addMethod(parameterFactory.build())
                                        .build())
                                .build()
                                .writeTo(filer);
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
        return false;
    }

先进行Parameter扫描,获取父节点然后判断添加,先写方法,再写内容,最后生成类文件,ParameterFactory使我们封装的用于添加内容的类,设计为构建者模式方便链式调用。

public class ParameterFactory {

    // 方法的构建
    private MethodSpec.Builder methodBuilder;

    private Messager messager;

    private ClassName className;// 类名,如:MainActivity

    private Types typeTool;

    private Elements elementTool; // 类信息

    private ParameterFactory(Builder builder) {
        this.messager = builder.messager;
        this.className = builder.className;
        this.typeTool = builder.typeTool;
        this.elementTool = builder.elementTool;
        this.methodBuilder = builder.methodBuilder;
    }

    public void addFirstStatement() {
        methodBuilder.addStatement("$T t = ($T)" + ProcessorConfig.PARAMETER_NAME, className, className);
    }

    public void buildStatement(Element element) {
        TypeMirror parcelableType = elementTool.getTypeElement(ProcessorConfig.PARCELABLE).asType();//  //判断是不是对象bean类型
        TypeMirror typeMirror = element.asType();//遍历注解的属性节点, 生成函数体
        int type = typeMirror.getKind().ordinal();//获取TypeKind枚举的序列号
        String fieldName = element.getSimpleName().toString();//获取属性名字 numberId 、name等
        String annotationValue = element.getAnnotation(Parameter.class).name();//获取注解值
        annotationValue = ProcessorUtils.isEmpty(annotationValue) ? fieldName : annotationValue;
        String finalValue = "t." + fieldName;
        String methodContent = finalValue + " = t.getIntent().";
        // TypeKind 枚举类型不包含String
        if (type == TypeKind.INT.ordinal()) {
            methodContent += "getIntExtra($S, " + finalValue + ")";  // Int有默认值
        } else if (type == TypeKind.DOUBLE.ordinal()) {
            methodContent += "getDoubleExtra($S, " + finalValue + ")";  // Double有默认值
        } else if (type == TypeKind.BOOLEAN.ordinal()) {
            methodContent += "getBooleanExtra($S, " + finalValue + ")";  // Boolean有默认值
        } else if (typeTool.isSubtype(typeMirror, parcelableType)) {
            methodContent += "getParcelableExtra($S)"; // 没有默认值
        } else if (typeMirror.toString().equals(ProcessorConfig.STRING)) {
            methodContent += "getStringExtra($S)"; // 没有默认值
        } else if (typeMirror.toString().equals(ProcessorConfig.LIST_STRING)) {
            methodContent += "getStringArrayListExtra($S)"; // 没有默认值
        } else {
            messager.printMessage(Diagnostic.Kind.ERROR, "目前暂支持String、int、boolean、List<String>、对象bean传参");
        }
        methodBuilder.addStatement(methodContent, annotationValue);
    }

    public MethodSpec build() {
        return methodBuilder.build();
    }

    /**
     * Builder构建者设计模式
     */
    public static class Builder {

        private Messager messager;

        private MethodSpec.Builder methodBuilder;

        private ClassName className;

        private Types typeTool;

        private Elements elementTool;

        public Builder(MethodSpec.Builder methodBuilder) {
            this.methodBuilder = methodBuilder;
        }

        public Builder setElementTool(Elements elementTool) {
            this.elementTool = elementTool;
            return this;
        }

        public Builder setTypeTool(Types typeTool) {
            this.typeTool = typeTool;
            return this;
        }

        public Builder setMessager(Messager messager) {
            this.messager = messager;
            return this;
        }

        public Builder setClassName(ClassName className) {
            this.className = className;
            return this;
        }

        public ParameterFactory build() {
            if (methodBuilder == null) {
                throw new IllegalArgumentException("方法为空");
            }
            if (messager == null) {
                throw new IllegalArgumentException("messager为空,Messager用来报告错误、警告和其他提示信息");
            }
            return new ParameterFactory(this);
        }
    }

在buildStatement方法中生成getIntent,build一下生成文件。

public class NextActivity$$Parameter implements ParameterData {
  @Override
  public void getParameter(Object targetParameter) {
    NextActivity t = (NextActivity)targetParameter;
    t.numberId = t.getIntent().getStringExtra("numberId");
    t.name = t.getIntent().getStringExtra("name");
    t.age = t.getIntent().getIntExtra("age", t.age);
  }
}

4、接收参数,封装一个管理类ParameterManager,用于给带Parameter注解的变量赋值

public class ParameterManager {

    private static ParameterManager instance;

    public static ParameterManager getInstance() {
        if (instance == null) {
            synchronized (ParameterManager.class) {
                if (instance == null) {
                    instance = new ParameterManager();
                }
            }
        }
        return instance;
    }

    // 为了寻找javapoet生成的User_LoginActivity + $$Parameter
    static final String FILE_SUFFIX_NAME = "$$Parameter";

    private LruCache<String, ParameterData> cache;

    private ParameterManager() {
        cache = new LruCache<>(100);//为了提高性能
    }

    public void loadParameter(Activity activity) {
        String activityName = activity.getClass().getName();
        ParameterData parameterData = cache.get(activityName);
        if (parameterData == null) {
            try {
                Class<?> aClass = Class.forName(activityName + FILE_SUFFIX_NAME);
                parameterData = (ParameterData) aClass.newInstance();
                cache.put(activityName, parameterData);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        parameterData.getParameter(activity);
    }

5、实现跳转的封装和携带数据。封装一个路由类RouterManager

public class RouterManager {

    private String group;

    private String path;

    private static RouterManager instance;

    private LruCache<String, ARouterGroup> mGroupLruCache;

    private LruCache<String, ARouterPath> mPathLruCache;

    public static RouterManager getInstance() {
        if (instance == null) {
            synchronized (RouterManager.class) {
                if (instance == null) {
                    instance = new RouterManager();
                }
            }
        }
        return instance;
    }

    private RouterManager() {
        mGroupLruCache = new LruCache<>(100);
        mPathLruCache = new LruCache<>(100);
    }
}

mGroupLruCache、mPathLruCache存储group和path通过下面的build方法获取跳转的activity

 public BundleManager build(String path) {
        if (TextUtils.isEmpty(path) || !path.startsWith("/")) {
            throw new IllegalArgumentException("path错误,正确写法:如/user/User_LoginActivity");
        }

        if (path.lastIndexOf("/") == 0) {
            throw new IllegalArgumentException("path错误,正确写法:如/user/User_LoginActivity");
        }
        // 截取组名  /user/User_LoginActivity  finalGroup = user
        String finalGroup = path.substring(1, path.indexOf("/", 1));

        if (TextUtils.isEmpty(finalGroup)) {
            throw new IllegalArgumentException("path错误,正确写法:如/user/User_LoginActivity");
        }
        this.path = path;
        this.group = finalGroup;
        return new BundleManager();
    }

BundleManager是我们创建的传递数据的管理类,Activity传递数据,底层都是Bundle,所以返回一个Bundle对象为了我们链式调用传递数据

public class BundleManager {

    private Bundle mBundle = new Bundle();

    public Bundle getBundle() {
        return mBundle;
    }

    // 对外界提供,可以携带参数的方法链式调用效果 模仿开源框架
    public BundleManager withString(@NonNull String key, @Nullable String value) {
        mBundle.putString(key, value);
        return this;
    }

    public BundleManager withDouble(@NonNull String key, @Nullable double value) {
        mBundle.putDouble(key, value);
        return this;
    }

    public BundleManager withBoolean(@NonNull String key, @Nullable boolean value) {
        mBundle.putBoolean(key, value);
        return this;
    }

    public BundleManager withParcelable(@NonNull String key, @Nullable Parcelable value) {
        mBundle.putParcelable(key, value);
        return this;
    }

    public BundleManager withSerializable(@NonNull String key, @Nullable Serializable value) {
        mBundle.putSerializable(key, value);
        return this;
    }

    public BundleManager withInt(@NonNull String key, @Nullable int value) {
        mBundle.putInt(key, value);
        return this;
    }

    public BundleManager withStringArrayListExtra(@NonNull String key, @Nullable List<String> value) {
        mBundle.putStringArrayList(key, (ArrayList<String>) value);
        return this;
    }

    public BundleManager withBundle(Bundle bundle) {
        this.mBundle = bundle;
        return this;
    }

    public Object navigation(Context context) {
        return RouterManager.getInstance().navigation(context, this);
    }

    public Object navigation(Context context, int requestCode) {
        return RouterManager.getInstance().navigation(context, this, requestCode);
    }

}

navagation调用RouterManager里的navigation方法最终实现跳转。

  // 例如:寻找 ARouter$$Group$$user  寻址   ARouter$$Group$$user   ARouter$$Group$$app
    public Object navigation(Context context, BundleManager bundleManager) {
        RouterBean routerBean = getRouterBean(context);
        if (routerBean != null) {
            switch (routerBean.getTypeEnum()) {//为了区别Fragment Activity
                case ACTIVITY:
                    Intent intent = new Intent(context, routerBean.getMyClass()); // 例如:getClazz == Order_MainActivity.class
                    intent.putExtras(bundleManager.getBundle()); // 携带参数
                    context.startActivity(intent);
                    break;
            }
        }
        return null;
    }

    public Object navigation(Context context, BundleManager bundleManager, int requestCode) {
        RouterBean routerBean = getRouterBean(context);
        if (routerBean != null) {
            switch (routerBean.getTypeEnum()) {//为了区别Fragment  Activity
                case ACTIVITY:
                    Intent intent = new Intent(context, routerBean.getMyClass()); // 例如:getClazz == Order_MainActivity.class
                    intent.putExtras(bundleManager.getBundle()); // 携带参数
                    ((Activity) context).startActivityForResult(intent, requestCode);
                    break;
            }
        }
        return null;
    }

6、项目中实现调用:

@ARouter(path = "/zixun/ZiXun_WebViewActivity")
public class ZiXun_WebViewActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.zixun_webview_layout);
    }

    public void jumpLogin(View view) {
        List<String> data = new ArrayList<>();
        data.add("biubiubiu");
        data.add("男男女女多所军军");
        RouterManager.getInstance().build("/user/User_LoginAcitivty")
                .withStringArrayListExtra("data", data)
                .navigation(this);
    }
}

在User_LoginAcitivty 通过ParameterManager.getInstance().loadParameter(this);接收数据

@ARouter(path = "/user/User_LoginAcitivty")
public class User_LoginAcitivty extends AppCompatActivity {

    @Parameter
    List<String> data;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.user_login_layout);
        ParameterManager.getInstance().loadParameter(this);

        for (String sss : data) {
            Log.e("TAG", sss);
        }
    }

这样就完成了数据的传递和Activity的跳转。
项目地址
项目中用到的都是最基本的技术,基本上不会遇到适配问题。不过需要对注解和反射有所了解。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,921评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,635评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,393评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,836评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,833评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,685评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,043评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,694评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,671评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,670评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,779评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,424评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,027评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,984评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,214评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,108评论 2 351
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,517评论 2 343

推荐阅读更多精彩内容