/Wired

@Wired-->Arouter @Autowired simplified version

Primary LanguageKotlinApache License 2.0Apache-2.0

Wired

项目简介

基于KSP实现简化版本的Arouter Autowired实现, 功能完全相同

配置教程

kotlin && ksp配置

1、 首先配置kotlin和ksp版本, 要求kotlin大于等于1.6.10; 注意:ksp版本需要和实际kotlin版本相匹配;点击此处 查询KSP版本

Wired项目中kotlin和ksp版本号分别为1.6.10和1.6.10-1.0.4, 故以kotlin 1.6.10为例 在根目录build.gradle下配置ksp的版本号

buildscript {
	ext.kotlin_version = '1.6.10'
}
plugins {
    id 'com.google.devtools.ksp' version '1.6.10-1.0.4' apply false
}

在需要接入Wired的模块build.gradle中导入ksp插件

plugins {
	id 'com.google.devtools.ksp'
}
// or--> apply plugin: 'com.google.devtools.ksp'

如果需要模块内的生成的KSP代码可以被IDE识别, 请在模块的build.gradle中任意一行进行如下的配置:

android.sourceSets.all { it.java.srcDir("build/generated/ksp/${it.name}/kotlin/") }

注:ksp生成的代码默认会被编译到app中,但是默认对IDE不可见

Wired接入

1、 首先导入jitpack仓库

maven { url 'https://jitpack.io' }

2、 配置kotlin、ksp, 然后导入api模块和注解处理器模块

implementation 'com.github.JailedBird.Wired:lib_api:0.9.9'
ksp 'com.github.JailedBird.Wired:lib_compiler:0.9.9'

类似Arouter的结构,Wired分为三个模块

  • lib_api(Wired api)
  • lib_compiler(Wired compiler)
  • lib_annotation(Wired annotation)

lib_api通过api的形式依赖lib_annotaion, 因此不需要单独导入lib_annotation

增量编译及其日志

建议在 gradle.properties 开启如下三个配置

# KSP Incremental processing
# https://kotlinlang.org/docs/ksp-incremental.html#program-elements
ksp.incremental=true
ksp.incremental.log=true
# track classpath
ksp.incremental.intermodule=true

1、 是增量编译, 默认开启 2、增量编译的相关日志(生成路径:模块下build下kspcache) 3、 我不是很熟,但是建议开启

使用教程

参数构造

支持8种基础数据类型(非空)、withString(可空)、withParcelable(可空)、withSerializable(可空)、withObject(可空)

(PS:待优化->提供快速启动Activity的ktx)

private fun gotoSecond() {
        val bundle = WiredBuilder.Builder()
            .withString("name", "abelzhao")
            .withInt("age", 23)
            .withObject("testObj", TestObj("p1", "p2"))
            .withObject("nameList", listOf("1", "2", "3"))
            .withParcelable("testParcelable", TestParcelable("p1", "p2"))
            .withObject(
                "testParcelableList",
                listOf(
                    TestParcelable("p1", "p2"),
                    TestParcelable("p1", "p2"),
                    TestParcelable("p1", "p2")
                )
            )
            .withSerializable("testSerializable", TestSerializable("p1", "p2"))
            .withObject(
                "testSerializableList",
                listOf(
                    TestSerializable("p1", "p2"),
                    TestSerializable("p1", "p2"),
                    TestSerializable("p1", "p2")
                )
            )
            .withObject("testEnum", TestEnum.A)
            .build()
        val intent = Intent(this, SecondActivity::class.java)
        intent.putExtras(bundle)
        startActivity(intent)
    }

注意:

1、 List<Parcelable> 这种的统一按照Obj对象处理, 内部通过GSON实现, 这也是为啥最好使用WiredBuilder构建我们的参数(当然除Obj类型外也可以不使用WiredBuilder)

2、 支持枚举,同样按照Object类型传递参数 (PS: 建议使用枚举而非数字判定Activity的打开类型)

参数注入

