美文网首页
Butterfly 重磅更新,一个支持Activity、Frag

Butterfly 重磅更新,一个支持Activity、Frag

作者: 代码我写的怎么 | 来源:发表于2022-12-09 21:24 被阅读0次

    经过漫长的等待,Butterfly 终于发布了新版本,本次更新为大家带来全功能的路由框架,不仅支持Activity、Fragment和DialogFragment,同时增加了Compose UI的路由支持,至此,一个大一统的路由框架诞生了。不仅小巧灵活,而且功能强大,Butterfly提供了统一的API,你无需关心目的地是什么,在哪里,只需要提供一个scheme,Butterfly便会导航至正确的地方。

    话不多说,先看下Butterfly支持的功能列表。

    功能列表

    ✅ 支持导航Activity
    ✅ 支持导航Fragment
    ✅ 支持导航DialogFragment
    ✅ 支持导航Compose UI
    ✅ 支持导航Action
    ✅ 支持导航参数传递和解析
    ✅ 支持获取导航返回结果
    ✅ 支持导航拦截器
    ✅ 支持Fragment和Compose UI回退栈
    ✅ 支持Fragment和Compose UI组管理
    ✅ 支持Fragment和Compose UI启动模式,如SingleTop、ClearTop
    ✅ 支持组件化通信

    导航和参数传递

    耳听为虚,眼见为实,如此强大的功能,没有几个示例怎么能行,来看一眼基本的导航功能:

    @Agile("test/activity")
    class AgileTestActivity : AppCompatActivity()
    
    @Agile("test/fragment")
    class TestFragment : Fragment()
    
    @Agile("test/dialog")
    class TestDialogFragment : DialogFragment()
    
    //导航
    Butterfly.agile("test/xxx").carry()
    
    //或者导航并获取返回数据
    Butterfly.agile("test/xxx")
        .carry {
            val result = it.getStringExtra("result")
            binding.tvResult.text = result
        }
    

    如上所示,导航就是这么简单,无论目的地是什么、在何方,只需要一个scheme即可完成路由跳转。 scheme没有任何限制,可以是任意字符串,也可以是标准scheme,如butterfly://home/path等。

    在导航的过程中,参数传递也是必不可少的一环,Butterfly提供两种方式供你选择,一是通过拼接scheme的形式,如test/activity?paramA=123&paramB=abc,即可将paramAparamB传递至目的地,二是通过调用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()
    

    如上所示,拼接scheme和调用params方法可以同时进行,在导航目的地可以获取到传递的所有的参数,params方法可以传递Bundle支持的任意类型的参数。

    在目的地接受参数:

    //在导航目的地页面,可通过参数的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

    Compose 导航

    Compose 作为Android全新的UI框架异军突起,越来越多的项目正在大量使用Compose,因此支持Compose导航已经刻不容缓。

    @Agile("test/compose")
    @Composable
    fun HomeScreen() {
        Box {
            ...
        }
    }
    
    //导航到HomeScreen页面
    Butterfly.agile("test/compose").carry()
    

    如上所示,Compose的导航和Activity或Fragment的导航完全一致,统一的API使得简单易用程度得到更进一步的提高。

    Compose 导航中的参数传递和获取与Activity或Fragment几乎相同,传递参数同样支持拼接scheme和调用params参数。获取参数只需要给Compose组件添加一个Bundle参数,随后即可通过该bundle参数获取导航中传递的参数。

    @Agile("test/compose")
    @Composable
    fun HomeScreen(bundle: Bundle) {
        val a by bundle.params<Int>()
        val b by bundle.params<Int>()
        val booleanValue by bundle.params<Boolean>()
    
        Box {
            Text(text = a)
        }
    }
    
    //拼接scheme
    Butterfly.agile("test/compose?a=1&b=2").carry()
    
    //或者调用params
    Butterfly.agile("test/compose?a=1&b=2")
        .params("intValue" to 1)
        .params("booleanValue" to true)
        .params("stringValue" to "test value")
        .carry()
    

    路由拦截器

    拦截器是一个很实用的功能,可以提供强大的路由控制功能,如登录检测、AB测试等功能。Butterfly提供两种作用域的拦截器:全局拦截器和一次性拦截器,可针对不同使用场景灵活选择。

    首先定义好拦截器功能:

    //自定义拦截器
    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").skipGlobalInterceptor().carry()
    

    配置一次性拦截器:

    //仅当前导航使用该拦截器
    Butterfly.agile("test/scheme")
        .addInterceptor(TestInterceptor())
        .carry()
    

    拦截器提供了suspend的intercept方法,聪明的你应该一眼就能明白它的强大之处。👻

    SingleTop和ClearTop启动模式以及回退栈

    众所周知,Activity支持多种启动模式,其中SingleTop和ClearTop尤为常用, Butterfly将这两种启动模式带给了Fragment和Compose,并且提供了统一的API,只需要调用singleTop或者clearTop即可。

    //for Activity
    @Agile("test/activity")
    class AgileTestActivity : AppCompatActivity()
    
    Butterfly.agile("test/activity")
        .clearTop()              
        // or .singleTop() 
        .carry()
    
    //for Fragment
    @Agile("test/fragment")
    class TestFragment : Fragment()
    
    Butterfly.agile("test/fragment")
        .clearTop()                 
        // or .singleTop()   
        .carry()
    
    //for Compose
    @Agile("test/compose")
    @Composable
    fun HomeScreen() {}
    
    Butterfly.agile("test/compose")
        .clearTop()                 
         // or .singleTop()  
        .carry()
    

    有了启动模式,回退栈自然必不可少,默认情况下,Butterfly使用回退栈对Activity、Fragment、DialogFragment以及Compose进行管理,每当导航到一个目的地时,该目的地将会添加到回退栈中,通过使用retreat方法即可回退至上一个页面。

    //关闭栈顶页面,回退至上一个页面
    Butterfly.retreat()
    
    //关闭栈顶页面, 回退至上一个页面并返回数据
    Butterfly.retreat("result" to "123")
    

    由于Compose的特性,不支持回退并返回数据,Compose可直接通过再次导航传递新数据的形式刷新页面

    Fragment和Compose的组管理

    除了使用栈的形式对Fragment和Compose进行管理以外, Butterfly还支持使用group的形式对它们进行管理,例如APP首页多个TAB对应多个Fragment、Compose Screen的情形,使用组管理可以很好的应对这种场景。

    // group fragments
    Butterfly.agile("test/fragment1")
        .group("groupName")          
        .carry()
    
    Butterfly.agile("test/fragment2")
        .group("groupName")          
        .carry()
    
    Butterfly.agile("test/fragment3")
        .group("groupName")          
        .carry()
    
    // group composes
    Butterfly.agile("test/compose_screen1")
        .group("groupName")          
        .carry()
    
    Butterfly.agile("test/compose_screen2")
        .group("groupName")          
        .carry()
    
    Butterfly.agile("test/compose_screen3")
        .group("groupName")          
        .carry()
    

    使用相同的groupName可将这些Fragments或Compose Screens管理在同一个group中,在同一个group中重复导航不会重复创建实例。

    组件化通信

    作为一个组件化路由框架,组件之间的通信也是必不可少的功能。Butterfly除了支持常规的接口下沉的形式,还支持组件之间直接通信,无需互相依赖。

    使用接口下沉的形式,首先会有一个公共的依赖组件,通常叫Base或者Common,而需要通信的两个组件如Home和Cart,都依赖于Base或者Common,构成如下的依赖图:

    如Home需要调用Cart中的addToCart方法,首先需要在Base中定义通信接口:

    Module Base:

    @Evade
    interface CartApi {
        fun addToCart(product: String)
    }
    

    随后在Cart中创建CartApi的实现:

    Module Cart:

    @EvadeImpl
    class CartApiImpl : CartApi {
        override fun addToCart(product: String) {
            // do something
        }
    }
    

    然后便可以在Home中调用CartApi提供的方法:

    Module Home:

    val cartApi = Butterfly.evade<CartApi>()
    cartApi.addToCart("test")
    

    使用接口下沉的形式很好,但也有一些问题,如果需要通信的组件越来越多,那么下沉的接口也会越来越多,最终会导致Base或者Common爆炸💥。因此,除了使用接口下沉的形式以外,Butterfly还提供了组件之间直接通信的功能,组件之间完全无需任何依赖。

    和之前的结构类似,同样是Home和Cart需要通信,Home需要调用Cart中的addToCart方法,但这次不需要下沉接口至Base或Common,只需要在Home中定义通信接口:

    Module Home:

    @Evade
    interface CartApi {
        fun addToCart(product: String)
    }
    

    随后在Cart中创建CartApi的实现,由于Home和Cart之间没有直接依赖,因此在Cart的实现类无需实现Home中的接口,只需要使用和Home中的接口相同的方法名和方法参数即可:

    Module Cart:

    // 不需要实现CartApi
    @EvadeImpl
    class CartApiImpl {
        fun addToCart(product: String) {
            // do something
        }
    }
    

    然后便可以在Home中调用CartApi的方法:

    Module Home:

    val cartApi = Butterfly.evade<CartApi>()
    cartApi.addToCart("test")
    

    更多详细代码请移步GitHub: github.com/ssseasonnn/…

    路由表注册和Gradle插件

    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()
        }
    }
    

    通过插件,Butterfly会自动发现所有的路由模块,并自动注入添加路由表的代码到Application中,达到了解放双手的目的

    End

    更多关于Butterfly的详细情况,请移步GitHub: github.com/ssseasonnn/…

    欢迎感兴趣的朋友提供反馈和建议。

    作者:Season3266
    链接:https://juejin.cn/post/7167760843021484039

    相关文章

      网友评论

          本文标题:Butterfly 重磅更新,一个支持Activity、Frag

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