Android组件化设计4 --- 路由参数传递

ARouter除了路由寻址跳转之外,还可以通过注解的方式传递参数,类似于Intent的作用

路由参数传递

    • 1 Paramter注解
    • 1.1 TypeElement 和 Element
    • 1.2 Element常用的API
    • 2 APT生成参数接收代码
    • 2.1 TypeMirror 元素类型
    • 2.2 TypeMirror怎么判断一个类是否继承自Activity
    • 3 ParamterManager
    • 4 RouterManager

1 Paramter注解

@Target(AnnotationTarget.LOCAL_VARIABLE, AnnotationTarget.PROPERTY, AnnotationTarget.FILE)
@Retention(AnnotationRetention.BINARY)
annotation class Parameter(
   val name:String = ""
)

像LayRouter一样,声明一个注解,创建一个注解处理类,在service中注册
Android组件化设计4 --- 路由参数传递_第1张图片

@AutoService(Processor::class)
class ParameterProcessor : AbstractProcessor() {

    private lateinit var message:Messager
    private lateinit var filer: Filer
    private lateinit var elementUtils: Elements
    private lateinit var typeUtils:Types

    //仓库
    //key TypeElement Activity 相当于父节点
    //value 参数数组  一个Activity中使用Parameter注解的参数
    private val paramMap = mutableMapOf<TypeElement,MutableList<Element>>()

    override fun getSupportedAnnotationTypes(): MutableSet<String> {
        return mutableSetOf(Parameter::class.java.canonicalName)
    }

    override fun getSupportedSourceVersion(): SourceVersion {
        return SourceVersion.RELEASE_8
    }


    override fun init(processingEnv: ProcessingEnvironment?) {
        super.init(processingEnv)

        //打印日志
        message = processingEnv!!.messager
        //输出kotlin文件
        filer = processingEnv.filer
        elementUtils = processingEnv.elementUtils
        typeUtils = processingEnv.typeUtils


    }
    override fun process(p0: MutableSet<out TypeElement>?, p1: RoundEnvironment?): Boolean {

        if(p0.isNullOrEmpty()){

            return false
        }

        message.printMessage(Diagnostic.Kind.NOTE,"process ------->")

        //获取全部注解过Parameter的参数
        val set = p1!!.getElementsAnnotatedWith(Parameter::class.java)

        set.forEach { element ->

            //加入集合

            //判断当前属性的父节点
            val typeElement:TypeElement = element.enclosingElement as TypeElement

            if(paramMap.containsKey(typeElement)){

                val list = paramMap[typeElement]
                list!!.add(element)

            }else{

                //如果不存在,就需要重新创建集合
                val list = mutableListOf<Element>()
                list.add(element)
                paramMap[typeElement] = list
            }

        }

        //需要处理
        message.printMessage(Diagnostic.Kind.NOTE,"----------> $paramMap")


        return false
    }
}

使用方式跟LayRouter注解时一致,需要提到一个新的知识点

1.1 TypeElement 和 Element

TypeElement是Element的一个子类,通常来说,Element就是一个节点元素,像TypeElement就是代表一个类或者接口,例如MainActivity…PackageElement代表包元素,VariableElement代表参数元素(字段、枚举、形参等),ExecutableElement代表方法元素(某个类 接口的函数)

总体来看,所有的节点都可以看做是Element

1.2 Element常用的API

public interface Element extends AnnotatedConstruct {

    TypeMirror asType();
	//返回Element的类型
    ElementKind getKind();

    Set<Modifier> getModifiers();

    Name getSimpleName();
	//返回当前元素的父元素
    Element getEnclosingElement();
	//返回当前元素的全部子元素
    List<? extends Element> getEnclosedElements();
}

Element的类型包括如下几种:

public enum ElementKind {
    PACKAGE,
    ENUM,
    CLASS,
    ANNOTATION_TYPE,
    INTERFACE,
    ENUM_CONSTANT,
    FIELD,
    PARAMETER,
    LOCAL_VARIABLE,
    EXCEPTION_PARAMETER,
    METHOD,
    CONSTRUCTOR,
    STATIC_INIT,
    INSTANCE_INIT,
    TYPE_PARAMETER,
    OTHER,
    RESOURCE_VARIABLE,
    MODULE;

