Skip to content

How to Design Android Apps for Modular Updates and Multi-Device Fragmentation

Samsung Galaxy Fold

The Problem

I released my Android app last year. It worked perfectly on my test phone. Then users started complaining. The layout looked broken on their Samsung foldables. Buttons were cut off on tablets. The app crashed on Android Auto. And suddenly, a minor system update changed an API behavior I relied on.

The old assumptions I made were wrong:

Old Assumptions (No Longer Valid)
- Android updates come once a year with full OS releases
- Standard phone screen is 360dp width
- API behavior stays stable between minor updates
- One APK fits all devices

The reality in 2026 is fundamentally different.

What Changed in Android

Android’s architecture shifted in ways that break traditional development approaches:

Modular System Updates

System-level packages now update independently from full OS upgrades. This means:

Modular Update Impact
Traditional: One big OS update per year
→ Predictable behavior changes
→ Yearly architecture review
Now: Continuous component updates
→ Faster behavior changes across devices
→ Reduced control over version assumptions
→ Quarterly (or more frequent) review needed

I can no longer assume my app runs on a “stable” Android version. A component update might change notification handling. Another might modify permission behavior. The platform evolves continuously.

Multi-Device Fragmentation

Fragmentation now means more than just OS versions. The device ecosystem exploded:

Device Fragmentation in 2026
┌─────────────────────────────────────────────┐
│ │
Wearables │ 2-inch round screens (Wear OS) │
───────────────│ │
│ │
Phones │ Compact screens (360dp typical) │
───────────────│ │
│ │
Foldables │ Multiple states: folded → unfolded │
───────────────│ Screen sizes change during use │
│ │
Tablets │ Medium to large screens (600dp - 900dp) │
───────────────│ │
│ │
Automotive │ Wide landscape displays │
───────────────│ Split-screen requirements │
│ │
IoT/TV │ Very large screens, limited input │
──────────────────────────────────────────────┘

Each category has different constraints. Wearables need glanceable UI. Foldables need layouts that adapt when the screen size changes mid-use. Automotive needs landscape-optimized designs with split-screen support.

Performance and Ranking Impact

Apps that don’t adapt face consequences beyond user complaints:

Store Ranking Impact
- Poor performance on foldables → Lower visibility
- Non-responsive layouts → Negative reviews
- Missing automotive support → Limited ecosystem reach
- Large monolithic APKs → Longer install times, fewer installs

Google Play now considers multi-device performance in ranking. Apps that work well across categories get more visibility.

The Solution: Adaptive Architecture

I rebuilt my app with three core principles:

1. Jetpack Libraries for Stability

Jetpack libraries provide backward-compatible behavior that survives platform updates:

Jetpack Stability Layers
┌─────────────────────────────────────────────────────────────────┐
│ Your App Code │
├─────────────────────────────────────────────────────────────────┤
│ Jetpack Libraries │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │Activity │ │Fragment │ │Lifecycle │ │ViewModel │ │
│ └──────────┘ └──────────┘ └──────────┘ └──────────┘ │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │Navigation│ │Room │ │WorkManager│ │DataStore │ │
│ └──────────┘ └──────────┘ └──────────┘ └──────────┘ │
├─────────────────────────────────────────────────────────────────┤
│ Android Platform │
│ (Continuous modular updates) │
└─────────────────────────────────────────────────────────────────┘

Jetpack abstracts platform changes. When Android modifies notification behavior, Jetpack’s WorkManager handles the transition. When lifecycle rules change, Jetpack’s Lifecycle components adapt.

Key libraries I now use:

Essential Jetpack Components
- Activity/Fragment: Standardized component architecture
- Lifecycle/ViewModel: Survive configuration changes (fold/unfold)
- Navigation: Handle multi-screen flows across devices
- WindowManager: Detect foldable states and postures
- Room: Database that works across Android versions

2. Window Size Classes for Adaptive Layouts

The old approach of fixed dimensions is broken. I used to write:

layout.xml (WRONG - Fixed dimensions)
<LinearLayout
android:layout_width="360dp"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:layout_width="300dp"
android:layout_height="wrap_content"
android:text="Welcome"/>
<Button
android:layout_width="280dp"
android:layout_height="48dp"
android:text="Continue"/>
</LinearLayout>

