Mobile Development 15 min read

Designing a Dynamic Performance‑Degradation System for iOS & Android Apps

An in‑depth guide shows how to build a client‑side dynamic degradation framework for iOS and Android that monitors CPU, memory, battery and network speed, classifies them into levels, and notifies the business layer to adapt UI and network usage for smoother user experience.

Sohu Tech Products
Sohu Tech Products
Sohu Tech Products
Designing a Dynamic Performance‑Degradation System for iOS & Android Apps

Background

Both iOS and Android apps can suffer performance issues in production due to hardware constraints, network conditions, or code quality. Providing a smooth user experience requires handling these issues at runtime.

Service‑side downgrade and circuit‑breaker

Server‑side mechanisms can be referenced: when the server is overloaded or data errors occur, it may downgrade responses (e.g., return cached DB data). A circuit‑breaker stops calling an unavailable micro‑service, causing UI effects such as missing avatars or disabled coupons.

Solution Overview

The client implements a dynamic degradation system that monitors CPU, memory, battery, and network speed, classifies them into levels, and notifies the business layer to adjust behavior (e.g., lower image size, pause video playback).

Problem decomposition

Two main problem categories: performance (CPU, memory, battery) and network speed. Each metric is split into three levels (low, middle, high) using defined thresholds.

System architecture

The system consists of three core classes: DynamicLevelManager: orchestrates monitoring and decision making, registers a timer using dispatch_source_t that fires every 1.5 s. DynamicLevelMonitor: provides methods cpuUsageForApp, useMemoryForApp, batteryUsageForApp, and obtains network speed from a custom QUICManager. DynamicLevelDecision: receives raw metrics, converts them to enum levels, and computes a combined MultiplePerformanceLevel or NetworkSpeedLevel.

Enums

typedef NS_ENUM(NSUInteger, MultiplePerformanceLevel) {
    MultiplePerformanceLevelNormal,
    MultiplePerformanceLevelLow,
    MultiplePerformanceLevelVeryLow,
};

typedef NS_ENUM(NSUInteger, CPUUsageLevel) {
    CPUUsageLevelLow,
    CPUUsageLevelHigh,
    CPUUsageLevelOverclock,
};

typedef NS_ENUM(NSUInteger, MemoryUsageLevel) {
    MemoryUsageLevelLow,
    MemoryUsageLevelMiddle,
    MemoryUsageLevelHigh,
};

typedef NS_ENUM(NSUInteger, BatteryUsageLevel) {
    BatteryUsageLevelLow,
    BatteryUsageLevelMiddle,
    BatteryUsageLevelHigh,
};

typedef NS_ENUM(NSUInteger, NetworkSpeedLevel) {
    NetworkSpeedLevelNormal,
    NetworkSpeedLevelLow,
    NetworkSpeedLevelVeryLow,
};

Performance level calculation

The monitor returns raw values (CPU usage 0‑1, memory usage 0‑1, battery 0‑100). DynamicLevelDecision maps each raw value to its corresponding enum, then applies a series of mutually exclusive rules to produce performanceLevel. Example rules: if battery is high → very low; if CPU is overclock and memory is high → very low; otherwise combine thresholds to set low or normal.

Network speed calculation

Network speed is measured in kb/s by QUICManager. The last five samples are kept in a FIFO array; counts of values ≤ 200 kb/s and ≤ 50 kb/s determine whether the level is Low or VeryLow. Otherwise the level stays Normal.

Implementation snippets