    private ElementKind() {
    }
	//判断是不是类
    public boolean isClass() {
        return this == CLASS || this == ENUM;
    }
	//是不是接口
    public boolean isInterface() {
        return this == INTERFACE || this == ANNOTATION_TYPE;
    }
	//是不是属性
    public boolean isField() {
        return this == FIELD || this == ENUM_CONSTANT;
    }
}

2 APT生成参数接收代码

class MainActivityParams : ParameterGet {
        
    override fun getParameter(activity: Any) {

        val activityInstance = activity as MainActivity

        activityInstance.age = activityInstance.intent.getStringExtra("age").toString()
        activityInstance.name = activityInstance.intent.getStringExtra("name").toString()
        activityInstance.subject = activityInstance.intent.getStringExtra("subject").toString()

    }

}

生成上述模板代码,其中getParameter的参数activity,就是携带参数跳转到的目标Activity,那么在路由跳转的时候,通过在Intent中传入参数,在目标Activity中接收

 override fun process(p0: MutableSet<out TypeElement>?, p1: RoundEnvironment?): Boolean {

        if (p0.isNullOrEmpty()) {

            return false
        }

        message.printMessage(Diagnostic.Kind.NOTE, "process ------->")

        //获取全部注解过Parameter的参数
        val set: Set<Element> = p1!!.getElementsAnnotatedWith(Parameter::class.java)

        set.forEach { element ->

            //加入集合
            //判断当前属性的父节点
            val typeElement = element.enclosingElement

            if (paramMap.containsKey(typeElement)) {

                val list = paramMap[typeElement]
                list!!.add(element)

            } else {

                //如果不存在,就需要重新创建集合
                val list = mutableListOf<Element>()
                list.add(element)
                paramMap[typeElement] = list
            }

        }

        //需要处理
        message.printMessage(Diagnostic.Kind.NOTE, "----------> $paramMap")

        if (paramMap.isEmpty()) {

            return false
        }

        createParamFile()


        return false
    }

    private fun createParamFile() {

        paramMap.forEach { (typeElement, mutableList) ->

            //创建方法
            val func = FunSpec.builder("getParameter")
                .addModifiers(KModifier.OVERRIDE)
                .addParameter("activity", Any::class)
                .addStatement(
                    "val %N = activity as %T",
                    property,
                    typeElement.asType().asTypeName()
                )

            mutableList.forEach { element ->

                //Parameter注解的赋值
                val name = element.getAnnotation(Parameter::class.java).name

                func.addStatement(
                    "%N.%L = %N.intent.getStringExtra(%S).toString()",
                    property,
                    element.simpleName.toString(),
                    property,
                    element.simpleName.toString()
                )
            }

            val classBuilder = TypeSpec.classBuilder(typeElement.simpleName.toString() + "Params")
                .addSuperinterface(ClassName(ipackage, iname))
                .addFunction(func.build())
                .build()

            val fileSpec = FileSpec.builder("", typeElement.simpleName.toString() + "Params")
                .addType(classBuilder).build()

            fileSpec.writeTo(filer)

        }
    }
}

老规矩,只说新的知识点,但是好像并没有~~

最终生成的模板代码

public class RegisterActivityParams : ParameterGet {
  public override fun getParameter(activity: Any): Unit {
    val activityInstance = activity as RegisterActivity
    //在这里就赋值了
    activityInstance.sex = activityInstance.intent.getStringExtra("sex").toString()
  }
}

那么在路由跳转的时候,通过Intent携带sex参数,跳转到RegisterActivity

This is last chapter code,please find this in last chapter

 val r = ARouteGroupregister()
 val registerRouter = r.getARouteGroup()["register"]

 val registerActivityClass = registerRouter!!.getARoutePath()["register/RegisterActivity"]

 startActivity(Intent(this,registerActivityClass!!.getModel().java).putExtra("sex","男性"))

在RegisterActivity中调用getParameter,其实已经对sex:String赋值

@LayRouter(group = "register",path = "register/RegisterActivity")
class RegisterActivity : AppCompatActivity() {

    @Parameter
    lateinit var sex:String

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        setContentView(R.layout.activity_register)
        RegisterActivityParams().getParameter(this)

        findViewById<TextView>(R.id.tv_info).text = sex
    }
}

Android组件化设计4 --- 路由参数传递_第2张图片

这是特殊的一种情况,所有的属性都是字符串,如果有其他类型的字符串,就不能只用 intent.getStringExtra输出代码,就会报错,因此需要判断属性的类型

