利用APT实现android路由框架二

时间:2021-7-21 作者:qvyue

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

利用APT实现android路由框架二
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 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 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> 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、对象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 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 mGroupLruCache;

    private LruCache 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 value) {
        mBundle.putStringArrayList(key, (ArrayList) 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 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 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的跳转。
项目地址
项目中用到的都是最基本的技术,基本上不会遇到适配问题。不过需要对注解和反射有所了解。

声明:本文内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎发送邮件至:qvyue@qq.com 进行举报,并提供相关证据,工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。