SecondActivity.kt

    @Wired(required = true)
    var char: Char = 'c'
    @Wired
    var charNullable: Char? = null
    @Wired
    var int: Int = 1
    @Wired
    var intNullable: Int? = null
    @Wired
    var name: String = ""
    @Wired(required = true)
    var nameNullable: String? = ""
    @Wired
    var testEnum: TestEnum = TestEnum.OTHER
    @Wired
    var nameList: List<String> = emptyList()
    @Wired
    var age: Int = 0
    @Wired
    var testObj: Any? = null
    @Wired
    var testParcelable: TestParcelable? = null
    @Wired
    var testParcelableList: List<TestParcelable>? = null
    @Wired
    var testSerializable: TestSerializable? = null
    @Wired
    var testSerializableList: List<TestSerializable>? = null

注意:

1、 鉴于Wired没得历史包袱,设计其仅支持kotlin; 也是为了减少潜在的问题,Java中使用Wired会直接编译错误

2、 参数类型不允许为val, 因为val属于不可变, 无法为其注入

(SecondActivity) 生成的范式如下:

/**
 * DO NOT EDIT THIS FILE!!! IT WAS GENERATED BY Wired
 */
public class `SecondActivity$$Wired$$Autowired` : ISyringe {
  public override fun inject(target: Any?): Unit {
    val substitute = (target as? SecondActivity)?: throw IllegalStateException(
         """The target that needs to be injected must be SecondActivity, please check your code!"""
         )
    substitute.intent?.extras?.let {
      substitute.char = it.getChar("char", substitute.char)
    }
    substitute.intent?.extras?.let {
      if(it.containsKey("charNullable")) {
        substitute.charNullable = it.getChar("charNullable")
      }
    }
    substitute.intent?.extras?.let {
      substitute.int = it.getInt("int", substitute.int)
    }
    substitute.intent?.extras?.let {
      if(it.containsKey("intNullable")) {
        substitute.intNullable = it.getInt("intNullable")
      }
    }
    substitute.intent?.extras?.let {
      substitute.name = it.getString("name", substitute.name)
    }
    substitute.intent?.extras?.let {
      substitute.nameNullable = it.getString("nameNullable", substitute.nameNullable)
    }
    if (substitute.nameNullable == null) {
      Log.e("Wired::" , """The field 'nameNullable' in class 'SecondActivity' is null!""")
    }
    substitute.intent?.extras?.let {
      substitute.testEnum = SerializationServiceImpl.parseObject(it.getString("testEnum"), (object :
          TypeWrapper<TestEnum>(){}).type)
    }
    substitute.intent?.extras?.let {
      substitute.nameList = SerializationServiceImpl.parseObject(it.getString("nameList"), (object :
          TypeWrapper<List<String>>(){}).type)
    }
    substitute.intent?.extras?.let {
      substitute.age = it.getInt("age", substitute.age)
    }
    substitute.intent?.extras?.let {
      substitute.testObj = SerializationServiceImpl.parseObject(it.getString("testObj"), (object :
          TypeWrapper<Any?>(){}).type)
    }
    substitute.intent?.getParcelableExtra<TestParcelable?>("testParcelable")?.let {
      substitute.testParcelable = it
    }
    substitute.intent?.extras?.let {
      substitute.testParcelableList =
          SerializationServiceImpl.parseObject(it.getString("testParcelableList"), (object :
          TypeWrapper<List<TestParcelable>?>(){}).type)
    }
    (substitute.intent?.getSerializableExtra("testSerializable") as? TestSerializable?)?.let {
      substitute.testSerializable = it
    }
    substitute.intent?.extras?.let {
      substitute.testSerializableList =
          SerializationServiceImpl.parseObject(it.getString("testSerializableList"), (object :
          TypeWrapper<List<TestSerializable>?>(){}).type)
    }
  }
}

在合适的时机获取参数

 override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        WiredInjector.inject(this)
}

注意事项

1、 支持任何类型, 且不需要混淆(模块内部已经处理), 但是也不要想着传递图片、超大的对象这种*操作

2、 有任何bug可以向我即是反馈

3、 目前ksp默认是通过warn打印操作日志的,看起来可能会报红, 不要在意, 希望取消可以提issue

4、 增量编译下, 当且仅当 当前文件或Wired关联的类文件变化,才会导致注解处理器为这个文件重新生成注入文件, 因此几乎不存在编译耗时问题

参考文献

https://kotlinlang.org/docs/ksp-overview.html#symbolprocessorprovider-the-entry-point