This breaks immediately on foldables. When a user unfolds their device, the screen width changes from ~360dp to ~600dp+. The fixed 300dp TextView looks tiny. The 280dp button floats in empty space.

The correct approach uses ConstraintLayout with guidelines:

layout.xml (CORRECT - Responsive design)
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<!-- Guideline adapts to screen width -->
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guideline_start"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintGuide_percent="0.05" />
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guideline_end"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintGuide_percent="0.95" />
<TextView
android:id="@+id/title"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="Welcome"
app:layout_constraintStart_toStartOf="@id/guideline_start"
app:layout_constraintEnd_toEndOf="@id/guideline_end"
app:layout_constraintTop_toTopOf="parent" />
<Button
android:id="@+id/continue_btn"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="Continue"
app:layout_constraintStart_toStartOf="@id/guideline_start"
app:layout_constraintEnd_toEndOf="@id/guideline_end"
app:layout_constraintTop_toBottomOf="@id/title"
android:layout_marginTop="16dp" />
</androidx.constraintlayout.widget.ConstraintLayout>

The 0dp width with constraints makes elements fill available space. Guidelines at 5% and 95% create consistent margins regardless of screen size.

But layout alone isn’t enough. Different device categories need different UI structures. Window size classes solve this:

WindowSizeClassAdapter.kt
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val windowSizeClass = calculateWindowSizeClass(this)
when (windowSizeClass.widthSizeClass) {
WindowWidthSizeClass.COMPACT -> {
// Single column layout for phones
// Typical width: 0-599dp
setContentView(R.layout.layout_compact)
}
WindowWidthSizeClass.MEDIUM -> {
// Two column layout for tablets/foldables
// Typical width: 600-839dp
setContentView(R.layout.layout_medium)
}
WindowWidthSizeClass.EXPANDED -> {
// Three column layout for large screens
// Typical width: 840dp+
setContentView(R.layout.layout_expanded)
}
}
}
}

The size class boundaries are meaningful:

Window Size Class Breakpoints
COMPACT (0-599dp):
- Most phones in portrait
- Foldables in folded state
- Single column, vertical navigation
MEDIUM (600-839dp):
- Tablets in portrait
- Foldables in unfolded portrait
- Large phones
- Two columns possible
EXPANDED (840dp+):
- Tablets in landscape
- Foldables in unfolded landscape
- Desktop-class Chrome OS
- Three+ columns, persistent navigation

This approach handles foldable transitions automatically. When a user unfolds their device, the size class changes from COMPACT to MEDIUM. The layout switches accordingly.

3. Modular Feature Delivery

Monolithic APKs create multiple problems:

Monolithic APK Issues
- Large install size → Users skip install
- All features loaded → Memory pressure on low-end devices
- Single update cycle → Any change requires full APK rebuild
- No device-specific optimization → Wear OS gets phone code

Dynamic Feature Modules solve these:

Modular App Architecture
┌─────────────────────────────────────────────┐
│ Base Module │
│ ┌───────────┐ ┌───────────┐ ┌───────────┐ │
│ │ Core UI │ │ Auth │ │ Settings │ │
│ └───────────┘ └───────────┘ └───────────┘ │
│ ┌───────────┐ ┌───────────┐ │
│ │ Navigation│ │ Network │ │
│ └───────────┘ └───────────┘ │
└─────────────────────────────────────────────┘
┌───────────────┼───────────────┐
│ │ │
┌───────▼───────┐ ┌─────▼─────┐ ┌──────▼──────┐
│ Maps Feature │ │ Chat │ │ Automotive │
│ Module │ │ Module │ │ Module │
└───────────────┘ └───────────┘ └─────────────┘
│ │ │
Download on Download on Only for
demand demand Android Auto

Benefits:

Modular Delivery Benefits
1. Smaller initial install → More installs
2. Device-specific modules → Wear OS doesn't download phone features
3. On-demand loading → Features load when user needs them
4. Faster updates → Change one module, not entire APK

Common Mistakes I Made

Mistake 1: Testing Only on Phones

Wrong vs Right
WRONG:
- Test on Pixel phone
- Release to all devices
- Users complain about tablet layout
RIGHT:
- Test on phone, tablet emulator, foldable emulator
- Use Android Studio's Device Manager
- Test Automotive in desktop head unit emulator
- Test Wear OS on round screen emulator

