新装备来了! Butterfly - 全新的Android路由组件化框架, 小而强大!

Butterfly.png

Butterfly - 蝴蝶

Github地址

Butterfly - 小巧而强大的武器,拥有它,让你的Android开发如虎添翼,Carry全场!

只有最强大和最经验的勇士才能挥动蝴蝶,但它在战斗中提供了令人难以置信的灵巧

物品介绍: +30 敏捷 +35% 闪避 +25 攻击 +30 攻速

功能介绍

蝴蝶通过两个不同的注解来实现不同的功能:

  • Agile 用于页面导航
  • Evade 用于组件化通信

Agile

  • 导航

通过给Activity添加Agile注解,并设置对应的scheme,随后即可通过Butterfly进行导航,或者导航并获取返回数据

@Agile("test/scheme")
class AgileTestActivity : AppCompatActivity() {
    //...
}

//导航
Butterfly.agile("test/scheme").carry()

//导航并获取返回数据
Butterfly.agile("test/scheme")
    .carry {
        val result = it.getStringExtra("result")
        binding.tvResult.text = result
    }
  • 传递参数

Agile支持附带参数导航,有两种方式,一种是通过拼接scheme,将参数添加到scheme中
另一种是通过调用params方法手动传入参数,或者两者混合进行, 随后可在导航后的页面中获取对应的参数

//拼接scheme
Butterfly.agile("test/scheme?a=1&b=2").carry()

//调用params
Butterfly.agile("test/scheme?a=1&b=2")
    .params("intValue" to 1)
    .params("booleanValue" to true)
    .params("stringValue" to "test value")
    .carry()
  • 解析参数

在导航目的页面,可通过参数的key字段来获取传递的参数值

@Agile("test/scheme")
class AgileTestActivity : AppCompatActivity() {
    val a by lazy { intent?.getStringExtra("a") ?: "" }
    val b by lazy { intent?.getStringExtra("b") ?: "" }
    val intValue by lazy { intent?.getIntExtra("intValue", 0) ?: 0 }
}

除了手动解析参数以外,还可以装备Bracer来实现全自动进行参数解析

@Agile("test/scheme")
class AgileTestActivity : AppCompatActivity() {
    val a by params<String>()
    val b by params<String>()
    val intValue by params<Int>()
}

Bracer 使用方式详情见: Github 地址 Bracer

  • 拦截器

Agile支持拦截器,可用于在导航前预处理部分逻辑,如进行登录检测
此外拦截器中也可进行导航,但为了避免拦截器套娃,需要添加skipInterceptor()方法以忽略拦截器

//实现自定义拦截器
class TestInterceptor : ButterflyInterceptor {
    override fun shouldIntercept(agileRequest: AgileRequest): Boolean {
        //检测是否需要拦截
        return true
    }

    override suspend fun intercept(agileRequest: AgileRequest) {
        //处理拦截逻辑
        println("intercepting")
        delay(5000)
        println("intercept finish")
    }
}

//注册拦截器
ButterflyCore.addInterceptor(TestInterceptor())

//跳过拦截器
Butterfly.agile("test/scheme").skipInterceptor().carry()
  • Action

Agile除了支持页面导航以外,还支持导航Action,Action无页面,可进行某些逻辑处理
首先让自定义的Class继承Action,然后添加@Agile注解并设置scheme,其余和页面导航一致

@Agile("test/action")
class TestAction : Action {
    override fun doAction(context: Context, scheme: String, data: Bundle) {
        //可从data中获取传入的参数
        Toast.makeText(context, "This is an Action", Toast.LENGTH_SHORT).show()
    }
}

//启动Action
Butterfly.agile("test/action").carry()

//Action同样支持传参
Butterfly.agile("test/action?a=1&b=2").carry()

//params 传参
Butterfly.agile("test/action")
    .params("intValue" to 1)
    .carry()
  • 流程控制

Agile除了直接调用carry导航以外,还可以调用flow返回Flow对象, 利用Flow对象可对导航流程进行处理

Butterfly.agile("test/scheme").flow()
    .onStart { println("start") }
    .onCompletion { println("complete") }
    .onEach { println("process result") }
    .launchIn(lifecycleScope)

Evade

蝴蝶使用简单的两个注解即可实现任意组件之间进行通信,而组件之间无需任何直接或间接依赖

例如有两个组件:Module Foo 和Module Bar 需要通信

在Module Foo中,定义接口,并添加Evade注解:

@Evade
interface Home {
    //定义方法
    fun showHome(fragmentManager: FragmentManager, container: Int)
}

