Mobile Development 14 min read

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.

Watermelon Video Tech Team
Watermelon Video Tech Team
Watermelon Video Tech Team
Scene: ByteDance’s Open‑Source Android Single‑Activity Navigation and Composition Framework

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

animationViewModelAndroidFragmentnavigationSceneSingle Activity
Watermelon Video Tech Team
Written by

Watermelon Video Tech Team

Technical practice sharing from Watermelon Video

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.