Android Studio provides emulators for every device category. Testing across categories catches layout issues before release.

Mistake 2: Hardcoding Dimensions

Wrong vs Right
WRONG:
android:layout_width="360dp"
android:padding="16dp"
RIGHT:
android:layout_width="match_parent" // or 0dp with constraints
android:padding="@dimen/screen_margin" // Define in dimens.xml with qualifiers

I now define dimensions in resource files with screen size qualifiers:

res/values/dimens.xml (default)
<resources>
<dimen name="screen_margin">16dp</dimen>
<dimen name="card_width">0dp</dimen>
</resources>
<!-- res/values-w600dp/dimens.xml (medium screens) -->
<resources>
<dimen name="screen_margin">24dp</dimen>
<dimen name="card_width">300dp</dimen>
</resources>
<!-- res/values-w840dp/dimens.xml (large screens) -->
<resources>
<dimen name="screen_margin">32dp</dimen>
<dimen name="card_width">400dp</dimen>
</resources>

Mistake 3: Assuming API Stability

Wrong vs Right
WRONG:
// Assume this API behavior is stable
notificationManager.notify(id, notification)
// Works today, might change next week
RIGHT:
// Use Jetpack wrapper
NotificationManagerCompat.from(context).notify(id, notification)
// Jetpack handles platform changes

Every platform API I use gets wrapped in Jetpack or a compatibility layer.

Mistake 4: Ignoring Foldable State Changes

Wrong vs Right
WRONG:
- Layout fixed at onCreate
- No handling for screen size changes during use
- User unfolds device → layout breaks
RIGHT:
- Listen for configuration changes
- Use WindowManager library for foldable state
- Recalculate size class on device posture change
FoldableStateHandler.kt
class MainActivity : AppCompatActivity() {
private val windowManager = WindowManager(this)
override fun onCreate(savedInstanceState: Bundle?) {
windowManager.addLayoutChangeListener { layoutInfo ->
when (layoutInfo.displayFeatures.find { it is FoldingFeature }) {
is FoldingFeature -> {
// Device is folded or in tabletop posture
updateLayoutForFoldState(layoutInfo)
}
null -> {
// No fold feature, standard display
updateLayoutForStandardDisplay()
}
}
}
}
}

Implementation Checklist

I use this checklist before releasing:

Multi-Device Checklist
Phase 1: Architecture Review
[ ] All platform APIs wrapped in Jetpack
[ ] No hardcoded dimensions
[ ] Configuration change handling implemented
[ ] Window size class detection in place
Phase 2: Layout Testing
[ ] COMPACT layout verified (phone, folded)
[ ] MEDIUM layout verified (tablet portrait, unfolded portrait)
[ ] EXPANDED layout verified (tablet landscape, Chrome OS)
[ ] Fold state transitions tested
[ ] Guidelines and constraints used, not fixed values
Phase 3: Device Category Testing
[ ] Phone (multiple sizes)
[ ] Foldable (folded and unfolded states)
[ ] Tablet (portrait and landscape)
[ ] Wear OS (if applicable)
[ ] Android Auto (if applicable)
Phase 4: Module Structure
[ ] Base module contains only core features
[ ] Optional features in dynamic modules
[ ] Device-specific modules conditionally delivered
[ ] Install size optimized

Summary

Android in 2026 requires a fundamental shift in how we build apps. The platform updates continuously. The device ecosystem spans wearables to automotive. Old assumptions about stable versions and standard phone screens no longer hold.

The solution is adaptive architecture:

  1. Jetpack libraries abstract platform changes and provide stable behavior
  2. Window size classes handle layout across screen categories
  3. Modular feature delivery optimizes for device types and install size

Apps that don’t adapt face user complaints, store ranking penalties, and ecosystem exclusion. Apps built with these principles work across phones, foldables, tablets, wearables, and automotive systems.

Final Words + More Resources

My intention with this article was to help others share my knowledge and experience. If you want to contact me, you can contact by email: Email me

Here are also the most important links from this article along with some further resources that will help you in this scope:

Oh, and if you found these resources useful, don’t forget to support me by starring the repo on GitHub!

Comments