Scene: ByteDance’s Open‑Source Android Single‑Activity Navigation and Composition Framework
Scene is an open‑source Android framework from ByteDance’s Xigua video team that provides lightweight, view‑based page navigation and composition for Single‑Activity applications, addressing Activity/Fragment limitations with flexible stack management, shared‑element animations, and ViewModel‑based communication.
Scene is an open‑source Android page‑navigation and composition framework released by ByteDance’s Xigua video team to support Single‑Activity Applications with flexible stack management, page splitting, and comprehensive animation support.
The framework originated from the need to create smooth transition animations for short‑video playback in Xigua’s live‑streaming business and was later adopted by Douyin’s recording tool, prompting the team to open‑source it for broader use.
Development background
Problems with traditional Activity and Fragment approaches included weak stack management, high latency, fragile lifecycle handling, limited animation capabilities, and frequent crashes caused by state‑saving and fragment transactions.
Scene addresses these issues by providing a lightweight view‑based solution with a single lifecycle, eliminating the dual‑lifecycle problem of Fragments.
Key features
Implemented on top of View, making it very lightweight.
Single lifecycle – when the View is destroyed, the Scene is destroyed.
Flexible navigation stack that avoids black‑screen transitions.
Navigation and composition operations execute directly without distinguishing between commit and commitNow .
Optional state‑saving at the page level, enhancing component communication.
Full support for shared‑element animations.
Navigation and composition can be used independently.
Basic concepts
Scene defines three core components: Scene (base class with lifecycle and view support), NavigationScene (handles page navigation), and GroupScene (allows arbitrary Scene composition).
Component
Purpose
Scene
Base class for all Scenes, provides lifecycle and view handling.
NavigationScene
Supports page navigation.
GroupScene
Supports composing multiple Scenes together.
Getting started
Add the dependencies:
dependencies {
implementation 'com.bytedance.scene:scene:$latest_version'
implementation 'com.bytedance.scene:scene-ui:$latest_version'
implementation 'com.bytedance.scene:scene-shared-element-animation:$latest_version'
// Kotlin
implementation 'com.bytedance.scene:scene-ktx:$latest_version'
}Create a home Scene and a corresponding Activity:
class MainScene : AppCompatScene() {
override fun onCreateContentView(inflater: LayoutInflater, container: ViewGroup, savedInstanceState: Bundle?): View? {
return View(requireSceneContext())
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
setTitle("Main")
toolbar?.navigationIcon = null
}
}
class MainActivity : SceneActivity() {
override fun getHomeSceneClass(): Class
= MainScene::class.java
override fun supportRestore(): Boolean = false
}Declare the Activity in AndroidManifest.xml and launch the app.
Navigation API
requireNavigationScene().push(TargetScene::class.java)
requireNavigationScene().pop()
requireNavigationScene().push(TargetScene::class.java, null, PushOptions.Builder().setPushResultCallback { result -> }.build())
requireNavigationScene().setResult(this@TargetScene, YOUR_RESULT)Composition API
void add(@IdRes int viewId, @NonNull Scene childScene, @NonNull String tag);
void remove(@NonNull Scene childScene);
void show(@NonNull Scene childScene);
void hide(@NonNull Scene childScene);
@Nullable
T findSceneByTag(@NonNull String tag);Example of a GroupScene that adds a child Scene into a dynamically generated FrameLayout :
class SecondScene : AppCompatScene() {
private val mId: Int by lazy { View.generateViewId() }
override fun onCreateContentView(inflater: LayoutInflater, container: ViewGroup, savedInstanceState: Bundle?): View? {
val frameLayout = FrameLayout(requireSceneContext())
frameLayout.id = mId
return frameLayout
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
setTitle("Second")
add(mId, ChildScene(), "TAG")
}
}
class ChildScene : Scene() {
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup, savedInstanceState: Bundle?): View {
val view = View(requireSceneContext())
view.setBackgroundColor(Color.GREEN)
return view
}
}Communication
Scenes can use Android ViewModel via by activityViewModels or by viewModels to share data between parent and child Scenes.
class ViewModelSceneSamples : GroupScene() {
private val viewModel: SampleViewModel by activityViewModels()
// ... observe LiveData and add child Scene
}Animation
Push transitions can be customized with PushOptions :
val enter = R.anim.slide_in_from_right
val exit = R.anim.slide_out_to_left
requireNavigationScene().push(TargetScene::class.java, null,
PushOptions.Builder().setAnimation(requireActivity(), enter, exit).build())Complex shared‑element and gesture animations are demonstrated in the demo repository.
Swipe‑back
setSwipeEnabled(true)Core design ideas
Scene wraps a View with a lifecycle, using an internal LifeCycleFragment to propagate lifecycle events.
Parent‑child lifecycle callbacks are ordered (parent before child on enter, child before parent on exit).
NavigationScene manages the navigation stack, while GroupScene handles composition, similar to iOS UINavigationController.
Lifecycle and animation are processed sequentially to avoid Activity‑level bitmap tricks and black‑screen issues.
Shared‑element animations are re‑implemented using the system’s GhostView for better performance.
Future work
Scene Router – to support popular Android modularization patterns.
Scene Dialog – to solve Dialog‑over‑View problems caused by Window‑based implementation.
Discussion of Single‑Activity approaches, including comparisons with Conductor, Navigation Component, and upcoming Fragment redesigns.
The repository’s demo covers most everyday Android scenarios; additional features can be explored in the source code.
References
Single Activity: Why, When, and How (Android Dev Summit ’18)
Fragments: Past, Present, and Future (Android Dev Summit ’19)
Conductor – https://github.com/bluelinelabs/Conductor
Uber RIBs – https://github.com/uber/RIBs
Watermelon Video Tech Team
Technical practice sharing from Watermelon Video
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.