ARouter除了路由寻址跳转之外,还可以通过注解的方式传递参数,类似于Intent的作用
@Target(AnnotationTarget.LOCAL_VARIABLE, AnnotationTarget.PROPERTY, AnnotationTarget.FILE)
@Retention(AnnotationRetention.BINARY)
annotation class Parameter(
val name:String = ""
)
像LayRouter一样,声明一个注解,创建一个注解处理类,在service中注册
@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注解时一致,需要提到一个新的知识点
TypeElement是Element的一个子类,通常来说,Element就是一个节点元素,像TypeElement就是代表一个类或者接口,例如MainActivity…PackageElement代表包元素,VariableElement代表参数元素(字段、枚举、形参等),ExecutableElement代表方法元素(某个类 接口的函数)
总体来看,所有的节点都可以看做是Element
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;
}
}
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
}
}
这是特殊的一种情况,所有的属性都是字符串,如果有其他类型的字符串,就不能只用 intent.getStringExtra
输出代码,就会报错,因此需要判断属性的类型
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)
}
}
见上一节的代码
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 方法
在目标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)
再回到我们最初的愿景:一行代码,搞定路由通信传参
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~~~~~