Scene 最初用于解决西瓜视频的直播业务在演进过程中遇到的问题,后来又在抖音的拍摄工具中落地,经过了实践与验证,于是团队觉得将其开源到社区,希望能够帮助大家在更多的场景解决问题。
西瓜视频面临的问题
下面的视频是老版本的过度效果:
这种复杂的过渡动画,是不可能拿 Activity 实现的。然而 Fragment 在那个时候也会出现各种怪异的状态保存引发的崩溃(虽然知道崩溃的原理,但是不能接受这种设计),于是西瓜视频技术团队设计了名为 Page 的 UI 方案,来实现过渡动画这个需求。
下面是西瓜长视频详情页和抖音拍摄页面使用Scene的场景截图:
西瓜的长视频页面和抖音的拍摄页面截图
这里简单列下 Activity 和 Support 28 的 Fragment 的不足,部分问题已经在 Android X 的 Fragment 上修复了。
页面组合对比 Fragment
- 各种奇怪的崩溃,就算不用 Fragment,但是用了 AppCompatActivity 还是会在 onBackPressed 里面触发崩溃;
功能特点
基本概念
Scene
GroupScene
Scene 使用
这里介绍简单的上手,更多用法见 Github 仓库的示例。
添加依赖:
创建 Activity:
运行就可以了。
导航
返回:
设置结果:
组合的 API 类似 Fragment,继承 GroupScene,然后可以操作任意 Scene 添加到自己的 View 布局内:
通讯
Scene 支持 ViewModel,可以通过 by activityViewModels,by viewModels 拿到托管到 Activity 或者自己的 ViewModel:
动画
在 Push 的时候,通过 PushOptions 可以配置简单的过场动画:
右划返回
核心设计思路
- Scene 本身是在 View 上面包一层生命周期,通过一个叫 LifeCycleFragment 的原生 Fragment 分发生命周期事件给框架内部,再由父组件同步给子组件。
- 父子组件同步生命周期,在原则上: 进入的时候,先执行父组件的生命周期回调,再执行子组件的生命周期回调; 退出的时候,先执行子组件的生命周期回调,再执行父组件的生命周期回调;
- NavigationScene 负责导航栈的处理,GroupScene 负责页面组合的处理,有点类似 iOS 的 UINavigationController/UIViewController,WinRT 的 Page。拆分的原因,是出于考虑性能,因为导航这个任务,由于动画的要求,本身的层级就会比普通的页面组合复杂,动画的 API 也更加强大。这两件事情,本身影响的生命周期也不一样,导航会影响之前的页面,而组合并不会。
- 生命周期和动画的处理原则是,先执行完生命周期,然后拿前后两个页面的 View 做动画,所以避免了Activity 动画需要在页面之间来回传递 Bitmap 来模拟控件这种繁琐的步骤,也避免了 Activity 动画黑屏的问题。
- 最后再由于 Transition 库过于无力,所以用系统核心的 GhostView,Scene 重头实现一遍共享元素动画。
未来与总结
Scene Dialog,开发中,用于解决 Android 框架的 Dialog 因为是基于 Window 会盖在普通的 View 之上的问题。
基于 View 重新实现的导航和组合方案,一方面是没有之前的技术债,一方面可以跳出 Google 的想法,比如说可以控制状态保存的范围,来实现更加强大的动画能力和组件通讯能力,这是官方的组件不会提供给开发者的。
Single Activity: Why, When, and How (Android Dev Summit '18)(https://www.youtube.com/watch?v=2k8x8V77CrU)
Conductor (https://github.com/bluelinelabs/Conductor)
欢迎关注「字节跳动技术团队」
