# Performance Considerations for Listing Thousands of Packages in Universal Android Debloater

> Discover how Universal Android Debloater manages thousands of packages with optimized UI performance using HashMaps, virtual scrolling, and async fetching. Enhance your debloating experience today.

- Repository: [Universal-Debloater-Alliance/universal-android-debloater-next-generation](https://github.com/Universal-Debloater-Alliance/universal-android-debloater-next-generation)
- Tags: performance
- Published: 2026-06-20

---

**Universal Android Debloater Next Generation combines HashMap storage, virtual scrolling, asynchronous batch fetching, and selective filtering to maintain UI responsiveness when displaying thousands of Android packages.**

When managing Android devices with extensive system inventories, performance considerations for listing thousands of packages become critical to preventing UI freezes and memory bloat. The Universal Android Debloater Next Generation (UAD) implements targeted optimization strategies in its Rust codebase to ensure smooth interaction even when processing massive package lists retrieved via ADB.

## Efficient Storage with PackageHashMap

The foundation of UAD's performance starts with its choice of data structure. The codebase stores packages in a **`PackageHashMap`** defined in [`crates/uad-core/src/uad_lists.rs`](https://github.com/Universal-Debloater-Alliance/universal-android-debloater-next-generation/blob/main/crates/uad-core/src/uad_lists.rs), which provides **O(1)** lookup complexity for searches and filters.

This HashMap uses the package name as its key, allowing the application to instantly locate specific entries without iterating through the entire collection. When the user searches or applies filters, the system accesses package metadata directly rather than scanning linear arrays, which is essential when handling inventories exceeding 10,000 items.

## Selective Filtering and View Optimization

Rather than iterating over the entire package collection on every UI frame, UAD employs a dedicated filtering strategy. The function **`filter_package_lists`** in [`crates/uad-gui/src/views/list.rs`](https://github.com/Universal-Debloater-Alliance/universal-android-debloater-next-generation/blob/main/crates/uad-gui/src/views/list.rs) (lines 742‑759) produces a view-specific vector containing only packages matching the current **`UadList`** and **`PackageState`** criteria.

This approach ensures the UI painting logic only processes relevant subsets. When users switch between lists (All, System, Carrier, etc.) or toggle state filters (Installed, Removed), the system rehydrates the view with a curated slice rather than re-rendering the entire inventory.

```rust
fn filter_package_lists(&mut self) {
    let list_filter = self.selected_list.expect("UAD-list type must be selected");
    self.uad_lists.retain(|_, p| {
        // Keep only packages that belong to the chosen UAD‑list (All, System, etc.)
        (list_filter == UadList::All || p.list == list_filter)
            // Optionally also filter by package state (installed / removed)
            && matches!(p.state, PackageState::Installed | PackageState::Removed)
    });
}

```

## Virtual Scrolling for UI Performance

To prevent memory exhaustion from instantiating thousands of UI widgets simultaneously, UAD leverages the Iced framework's **`scrollable`** widget. In [`crates/uad-gui/src/views/list.rs`](https://github.com/Universal-Debloater-Alliance/universal-android-debloater-next-generation/blob/main/crates/uad-gui/src/views/list.rs) (line 438), the scrollable container wraps the package rows and internally recycles widget instances.

This means a list of 10,000 items does not create 10,000 Rust structs at once. Only the rows visible in the viewport plus a small buffer are materialized, drastically reducing memory pressure and keeping frame rates stable during rapid scrolling through large package inventories.

## Asynchronous Batch Fetching

Package retrieval from Android devices occurs through the **`fetch_packages`** function in [`crates/uad-core/src/utils.rs`](https://github.com/Universal-Debloater-Alliance/universal-android-debloater-next-generation/blob/main/crates/uad-core/src/utils.rs), which executes ADB commands asynchronously. The GUI invokes this function from [`list.rs`](https://github.com/Universal-Debloater-Alliance/universal-android-debloater-next-generation/blob/main/list.rs) (lines 777‑786) and processes packages in batches per user profile.

Because the method runs as a Future, the UI thread remains responsive while waiting for ADB shell commands to return. The application updates the internal state only when the entire batch finishes, preventing the micro-stalls that would occur if updating the interface after each individual package retrieval.

```rust
async fn fetch_packages(list: &UadList, serial: &str, user_id: Option<u64>) -> Result<(), Error> {
    // Call ADB to list packages for the given user (or all users)
    let raw = adb::run_adb(&["shell", "pm", "list", "packages"], serial).await?;
    // Parse and insert into the core `PackageHashMap`
    parse_and_store(raw, list, user_id);
    Ok(())
}

```

## Minimal UI State Updates

UAD minimizes redundant redraws through strict state management. The **`UadListState`** enum toggles only between *Loading*, *Done*, and *Failed* states, with transitions handled in `on_load_uad_list` (line 854) and `on_list_row` (line 949).

The view redraws exclusively when these high-level states change, not during internal package iterations or background processing. This convention prevents the GUI from wasting cycles repainting identical content while the backend processes data.

## Memory Optimization Techniques

The codebase avoids unnecessary allocations by using **`clone_from`** to replace large structures without new memory allocation, as seen in [`list.rs`](https://github.com/Universal-Debloater-Alliance/universal-android-debloater-next-generation/blob/main/list.rs) (line 876). The system also passes references (`&UadList`) rather than cloning data structures when functions need read-only access to package metadata.

Individual package rows are rendered through [`crates/uad-gui/src/widgets/package_row.rs`](https://github.com/Universal-Debloater-Alliance/universal-android-debloater-next-generation/blob/main/crates/uad-gui/src/widgets/package_row.rs), which constructs widgets only once per render pass. Pick-lists for users, lists, and removal types in [`list.rs`](https://github.com/Universal-Debloater-Alliance/universal-android-debloater-next-generation/blob/main/list.rs) (lines 279‑309) follow the same lazy evaluation pattern, ensuring UI construction costs remain constant regardless of package count.

## Summary

- **PackageHashMap** in [`crates/uad-core/src/uad_lists.rs`](https://github.com/Universal-Debloater-Alliance/universal-android-debloater-next-generation/blob/main/crates/uad-core/src/uad_lists.rs) provides O(1) package lookups using the package name as the key.
- **`filter_package_lists`** creates view-specific subsets in [`crates/uad-gui/src/views/list.rs`](https://github.com/Universal-Debloater-Alliance/universal-android-debloater-next-generation/blob/main/crates/uad-gui/src/views/list.rs), preventing iteration over the entire collection on every frame.
- The **Iced scrollable widget** recycles row widgets in [`list.rs`](https://github.com/Universal-Debloater-Alliance/universal-android-debloater-next-generation/blob/main/list.rs), enabling smooth scrolling through 10,000+ items without proportional memory growth.
- **`fetch_packages`** runs asynchronously in [`crates/uad-core/src/utils.rs`](https://github.com/Universal-Debloater-Alliance/universal-android-debloater-next-generation/blob/main/crates/uad-core/src/utils.rs), batching ADB calls per user profile to keep the UI thread unblocked.
- **Minimal state updates** and **clone_from** usage reduce memory churn and unnecessary redraws during heavy package operations.

## Frequently Asked Questions

### How does UAD maintain performance when listing thousands of packages?

UAD maintains performance by combining a HashMap-based storage structure for O(1) lookups, virtual scrolling to render only visible rows, and asynchronous batch fetching that prevents UI blocking during ADB operations. These architectural choices ensure memory usage scales with viewport size rather than total package count.

### What data structure does Universal Android Debloater use for package storage?

The application uses a **`PackageHashMap`** defined in [`crates/uad-core/src/uad_lists.rs`](https://github.com/Universal-Debloater-Alliance/universal-android-debloater-next-generation/blob/main/crates/uad-core/src/uad_lists.rs), keyed by package name. This structure enables constant-time lookups essential for rapid filtering and searching across large Android inventories.

### Why does UAD implement virtual scrolling for package lists?

Virtual scrolling, implemented via Iced's **`scrollable`** widget in [`list.rs`](https://github.com/Universal-Debloater-Alliance/universal-android-debloater-next-generation/blob/main/list.rs), recycles UI row widgets so that only visible items consume memory. This technique prevents the application from instantiating thousands of Rust structs simultaneously, which would otherwise cause memory bloat and frame drops on large devices.

### How does UAD fetch packages from Android devices without freezing the interface?

Package retrieval occurs through the asynchronous **`fetch_packages`** function in [`crates/uad-core/src/utils.rs`](https://github.com/Universal-Debloater-Alliance/universal-android-debloater-next-generation/blob/main/crates/uad-core/src/utils.rs), which executes ADB shell commands in a non-blocking Future. The GUI updates only after the complete batch returns, ensuring the interface remains responsive during lengthy device communication.