/// 开启动态降级监控系统
- (void)openLevelAnalyze {
    self.sourceHandle = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0,
        dispatch_get_global_queue(0, 0));
    dispatch_source_set_timer(self.sourceHandle,
        dispatch_time(DISPATCH_TIME_NOW, 0), 1.5 * NSEC_PER_SEC, 0);
    dispatch_source_set_event_handler(self.sourceHandle, ^{
        CGFloat cpuUsageValue = [[DynamicLevelMonitor sharedInstance] cpuUsageForApp];
        NSInteger memoryUsageValue = [[DynamicLevelMonitor sharedInstance] useMemoryForApp];
        CGFloat batteryUsageValue = [[DynamicLevelMonitor sharedInstance] batteryUsageForApp];
        [[DynamicLevelDecision sharedInstance] calculatePerformanceLevelWithMemoryValue:memoryUsageValue
            cpuValue:cpuUsageValue batteryValue:batteryUsageValue completionBlock:^(MemoryUsageLevel memoryLevel, CPUUsageLevel cpuLevel, BatteryUsageLevel batteryLevel, MultiplePerformanceLevel performanceLevel) {
                if (performanceLevel != self.currentPerformanceLevel) {
                    [self postPerformanceNotifiWithPerformanceLevel:performanceLevel
                        memoryLevel:memoryLevel cpuLevel:cpuLevel batteryLevel:batteryLevel];
                }
        }];

        CGFloat networkSpeed = [[QUICManager shareQUICManager] currentNetworkSpeed];
        [[DynamicLevelDecision sharedInstance] calculateNetworkLevelWithNetworkSpeed:networkSpeed completionBlock:^(NetworkSpeedLevel speedLevel) {
            if (speedLevel != self.currentNetworkSpeedLevel) {
                [self postPerformanceNotifiWithNetworkSpeedLevel:speedLevel];
            }
        }];
    });
    dispatch_resume(self.sourceHandle);
}
- (void)closeLevelAnalyze {
    dispatch_source_cancel(self.sourceHandle);
}

Additional helper methods retrieve raw metrics:

/// 当前app内存使用量,返回百分比
- (NSInteger)useMemoryForApp {
    task_vm_info_data_t vmInfo;
    mach_msg_type_number_t count = TASK_VM_INFO_COUNT;
    kern_return_t kr = task_info(mach_task_self(), TASK_VM_INFO,
        (task_info_t)&vmInfo, &count);
    if (kr == KERN_SUCCESS) {
        int64_t used = (int64_t)vmInfo.phys_footprint;
        int64_t total = [[NSProcessInfo processInfo] physicalMemory];
        return used / total;
    }
    return -1;
}
/// 当前app的CPU使用率
- (CGFloat)cpuUsageForApp {
    kern_return_t kr = task_threads(mach_task_self(), &thread_list, &thread_count);
    if (kr != KERN_SUCCESS) return -1;
    float total = 0;
    for (int i = 0; i < thread_count; i++) {
        thread_info_count = THREAD_INFO_MAX;
        kr = thread_info(thread_list[i], THREAD_BASIC_INFO,
            (thread_info_t)thinfo, &thread_info_count);
        if (kr != KERN_SUCCESS) return -1;
        thread_basic_info_t basic = (thread_basic_info_t)thinfo;
        if (!(basic->flags & TH_FLAGS_IDLE)) {
            total += basic->cpu_usage / (float)TH_USAGE_SCALE;
        }
    }
    vm_deallocate(mach_task_self(), (vm_offset_t)thread_list,
        thread_count * sizeof(thread_t));
    return total;
}

Business‑side handling

When PerformanceLevelChanged is posted, the UI layer can pause video auto‑play if the level is veryLow. It can also warn the user when battery level is low (below 6 %). When NetworkSpeedLevelChanged arrives, the app may request lower‑resolution images (80 % or 60 % compression) to improve loading on weak networks, adjusting the URL parameters accordingly.

Key takeaways

Separate monitoring, decision, and notification responsibilities.

Use a short‑interval GCD timer to collect metrics without blocking the main thread.

Apply a sliding‑window of recent samples for more stable network‑speed classification.

Expose enum‑based levels so business code can react uniformly.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

Mobile DevelopmentiOSAndroidPerformance MonitoringNetwork SpeedBattery ManagementDynamic Degradation
Sohu Tech Products
Written by

Sohu Tech Products

A knowledge-sharing platform for Sohu's technology products. As a leading Chinese internet brand with media, video, search, and gaming services and over 700 million users, Sohu continuously drives tech innovation and practice. We’ll share practical insights and tech news here.

0 followers
Reader feedback

How this landed with the community

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.