美文网首页
如何优雅的处理onActivityResult:ResultDi

如何优雅的处理onActivityResult:ResultDi

作者: 正在吃饭的晓雨 | 来源:发表于2020-09-22 11:02 被阅读0次

    最近学习到java注解这块,自己也想写一个注解试试看,正好当时在写onActivityResult,想到这块是不是可以通过注解用编译器生成固定代码(if else 判断code),然后通过对象回调到被注解的方法。
    代码已经上传到github,不想了解实现原理的童鞋可以直接滑到底部
    ps:本篇文章代码及源码语言均为kotlin,为了方便,文章的源码被修改过,但与源码相差不大

    首先定义注解

    /**
     *[requestCode] 请求值
     * [resultCode] 返回值
     */
    @Retention(AnnotationRetention.SOURCE)//注解的保留时期 因为我们是通过编译期生成,所以保留在源码阶段就好了
    @Target(AnnotationTarget.FUNCTION)//只能被注解到方法上
    annotation class ResultDispatch(val requestCode: Int, val resultCode: Int)
    

    编写注解处理器

    首先先获取到哪些方法被注解了,以及被注解的方法在什么地方

    //储存注解的map  key 可以理解为 文件路径,value就是这个文件内的所有ResultDispatch注解
    mDispatcherElements: MutableMap<TypeElement, MutableSet<Element>>
    //获取到所有被注解的方法,以及注解信息
    roundEnvironment.getElementsAnnotatedWith(ResultDispatch::class.java)
    //通过循环来处理这些信息
    for (element in elements) {
         //这个方法我是这么理解的,如果这个注解在方法的参数里,那么返回方法名等信息,
         //如果被注解的是方法,那么返回的是类的全路径等信息,
         //同理,如果注解到类上,那么返回包名
         //ps:这个仅个人理解,如果不对,还请指正
         val enclosingElement = element.enclosingElement
         //这里须要判断一下,因为正常情况下 储存类信息的是element是TypeElement
         if (enclosingElement is TypeElement) {
             var set = mDispatcherElements[enclosingElement]
             if (set == null) {
                 set = mutableSetOf()
                 mDispatcherElements[enclosingElement] = set
              }
              //添加到set里
              if (!set.contains(element)) {
                  set.add(element)
              }
          } else {
            continue
          }
     }
    

    到这里注解已经被我们处理完并且储存起来,但是这样还不行,我们须要通过编译器生成我们的辅助类

    这里使用的生成源码的框架叫做 javapoet 感兴趣的可以了解一下,很强大

           for (enclosedElement in mDispatcherElements.keys) {
                val mutableSet = mDispatcherElements[enclosedElement] ?: continue
                //默认的三个参数 requestCode resultCode Intent
                val parameters = mutableListOf(
                    ParameterSpec.builder(Int::class.java, "requestCode").build(),
                    ParameterSpec.builder(Int::class.java, "resultCode").build(),
                    ParameterSpec.builder(Class.forName("android.content.Intent"), "data")
                        .build()
                )
                //生成分发方法
                //参数名 首字母小写
                val name = enclosedElement.simpleName.toString().decapitalize(Locale.ROOT)
                //创建 dispatch 方法
                val methodBuilder = MethodSpec.methodBuilder("dispatch")
                     //修饰符为 public static
                    .addModifiers(Modifier.PUBLIC, Modifier.STATIC)
                    //添加参数,类型为回调引用的对象
                    .addParameter(ParameterSpec.builder( ClassName.get(enclosedElement.asType()),name).build())
                    //添加创建好的固定默认参数
                    .addParameters(parameters)
                    //方法的返回值为void
                    .returns(TypeName.VOID)
                //到这里方法已经创建好了,但是方法里还没有内容
                val contentBuilder = StringBuilder()
                val iterator = mutableSet.iterator()
                while (iterator.hasNext()) {
                    val element = iterator.next()
                    val dispatcher = element.getAnnotation(ResultDispatch::class.java)
                    //这里判断一下被注解的方法有没有Intent的参数 
                    //如果方法里有参数的话,toString  会是 方法名(类的全路径)
                    //没有的话是 方法名()
                    //所以这里判断一下有没有Intent,为了下面生成方法内容
                    val haveData = element.toString().contains("android.content.Intent")
                    //这里就是字符串的拼接
                    contentBuilder.append(
                        "if (requestCode == ${dispatcher.requestCode} && resultCode == ${dispatcher.resultCode}) {\n" +
                                "$name.${element.simpleName}(${if (haveData) "data" else ""});\n" +
                                "} ${if (iterator.hasNext()) "else" else ""} "
                    )
                }
                //这里把方法内容添加到方法里
                methodBuilder.addStatement(CodeBlock.of(contentBuilder.toString()))
                try {
                    //根据类名创建辅助类
                    val typeSpec =TypeSpec.classBuilder("${enclosedElement.simpleName}Dispatcher")
                             //类修饰符 final public
                            .addModifiers(Modifier.FINAL, Modifier.PUBLIC)
                            //添加方法
                            .addMethod(methodBuilder.build())
                            .build()
                    //获取文件并写入
                    val file = JavaFile.builder(getPackageName(enclosedElement), typeSpec).build()
                    file.writeTo(processingEnv.filer)
                } catch (e: IOException) {
                }
            }
    
     private fun getPackageName(typeElement: TypeElement): String {
            return mElementsUtil.getPackageOf(typeElement).qualifiedName.toString()
        }
    

    到这里实现的代码就已经全部介绍完了,下面看一下怎么使用

    如何使用

    在github上发不了新版本,但是引用下载就同步失败了。知道是为什么,有小伙伴知道问题出在哪里的话方便告诉一下

    将源码下载下来并导入到工程里,在需要的module里添加代码

    apply plugin: 'kotlin-kapt'
    dependencies {
        implementation project(path: ':annotation')
        kapt project(path: ':processor')
    }
    

    然后在Activity或者Fragment 里 定义方法

    @ResultDispatch(10, 20)
    fun testDispatcher() {
        Log.d("Main", "回来了,没有Intent")
    }
    
    @ResultDispatch(10, 30)
    fun testDispatcher(intent: Intent?) {
        Log.d("Main", "wow,这是传递过来的信息${intent?.getStringExtra("text")}")
    }
    

    重写一下onActivityResult

    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
            super.onActivityResult(requestCode, resultCode, data)
            MainActivityDispatcher.dispatch(this, requestCode, resultCode, data)
    }
    

    其中 MainActivityDispatcher是框架自动生成的,看一下里面的内容

    public final class MainActivityDispatcher {
      public static void dispatch(MainActivity mainActivity, int requestCode, int resultCode,
          Intent data) {
        if (requestCode == 10 && resultCode == 20) {
            mainActivity.testDispatcher();
        } else if (requestCode == 10 && resultCode == 30) {
            mainActivity.testDispatcher(data);
        }  
      }
    }
    

    只有一个dispatch的方法,里面的code判断由编译器帮我们自动生成,并且判断了我们需不需要intent,是不是方便了许多?

    其实原来打算 onActivityResult这个方法也是用编译器生成,但是功力不到家,只能换方法实现,哪位大佬知道方法请告知小弟一下(土下座)。。。

    啊,还有,为什么github发布的版本引入不了啊。呜呜呜呜

    相关文章

      网友评论

          本文标题:如何优雅的处理onActivityResult:ResultDi

          本文链接:https://www.haomeiwen.com/subject/deavyktx.html