在Module Bar中,定义实现,,并添加EvadeImpl注解:

//实现类名必须以Impl结尾
@EvadeImpl
class HomeImpl {
    val TAG = "home_tag"

    //实现Home接口中的方法, 方法名和方法参数必须相同
    fun showHome(fragmentManager: FragmentManager, container: Int) {
        val homeFragment = HomeFragment()
        fragmentManager.beginTransaction()
            .replace(container, homeFragment, TAG)
            .commit()
    }
}

由于Evade使用类名称作为定义和实现关联的重要依据,因此接口类名和实现类名必须相同,并且实现类名以Impl结尾.
如无法以类名作为关联,也可使用相同的字符串类型作为关联key

@Evade(identity = "same key")
interface Home

@EvadeImpl(identity = "same key")
class OtherNameImpl

随后即可在Module Foo中,使用evade方法获取Home并调用:

val home = Butterfly.evade<Home>()
home.showHome(supportFragmentManager, R.id.container)

除此之外, Evade也支持通过下沉依赖的形式, 进行强关联类型的通信

例如以下三个组件:公共组件Module Base,Module Foo,Module Bar

首先将Home接口下沉至公共组件Module Base中:

@Evade
interface Home {
    fun showHome(fragmentManager: FragmentManager, container: Int)
}

然后在Module Bar中,实现接口:

//同样需要使用相同的命名规则, 实现类名必须以Impl结尾
@EvadeImpl
class HomeImpl : Home {
    val TAG = "home_tag"

    override fun showHome(fragmentManager: FragmentManager, container: Int) {
        val homeFragment = HomeFragment()
        fragmentManager.beginTransaction()
            .replace(container, homeFragment, TAG)
            .commit()
    }
}

之后便可在Module Foo中,使用evade方法获取Home并调用:

val home = Butterfly.evade<Home>()
home.showHome(supportFragmentManager, R.id.container)

路由表

Butterfly会为每个使用了注解的Module生成一个路由表, 命名规则为: Butterfly[模块名称]Module

手动注册:

class DemoApplication : Application() {
    override fun onCreate() {
        super.onCreate()
        //注册
        ButterflyCore.addModule(ButterflyHomeModule())
        ButterflyCore.addModule(ButterflyFooModule())
        ButterflyCore.addModule(ButterflyBarModule())
    }
}

使用插件自动注册:

  1. 添加插件依赖
//使用 plugins DSL:
plugins {
  id "io.github.ssseasonnn.butterfly" version "1.0.1"
}

//或者使用legacy plugin application:
buildscript {
  repositories {
    maven {
      url "https://plugins.gradle.org/m2/"
    }
  }
  dependencies {
    classpath "io.github.ssseasonnn:plugin:1.0.1"
  }
}

//添加plugin
apply plugin: "io.github.ssseasonnn.butterfly"
  1. 实现自己的Application类
class DemoApplication : Application() {
    override fun onCreate() {
        super.onCreate()
    }
}

混淆配置

-keep public class zlc.season.butterfly.module.**
-keep public class zlc.season.butterfly.annotation.**
-keep public class zlc.season.butterfly.ButterflyCore {*;}
-keep public class * extends zlc.season.butterfly.Action

-keep @zlc.season.butterfly.annotation.Agile class * {*;}
-keep @zlc.season.butterfly.annotation.Evade class * {*;}
-keep @zlc.season.butterfly.annotation.EvadeImpl class * {*;}

俗话说得好,蝴蝶在手,天下我有! 这么好的装备你不来一把?

Github地址

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 141,930评论 1 299
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 60,888评论 1 255
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 93,606评论 0 211
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 40,848评论 0 175
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 48,544评论 1 253
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 38,671评论 1 173
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 30,312评论 2 267
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 29,112评论 0 165
  • 想象着我的养父在大火中拼命挣扎,窒息,最后皮肤化为焦炭。我心中就已经是抑制不住地欢快,这就叫做以其人之道,还治其人...
    爱写小说的胖达阅读 28,899评论 6 229
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 32,451评论 0 213
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 29,221评论 2 213
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 30,541评论 1 225
  • 白月光回国,霸总把我这个替身辞退。还一脸阴沉的警告我。[不要出现在思思面前, 不然我有一百种方法让你生不如死。]我...
    爱写小说的胖达阅读 24,196评论 0 31
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 27,079评论 2 213
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 31,448评论 3 204
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 25,597评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 25,969评论 0 164
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 33,451评论 2 230
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 33,523评论 2 229

推荐阅读更多精彩内容