React Native

Optimizing Mobile App Battery Usage & Crash Rate (Based on a True Story)

Katarzyna SmolenApr 24, 20255 min read

No one wants to use an app that drains their battery or crashes at the worst time. One of our clients, a company building a mobile app for the boating community, found that out the hard way.

Learn how we stabilized the app, improved its crash-free rate to nearly 99%, and optimized battery usage in just three months of collaboration. Spoiler alert — the challenges weren’t exactly what we expected at first.

What is battery optimization, and why is it important?

Battery optimization isn’t just about using less power, it’s about making sure your app doesn’t waste energy on unnecessary tasks.

Apps that run in the background, use GPS or sync data, tend to drain the battery faster. This is especially true for industries like travel, boating, and fitness, where apps are constantly tracking locations or providing updates in real time. And this may become a real problem for users who expect the app to function reliably over extended periods.

Optimizing battery usage: things that worked for our boating app and might work for yours

Balancing GPS accuracy & power consumption

When we first started addressing the battery drain with our client, we immediately suspected GPS was to blame. To verify our hypothesis, we rolled out three different tracking modes (low, medium, and high) to see if adjusting the GPS frequency would make a difference. But to our surprise, it turned out reducing its accuracy didn’t solve the problem — the app was still consuming too much power, even when idle.

Digging deeper, we realized the issue wasn’t GPS accuracy itself, but how often the app was requesting location updates. Instead of constantly polling for GPS data, we adjusted the app’s logic to trigger updates only when necessary, focusing on user activity — like navigating or moving — rather than updating non-stop in the background. This made a big difference in reducing battery consumption while still providing accurate location information when it was needed most.

Optimizing re-renders & background tasks

After profiling the app, we found out other factors contributing to excessive battery usage were unnecessary re-renders and continuous network requests. Even when the app wasn’t actively being used, the CPU remained under heavy load. We discovered that some of the screens were re-rendering every second, even when they weren’t visible or the app was running in the background.

In addition, the app was making continuous network requests to the local database, even when there was no new location data to update. This constant activity kept the CPU running for no reason, draining the battery. To fix this, we ensured that invisible screens wouldn’t trigger re-renders, which allowed us to reduce CPU usage from 170% to just 30% on an iPhone 13.

We also implemented a system to query the database only when new location data was available, reducing unnecessary network requests and improving battery life. This change not only helped with battery optimization but also allowed us to fix a few visual bugs that would occasionally pop up.

Improving the app’s crash-free rate

Fixing the headless mode on Android

Optimizing battery usage wasn’t our only goal. The Android app version was prone to frequent crashes, especially during route tracking. This caused a frustrating issue where users who started recording a route and then reopened the app after it had been running in the background would often find themselves stuck in a restart loop.

We found that the root cause was tied to the Android’s headless mode. This mode allows apps to run in the background without a visible UI, helping to save battery. However, when the app transitioned from headless mode back to the regular UI, the app’s logic wasn’t equipped to handle the change properly. This caused unnecessary resets and triggered the frustrating restart loop, as the app failed to recognize that the UI was active and incorrectly assumed it needed to reset.

To resolve the issue, we first needed to replicate the problem in a controlled environment. We simulated the transition between the headless mode and regular mode while ensuring that background location tracking continued to work as expected. By replicating the app’s behavior in both modes, we pinpointed the exact point where the transition was failing and causing the issue.

Improving the crash-free rate required us to fine-tuning the app’s task-handling logic to prevent unnecessary resets in the headless mode. Once we implemented the fixes, we saw a significant improvement in stability. In just three months, the app’s crash-free rate increased from 95% to around 98.5–99%, making a huge difference for users who rely on it for long trips.

Resolving global header issues

There was one more issue that was significantly affecting both the app stability and the user experience — we discovered that the headers in the Android app were getting stuck behind the top bar, making them inaccessible to users.

At first, the issue seemed simple — one of the screens had two headers, and we thought the solution would be to remove the extra one. However, we quickly realized the problem was more widespread and required more attention. The first header was a default for basic navigation, while the second was dynamic, designed with custom actions. The dynamic header was being generated using the setTimeout function, which introduced a delay in rendering. This caused unnecessary lag, re-renders, flickering, and occasional crashes on the screen.

To solve this, we created a global header component. By using a custom header component with direct props for the left and right components, we were able to eliminate the delay and improve performance:

<Header
  title={...}
  leftComponent={...}
  rightComponent={...}
/>

This helped to avoid the pitfalls of setTimeout:

useEffect(() => {
  setTimeout(() => {
    navigation.setOptions({
      headerRight: ...,
      headerLeft: ...,
    });
  }, delay);
}, [navigation]);

With the new approach, the headers started rendering instantly, making the app faster and more reliable.

Summing up

Optimizing app’s battery usage and stability can feel like an uphill battle, but it doesn’t have to. As you can see, the reasons behind poor app performance or excessive power consumption aren’t always as obvious as you may think. That’s why it’s good to work with an experienced team that keeps an open mind.

If you’re facing challenges with your project — whether it’s optimizing battery usage, improving crash rate, or something that’s hard to pinpoint — we’re here to help. Drop us a message at https://swmansion.com/contact/projects or join our Discord.

We’re Software Mansion: multimedia experts, AI explorers, React Native core contributors, community builders, and software development consultants.