Scene 是字节跳动西瓜视频技术团队开源的一款 Android 页面导航和组合框架,用于实现 Single Activity Applications,有着灵活的栈管理,页面拆分,以及完整的各种动画支持。
Scene 最初用于解决西瓜视频的直播业务在演进过程中遇到的问题,后来又在抖音的拍摄工具中落地,经过了实践与验证,于是团队觉得将其开源到社区,希望能够帮助大家在更多的场景解决问题。
Github 项目地址与使用文档: https://github.com/bytedance/scene 。
开发背景
西瓜视频面临的问题
西瓜视频在 1.0.8 版本有做过一次播放体验的优化,希望首页正在播放的短视频跳转到详情页面时,能够有一个平滑的动画过渡。
下面的视频是老版本的过度效果:
下面的视频是新版本的过度效果:
这种复杂的过渡动画,是不可能拿 Activity 实现的。然而 Fragment 在那个时候也会出现各种怪异的状态保存引发的崩溃(虽然知道崩溃的原理,但是不能接受这种设计),于是西瓜视频技术团队设计了名为 Page 的 UI 方案,来实现过渡动画这个需求。
但是 Page 本身跟业务耦合非常严重,没法单独抽出去给其他场景用。后来,随着西瓜直播业务的壮大,也有了需要类似框架的需求,为了解决 Activity 栈管理太弱、各种黑屏、动画能力太弱等问题,同时解决 Fragment 崩溃过多问题,我们开发了 Scene 这套通用的框架。
下面是西瓜长视频详情页和抖音拍摄页面使用 Scene 的场景截图:
Activity/Fragment 的不足
这里简单列下 Activity 和 Support 28 的 Fragment 的不足,部分问题已经在 Android X 的 Fragment 上修复了。
页面导航对比 Activity
栈管理弱,Intent+LaunchMode 的设计,使得开发者在使用的时候要么极容易出错,要么用 Hack 做对了但是动画过度黑屏;
Activity 性能差,普通的空白页面切换也得 60、70ms 耗时(基于三星 S9 设备测试);
因为销毁恢复的强制要求:
导致的 Activity 动画能力非常弱,无法直接拿到前后两个页面的 View 也就无法简单的实现复杂的交互动画;
SharedElement 动画能力弱,动画的瞬间不得不来回传递上下两个 Activity 各种控件的 Bitmap;
Android 9 之前 Activity 每次启动新的 Activity,都需要上个页面执行完 onSaveInstance,这一步影响了页面打开的速度;
Activity 依赖 Manifest 给 Android 动态化增加了难度,需要对系统的 Instrumentation ActivityThread 进行各种 Hack ;
依赖注入很难,因为创建 Activity 对象的流程在 Android 8 之前是没有 API 暴露给外部处理的;
因为 Window 的机制导致做悬浮窗播放也是问题,导致实现窗口播放必须依赖了一个危险的悬浮窗权限;
共享元素动画在某些版本的 Framework 层有 NPE,无法解决。
java.lang.NullPointerException(android.app.EnterTransitionCoordinator);
页面组合对比 Fragment
各种奇怪的崩溃,就算不用 Fragment,但是用了 AppCompatActivity 还是会在 onBackPressed 里面触发崩溃;
java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState
对于这种情况,西瓜直接在自己的 Activity 基类对
super.onBackPressed()
进行了try catch。