一行注解帮你搞定Parcelable

2,455 阅读2分钟

前言

android在序列化的时候有两种模式,一种是传统的Serializable,另一种就是android特有的Parcelable方式。另种序列化有各自的选择方式 实现Parcelable的作用 1)永久性保存对象,保存对象的字节序列到本地文件中; 2)通过序列化对象在网络中传递对象; 3)通过序列化在进程间传递对象。 选择序列化方法的原则 1)在使用内存的时候,Parcelable比Serializable性能高,所以推荐使用Parcelable。 2)Serializable在序列化的时候会产生大量的临时变量,从而引起频繁的GC。 3)Parcelable不能使用在要将数据存储在磁盘上的情况,因为Parcelable不能很好的保证数据的持续性在外界有变化的情况下。尽管Serializable效率低点,但此时还是建议使用Serializable 。

可以看到当我们不需要存储到磁盘时使用Parcelable这种序列化方式更加高效,但是有个问题,就是实现Parcelable需要编写大量的代码,尽管现在有一些ide插件可以帮你生成相应代码,但是一个bean类多出这么多代码也是蛮蛋疼的,而且看起来也较乱,所以我写了这个aop工具在编译成class文件时动态实现Parcelable

使用

在项目gradle文件里添加依赖

buildscript {
    repositories {
       ....
        maven{
            url "https://dl.bintray.com/wuhaoxuan1225/maven/"
        }
        
    }
    dependencies {
        ....
        classpath 'com.skateboard.parcelablehelper:parcelablehelper:1.0.0'
        ....
        // NOTE: Do not place your application dependencies here; they belong
        // in the individual module build.gradle files
    }
}

在module的gradle文件里添加插件的引用

apply plugin: 'parcelablehelper'

在需要实现Parcelable接口的类上添加注解列化

import com.skateboard.parcelableannoation.Parcelable;
import com.skateboard.parcelableannoation.Ignore;
@Parcelable
public class Demo {

    private String name;
    


}

原理

结合android gradle的Transform以及asm来实现该功能,编写gradle插件,利用android gradle的Transform获取编译后生成的class文件,然后利用asm动态修改相应的添加注释的文件。

核心代码

因为主要就是对Transfom的编译和ASM的编写,这里就不展开描述了,可以去看一看相关的文档,这里只是介绍一些基本的类的用途。

1递归生成class文件目录,并对标注了@Parcelable注解的类进行修改,生成相应代码

/**
 * 获取目录下class文件并调用asm解析
 * */
object ParcelableByteCodeUtil {


    fun transformDirectoryInput(input: File, output: File) {

        if (output.exists()) {
            FileUtil.forceDelete(output)
        }
        FileUtil.forceMkdir(output)
        val srcDirPath = input.absolutePath
        val destDirPath = output.absolutePath
        if (input.isDirectory) {
            input.listFiles().forEach {
                val destFilePath = it.absolutePath.replace(srcDirPath, destDirPath)
                val destFile = File(destFilePath)
                if (it.isDirectory) {
                    transformDirectoryInput(it, destFile)
                } else if (it.isFile) {
                    FileUtils.touch(destFile)
                    performClassParse(it, destFile)
                }
            }
        }

    }

    private fun performClassParse(inputFile: File, outputFile: File) {
        if (inputFile.name.endsWith(".class") && inputFile.name != "R.class" && inputFile.name != "BuildConfig.class" && !inputFile.name.startsWith("R$")) {
            ParcelableParser.parseClass(inputFile, outputFile)
        } else {
            FileUtil.writeToFile(inputFile, outputFile)
        }
    }

}

2.ParcelableCreatorGenerator

这个类利用ASM动态生成CREATOR的内部类,用来对应实现Parcelable接口需要的CREATOR变量

3.ParcelableGenerateVisitor

生成Parcelable相应的接口方法,为相应的属性字段生产相应的write和read调用。

源码

Github