2.1 TypeMirror 元素类型

TypeMirror是用来判断Element的类型,是基本数据类型,还是类,还是方法…

mutableList.forEach { element ->

   //Parameter注解的赋值
    val name = element.getAnnotation(Parameter::class.java).name

    val typeMirror = element.asType()

    var str = "intent.getStringExtra(%S).toString()"
    if(typeMirror.kind == TypeKind.INT){

        str = "intent.getIntExtra(%S,0)"
    }else if(typeMirror.kind == TypeKind.BOOLEAN){

        str = "intent.getBooleanExtra(%S,false)"
    }

    func.addStatement(
        "%N.%L = %N.$str",
        property,
        element.simpleName.toString(),
        property,
        element.simpleName.toString()
    )
}

因为是调用Element的asType方法,肯定是判断当前Element是哪些子类,因此通过判断Element的类型,来执行不同的语句

public class RegisterActivityParams : ParameterGet {
  public override fun getParameter(activity: Any): Unit {
    val activityInstance = activity as RegisterActivity
    activityInstance.sex = activityInstance.intent.getStringExtra("sex").toString()
    activityInstance.age = activityInstance.intent.getIntExtra("age",0)
    activityInstance.isOk = activityInstance.intent.getBooleanExtra("isOk",false)
  }
}

2.2 TypeMirror怎么判断一个类是否继承自Activity

见上一节的代码

val activityMirror = elementTool!!.getTypeElement(ACTIVITY_PACKAGE).asType()

if(typeTool!!.isSubtype(element.asType(),activityMirror)){

    routeBean.setType(RouterType.ACTIVITY)
}else{
    //抛出异常
    throw Exception("@LayRouter注解只能在Activity或者Fragment上使用")
}

每个Element都有自己的TypeMirror,通过TypesUtils,就可以判断,一个元素是否是继承自某个类,通过TypesUtils 的 isSubtype 方法

3 ParamterManager

在目标Activity中,每次需要创建一个对应的ParameterGet对象,因为实现类本身就是根据Activity创建的,因此可以通过一个管理类,来自动查找实现类

class ParameterManager {

    companion object{

        val instance by lazy(LazyThreadSafetyMode.SYNCHRONIZED) {
            ParameterManager()
        }
    }

    //定义缓存
    /**
     * key Activity的名称
     * value ParameterGet的实现类
     */
    private val map:MutableMap<String,ParameterGet> = mutableMapOf()


    fun loadParams(activity:Any) : ParameterGet?{

        //获取绑定的activity的名称
        val name = activity::class.simpleName!!

        if(map.containsKey(name)){

            return map[name]
        }

        //如果没有存在map集合中
        val className = Class.forName(name + "Params")
        map[name] = className.newInstance() as ParameterGet

        return map[name]
    }
}

在任何Activity中,通过这一行代码,就能够实现参数的传递

 ParameterManager.instance.loadParams(this)!!.getParameter(this)

4 RouterManager

再回到我们最初的愿景:一行代码,搞定路由通信传参

val targetGroup = ARouter_Group_register()
val registerRouter = targetGroup.getARouteGroup()["register"]

val registerActivityClass = registerRouter!!.getARoutePath()["register/RegisterActivity"]

val intent = Intent().apply {

    setClass(this@MainActivity,registerActivityClass!!.getModel().java)
    putExtra("sex","男性")
    putExtra("age",25)
    putExtra("isOk",true)
}
startActivity(intent)

最开始,Activity的跳转,首先找到目标Activity的Group,然后再找到目标Activity的Path组合,查找要跳转的路由路径

class RouterManager {

    companion object{

        val instance by lazy(LazyThreadSafetyMode.SYNCHRONIZED) {

            RouterManager()
        }
    }

    //需要两个缓存,组的缓存,路径的缓存
    /**
     * key 组的名称
     * value IARoutGroup的实现类
     */
    private val mGroupMap:MutableMap<String, IARoutGroup> = mutableMapOf()

    /**
     * key 路径名称
     * value 路径元素
     */
    private val mPathGroup:MutableMap<String, RouteBean> = mutableMapOf()

    private var group:String = ""
    private var path:String = ""

    fun build(path:String) : RouterManager?{

        if(path.isEmpty()){

            return null
        }

        //获取group
        val splitArray = path.split("/")
        group = splitArray[0]

        this.path = path

        return this
    }

