Multi-window and large screens
Split screen, freeform, picture-in-picture, foldables and desktop windowing: what happens to an Activity when it no longer owns the display — and why 'resizable' became the default assumption.
For most of Android's life an Activity could assume it owned the screen. It could lock the orientation, assume the window was full-size, and treat a configuration change as a rare event. Multi-window broke the first two assumptions; large screens and desktop windowing are now breaking the third.
TL;DR#
- Split screen arrived in API 24Android 7.0: two Activities share the display.
- Picture-in-picture arrived in API 26Android 8.0; freeform windows exist for desktop-style environments.
- An Activity in multi-window can be resized, paused but visible, and focused or not — four states that used to be conflated.
- Window size classes are the recommended way to reason about layout instead of screen size in dp.
- The platform's direction is adaptive-first: on large screens, manifest restrictions on orientation and resizability are being phased out, and desktop windowing brings freeform windows to tablets.
Mental model#
Think of a desk that can hold several documents.
On a phone, the desk has room for one sheet at a time, and the sheet is always the same size. On a tablet, the desk holds two sheets side by side, and each sheet gets half the width. In desktop windowing, sheets can be any size and can overlap.
The sheet is an Activity. What changed is not the sheet's content but the assumption that the desk always gives it the whole surface.
Where the analogy breaks#
Documents do not reflow when the desk changes. An Activity must re-layout, and its Configuration changes — which
historically meant the Activity was destroyed and recreated. That recreation is the part developers notice, and the
reason android:configChanges and ViewModel exist. On a desk, nothing is thrown away when you move a sheet.
The big picture#
Text description of this diagram
The window stack from wallpaper through the application band to the system bars. Two app windows share the application band, so neither can draw over the other: in split screen they are siblings in the same band rather than one being on top of the other. The divider between them is a system window with its own drag region, and the IME sits in its own band above both.
The important part is that both apps are in the same band. Neither is "the top app" in the old sense; they are siblings, and the system decides which one has focus.
Deep dive#
The four states#
Before multi-window, an Activity was essentially resumed-and-visible or stopped. Multi-window splits that into a matrix:
| State | Visible | Focused | Receives input |
|---|---|---|---|
| Full-screen, resumed | yes | yes | yes |
| Split, focused | yes | yes | yes |
| Split, visible but not focused | yes | no | no (except its own window chrome) |
| Picture-in-picture | yes | no | limited |
| Stopped | no | no | no |
The middle row is the new one: an Activity can be visible but not resumed, which is why onPause no longer means
"the user cannot see you". A video app in split screen must keep playing — that is what
Activity.onTopResumedActivityChanged and the multi-window lifecycle callbacks are for.
Configuration changes#
Whenever the window size, orientation, density or available screen changes, the Activity's Configuration changes.
By default the system destroys and recreates the Activity to apply it. That is expensive and, for a resize, often
unnecessary.
Three tools reduce the cost:
android:configChangesin the manifest lets the Activity handle the change itself. Useful for size and orientation if the layout can adapt without recreation.ViewModelsurvives recreation, so state does not have to be re-fetched.SavedStateHandlepersists small state across process death as well as recreation.
Window size classes#
Reasoning about "phone vs tablet" is fragile: a foldable can be both within one session, and a resizable desktop window can be any size. The recommended model is window size classes — compact, medium, expanded — computed from the window's dimensions, not the device's.
In Compose, adaptive layouts use the window size class to choose between a bottom bar, a navigation rail and a
permanent drawer. In Views, the same decision is a resource qualifier (layout-w600dp) or a runtime check on the
window metrics.
Activity embedding#
On a large screen, a list-detail layout is often two Activities side by side. Activity embedding lets you host a second Activity inside a container in the first Activity's layout, so the two can be developed and navigated independently.
The benefit is architectural: the detail screen keeps its own lifecycle, task and deep link, while the layout is a single pane on a phone and two panes on a tablet. The cost is that the host Activity must manage the embedded Activity's lifecycle and state.
Picture-in-picture#
PiP puts an Activity into a small always-on-top window. It is a window with its own type and a restricted interaction model: the user can move and dismiss it, but not resize it freely.
The lifecycle consequence is that a PiP Activity is visible but not focused, and may be paused. Video apps handle this by continuing playback and hiding controls that need focus.
Freeform and desktop windowing#
Freeform windows — arbitrarily sized, movable, overlapping — have existed for some Android form factors for years. Desktop windowing brings the model to tablets: apps appear in resizable windows with title bars, and the user can arrange several at once.
For an app, the implications are the same as multi-window, only more so: the window can be any size, the orientation can change without the device rotating, and "full screen" is a user choice rather than a default.
The large-screen behaviour changes#
The change is not optional: the manifest attributes that used to force orientation stop being honoured. The practical response is to make every screen resizable and to stop treating a configuration change as an error path.
Testing the window, not the device#
The reliable way to test adaptive behaviour is to resize the window, not to buy more devices. On a debuggable build,
adb shell wm size and the resizable emulator configurations let you exercise compact, medium and expanded widths on
one image.
Why configuration changes used to destroy the Activity#
The original design treated a configuration change as a restart: the Activity was destroyed, the new configuration was applied, and a new instance was created. That was simple and correct, and it was expensive for exactly the case that became common — a rotation, or a window resize.
The modern toolset does not remove the recreation; it makes it cheap and safe:
ViewModelkeeps state across recreation.SavedStateHandlekeeps state across process death.configChangesavoids recreation when the Activity can adapt in place.- Compose recomposes rather than recreating, so a Compose screen handles a size change without an Activity restart.
Even so, a resize is a real event that must invalidate caches — a bitmap decoded for the old size, a layout computed for the old width. Handling it well is less about avoiding the callback and more about making the recomputation cheap.
The window size class thresholds#
The size classes are defined by breakpoints in width and height, in dp.2
| Class | Width |
|---|---|
| Compact | under 600 dp |
| Medium | 600–839 dp |
| Expanded | 840 dp and up |
A phone in portrait is compact; in landscape it may be medium; a tablet is expanded; a freeform window can be any of the three. The thresholds are not arbitrary — they correspond roughly to where a two-pane layout becomes readable.
The point of using classes rather than raw dp is that the decision ("show a rail") is stable across devices, while the exact width is not.
Multi-window and the back stack#
Each window is associated with a task, and each task has a back stack. In split screen, the two windows are two tasks, and the back gesture applies to the focused one.
Desktop windowing complicates this: a freeform window can contain a task that the user minimises, and the recents view becomes a window switcher rather than a task switcher. The back stack survives, but the affordance changes.
What breaks first#
When an app that assumed full-screen meets a resizable window, the failures are predictable:
- Fixed-size dialogs that overflow a narrow window.
- Hardcoded paddings that assume a status bar height in pixels.
- Camera or video surfaces that lock an aspect ratio and leave letterboxing.
- Orientation-locked screens that cannot rotate with the window.
- Saved state that assumes the screen size did not change.
Each is a small fix, and each is invisible until the app runs in a window it did not expect.
How it evolved#
| API | Version | What changed |
|---|---|---|
| 1 | 1.0 |
|
| 3 | 1.5 Cupcake |
|
| 9 | 2.3 Gingerbread |
|
| 11 | 3.0 Honeycomb |
|
| 12 | 3.1 Honeycomb |
|
| 16 | 4.1 Jelly Bean |
|
| 20 | 4.4W KitKat (Wear) |
|
| 21 | 5.0 Lollipop |
|
| 24 | 7.0 Nougat |
|
| 26 | 8.0 Oreo |
|
| 28 | 9 Pie |
|
| 29 | 10 |
|
| 30 | 11 |
|
| 31 | 12 |
|
| 33 | 13 Tiramisu |
|
| 36 | 16 Baklava |
|
Under the hood#
- Activity.javaonMultiWindowModeChanged, onPictureInPictureModeChanged, onTopResumedActivityChanged
- Configuration.javathe object that describes the window's size and density
- DisplayArea.javahow the display is divided among window containers
- the split-screen implementation in the Shell
Curiosity corner#
- A foldable is two devices in one session. The same Activity can see a compact window and an expanded one
without the process ever restarting, if it declares the right
configChanges. - The divider is a window. It has its own input handling and its own system type, which is why dragging it feels different from dragging inside an app.
- Desktop windowing makes the phone's recents model look simple. A task in a freeform window can be minimised, maximised and restored independently of the back stack.
Try it#
adb shell wm size 720x1600Example output
Physical size: 1080x2400
Override size: 720x1600adb shell dumpsys activity configuration | head -5Example output
mGlobalConfiguration={1.0 310mcc... 720x1600 sw360dp w360dp h772dp}adb shell am start --windowingMode 3 com.example.app/.MainActivityExample output
SuccessCheck yourself#
Check yourself
Why does the system deliver input to an app through a socket pair rather than a Binder call?
Sources#
Sources
Every version number, API level, class name and date on this page is traced to one of these. Pages are re-verified against them; last_verified above says when.
- docsBehavior changes: Apps targeting Android 16 or higherdeveloper.android.comaccessed 2026-10-27↩
- docsBuild adaptive layouts with Composedeveloper.android.comaccessed 2026-10-27↩
- docsLarge screens overviewdeveloper.android.comaccessed 2026-10-27↩
- docsBehavior changes: Apps targeting Android 12developer.android.comaccessed 2026-10-27↩
- docsLayers and displayssource.android.comaccessed 2026-10-27↩