    fun navigation(context: Context){

        //查找组 ARouter_Group_register
        val targetGroup = "ARouter_Group_$group"

        val targetGroupImpl: IARoutGroup
        //从group中查找
        if(mGroupMap.containsKey(group)){

            targetGroupImpl = mGroupMap[group]!!
        }else{

            val groupClass = Class.forName(targetGroup)
            targetGroupImpl = groupClass.newInstance() as IARoutGroup
            mGroupMap[group] = targetGroupImpl
        }

        val routeBean: RouteBean

        //查找路由
        if(mPathGroup.containsKey(path)){

            routeBean = mPathGroup[path] as RouteBean
        }else{

            val targetRouterPath = targetGroupImpl.getARouteGroup()[group]!!
            routeBean = targetRouterPath.getARoutePath()[path] as RouteBean
            mPathGroup[group] = routeBean
        }

        val intent = Intent().apply {

            setClass(context,routeBean.getModel().java)
        }

        context.startActivity(intent)

    }

}

最终通过一行代码,实现路由跳转

RouterManager.instance.build("app/SecondActivity")!!.navigation(this)

如果想要实现带参数传递,实现如下,先写一个BundleManager用来传递参数

class BundleManager private constructor(){

    companion object {

        val instance by lazy(LazyThreadSafetyMode.SYNCHRONIZED) {
            BundleManager()
        }
    }

    private val bundle = Bundle()


    fun withString(key:String,value:String) : BundleManager{

        bundle.putString(key,value)

        return this
    }

    fun withInt(key: String,value: Int) : BundleManager{

        bundle.putInt(key,value)

        return this
    }

    fun withBoolean(key: String,value: Boolean) : BundleManager{

        bundle.putBoolean(key,value)

        return this
    }

    fun getBundle():Bundle{

        return bundle
    }

    fun navigation(context: Context) : RouterManager?{

        if(!bundle.isEmpty){
			//在这里直接路由跳转
            return RouterManager.instance.navigation(context,this)
        }

        return null
    }
}

修改后的RouteManager如下

class RouterManager private constructor(){

    companion object{

        val instance by lazy(LazyThreadSafetyMode.SYNCHRONIZED) {

            RouterManager()
        }
    }

    //需要两个缓存,组的缓存,路径的缓存
    /**
     * key 组的名称
     * value IARoutGroup的实现类
     */
    private val mGroupMap:MutableMap<String, IARoutGroup> = mutableMapOf()

    /**
     * key 路径名称
     * value 路径元素
     */
    private val mPathGroup:MutableMap<String, RouteBean> = mutableMapOf()

    private var group:String = ""
    private var path:String = ""

    fun build(path:String) : BundleManager?{

        if(path.isEmpty()){

            return null
        }

        //获取group
        val splitArray = path.split("/")
        group = splitArray[0]

        this.path = path

        return BundleManager.instance
    }

    fun navigation(context: Context,manager: BundleManager) : RouterManager{

        //查找组 ARouter_Group_register
        val targetGroup = "ARouter_Group_$group"

        val targetGroupImpl: IARoutGroup
        //从group中查找
        if(mGroupMap.containsKey(group)){

            targetGroupImpl = mGroupMap[group]!!
        }else{

            val groupClass = Class.forName(targetGroup)
            targetGroupImpl = groupClass.newInstance() as IARoutGroup
            mGroupMap[group] = targetGroupImpl
        }

        val routeBean: RouteBean

        //查找路由
        if(mPathGroup.containsKey(path)){

            routeBean = mPathGroup[path] as RouteBean
        }else{

            val targetRouterPath = targetGroupImpl.getARouteGroup()[group]!!
            routeBean = targetRouterPath.getARoutePath()[path] as RouteBean
            mPathGroup[group] = routeBean
        }

        val intent = Intent().apply {

            setClass(context,routeBean.getModel().java)
            putExtras(manager.getBundle())
        }

        context.startActivity(intent,manager.getBundle())
        return this

    }

}

最后,我们实现了一开始的愿景,通过一行代码就实现了路由的跳转(携带参数)

 RouterManager.instance.build("register/RegisterActivity")!!
            .withString("sex","男")
            .withInt("age",31)
            .withBoolean("isOk",true)
            .navigation(this)

End~~~~~

你可能感兴趣的:(技术,android,kotlin,java,apt,组件化)