メインコンテンツに移動

モバイルアプリのバッテリー消費を抑える方法|Android・iOS・Flutterの省電力設計と実装例

モバイルアプリの品質は、機能の多さや画面の美しさだけでは決まりません。アプリを使用している間に端末が熱くなる、短時間でバッテリー残量が減る、アプリを閉じても通信や位置情報取得が続くといった問題は、利用者からの低評価やアプリ削除につながります。特に、地図、配送、動画、音声、健康管理、業務管理などのアプリでは、継続的な処理による消費電力を慎重に管理する必要があります。

バッテリー消費を抑えるためには、一つの特別な省電力機能を追加するのではなく、処理を実行する回数、通信を開始する頻度、位置情報の精度、画面の再描画、バックグラウンド処理の時間を総合的に減らすことが重要です。Appleは「処理量を減らす」「処理を効率化する」「機能を誤用しない」という段階で電力消費を改善する方法を案内しており、Androidでも処理を適切な時期にまとめて実行することが推奨されています。

省電力化では、感覚的にプログラムを書き換えるのではなく、実機で消費電力を測定し、負荷の高い処理を特定してから改善する必要があります。本記事では、モバイルアプリでバッテリーを消費する原因から、Android、iOS、Flutterでの実装方法、測定、試験、運用まで詳しく解説します。

1. モバイルアプリのバッテリー節約とは

モバイルアプリのバッテリー節約とは、利用者が必要とする機能や応答性を維持しながら、中央処理装置、通信、位置情報、画面描画、保存処理などが使用する電力を減らすことです。単純に処理を遅くするのではなく、不要な処理を取り除き、必要な処理を適切な時期にまとめます。

1.1 必要な処理だけを実行する

省電力化の最も重要な原則は、不要な処理を実行しないことです。画面に表示されていないデータの更新、利用されていない位置情報の取得、変化していない情報の再計算などは、利用者に価値を与えずに電力だけを消費します。

アプリが背景へ移動した場合は、画面更新、短い間隔のタイマー、不要な通信を停止します。iOSでは、利用者がアプリを操作しなくなると処理が制限または停止される仕組みがあり、背景で長時間高い処理負荷を発生させる設計は避ける必要があります。

1.2 処理回数をまとめて減らす

小さな通信や保存を何度も実行すると、毎回、通信機器や保存装置を活動状態へ戻す必要があります。同じ量のデータを処理する場合でも、細かく分けて頻繁に実行するより、一定量をまとめて処理したほうが効率的です。

Appleは通信と入出力を可能な範囲でまとめ、不要な端末起動を減らすことを推奨しています。Androidでも、背景作業をまとめて実行できるよう、実行時間に柔軟性を持たせることが省電力につながります。

1.3 バッテリーだけでなく発熱も抑える

中央処理装置、画像処理装置、通信機器を長時間利用すると、端末内部の温度が上がります。発熱が大きくなると、基本ソフトが処理性能を制限し、画面の動きや計算速度が低下することがあります。

処理時間と電力消費を減らせば、発熱、動作遅延、強制終了の危険も抑えやすくなります。Appleも、電力を多く使用する機能を減らすことは、アプリの信頼性と端末全体の利用体験を改善すると説明しています。

1.4 基本ソフトの省電力機能を活用する

AndroidとiOSには、背景処理、通信、位置情報、アプリの活動時間を調整する省電力機能があります。アプリが独自に実行時刻を固定するより、基本ソフトへ実行条件を伝え、適切な時刻を選ばせるほうが効率的です。

AndroidではWorkManagerなどを利用して、通信接続、充電状態、保存領域などの条件を指定できます。iOSではBackgroundTasksを利用し、利用頻度や端末状態を考慮した実行時刻を基本ソフトへ委ねられます。

1.5 測定してから改善する

バッテリー消費の原因は、プログラムの見た目だけでは正確に判断できません。通信、位置情報、タイマー、外部ライブラリなど、予想していなかった処理が背景で継続している場合があります。

AndroidではBatterystatsとBattery Historian、iOSではInstrumentsのPower Profilerを利用して、実機上の電力使用状況を確認できます。最適化前後で同じ操作を実行し、処理時間、通信回数、端末起動回数などを比較することが重要です。

2. 省電力設計と性能改善の違い

省電力設計と性能改善は密接に関係していますが、同じものではありません。処理を高速化して電力消費を減らせる場合もあれば、応答速度を高めるために多くの電力を消費する場合もあります。

比較項目省電力設計性能改善
主な目的電力消費と発熱を減らす処理時間や応答時間を短くする
対象背景処理、通信、位置情報、画面、保存計算、描画、起動、データ処理
重要な指標消費電力、活動時間、端末起動回数処理時間、画面速度、応答時間
実行回数不要な処理を減らす一回の処理を速くする
背景処理遅延やまとめ処理を許可する必要な処理を短時間で終える
通信回数を減らし、まとめて送るデータ量と待機時間を減らす
位置情報精度と取得間隔を下げる必要な位置を速く取得する
画面描画不要な再描画を防ぐ一回の描画を高速化する

2.1 処理速度が速くても電力効率が良いとは限らない

大量の処理能力を使用して短時間で計算を終える方法は、操作の応答性を高められます。しかし、常に最大性能で処理を実行すると、瞬間的な電力消費と発熱が大きくなる可能性があります。

一方、処理を低速で長時間継続することも効率的とは限りません。省電力設計では、処理時間、処理回数、利用する機器、利用者が必要とする応答速度の均衡を考えます。Appleは、処理を減らすだけでなく、必要な処理を効率的に実行することを推奨しています。

2.2 前面処理と背景処理では優先順位が異なる

利用者が画面を操作している間は、応答速度が重要です。ボタンを押した後の処理や画面移動を過度に遅らせると、電力を節約できても利用体験が悪化します。

利用者が待っていない背景処理は、即時に実行する必要がないことがあります。同期、画像整理、記録送信などを、充電中や通信環境が良い時刻まで遅延させれば、体験を損なわずに電力を節約できます。

2.3 通信速度と通信回数を分けて考える

通信データを圧縮すれば、一回の通信時間を短くできます。しかし、数秒ごとに通信を開始していれば、通信機器が頻繁に高出力状態へ戻るため、電力消費が増える可能性があります。

省電力化では、一回のデータ量だけでなく、通信開始回数と通信間隔を確認します。Androidの公式資料でも、無線通信機器を頻繁に活動させることが、大きなバッテリー消費要因になり得ると説明されています。

2.4 記憶領域の削減だけでは不十分である

不要な物体生成や大きな画像を減らすと、記憶領域の使用量を抑えられます。しかし、画面外で動き続けるタイマーや位置情報取得が残っていれば、バッテリー消費は改善しません。

性能改善では、中央処理装置、記憶領域、描画速度を個別に測定します。省電力改善では、それらに加えて通信機器、位置情報機器、画面、端末起動状態を総合的に調査します。

2.5 両方を同時に改善する

不要な計算を取り除く、画面再描画を減らす、通信をまとめるといった対応は、性能と省電力の両方を改善できます。処理が早く終われば、中央処理装置や通信機器が休止状態へ戻るまでの時間も短くなります。

ただし、画像品質、更新頻度、位置精度を無条件に下げるべきではありません。利用者が必要とする品質水準を決め、その水準を維持できる最小限の処理量を目標にします。

3. バッテリー消費が増える主な原因

モバイルアプリの消費電力は、一つの処理だけで決まるものではありません。中央処理装置、通信、位置情報、画面描画、タイマー、保存処理などが組み合わさってバッテリーを消費します。

3.1 中央処理装置の継続的な使用

複雑な計算、データ変換、暗号化、画像処理、無限に近い繰り返し処理は、中央処理装置を長時間活動させます。処理が背景でも継続すると、利用者がアプリを使用していない間も電力を消費します。

計算結果を再利用できる場合は保存し、同じ入力に対して何度も計算しないようにします。Flutterでは中央処理装置の記録機能を利用し、どの関数に処理時間が集中しているかを確認できます。

3.2 頻繁なネットワーク通信

数秒ごとの状態確認、短い間隔の同期、小さなデータの個別送信は、無線通信機器を頻繁に活動させます。通信が完了しても、機器はすぐに最低電力状態へ戻らない場合があるため、通信回数が重要です。

利用者操作による更新、アプリ自身による定期更新、サーバーからの更新通知を区別し、可能な限り通信をまとめます。サーバー側の変更を確認するためだけに、短い間隔で問い合わせ続ける設計は避けます。

3.3 高精度な位置情報の継続取得

衛星測位を利用した高精度な位置情報を短い間隔で取得すると、端末の位置情報機器と中央処理装置が継続的に動作します。背景でも取得を続けるアプリでは、特に大きな消費要因になります。

AndroidとiOSの両方で、必要以上に高い精度を要求せず、取得間隔や移動距離の条件を設定することが推奨されています。位置情報が不要になった時点で更新を停止することも重要です。

3.4 不要な画面再描画と動き

画面上の小さな状態変更によって画面全体を再描画すると、中央処理装置と画像処理装置の負荷が増えます。常時動く装飾、背景動画、過度な透明効果も継続的な描画を発生させます。

Flutterでは、重い描画処理、不要な保存層、過度な透明処理などを避けることが性能改善の指針として示されています。滑らかな画面を維持しながら、利用者に価値のない描画を削除します。

3.5 タイマー・警報・端末起動の乱用

短い間隔のタイマーや正確な時刻を指定する警報は、端末が休止状態へ入る機会を減らします。背景で端末を何度も起動すると、短い処理でも総合的な消費電力が大きくなります。

Androidは、正確な警報が基本ソフトによるまとめ処理を難しくし、電力を多く消費する可能性があるため、必要な場合に限定するよう案内しています。Battery Historianでは、頻繁な端末起動警報や継続的な位置情報利用を確認できます。

4. バックグラウンド処理を省電力化する

バックグラウンド処理は、アプリを開いていない間にも同期や整理を行える便利な機能です。しかし、実行時間や頻度を適切に管理しなければ、バッテリー消費の大きな原因になります。

4.1 即時実行が必要か判断する

背景作業を登録する前に、利用者が結果をすぐに必要としているかを判断します。今すぐ必要でない送信、整理、画像処理、統計処理は、基本ソフトが効率的な時刻を選択できるようにします。

Androidでは、延期可能で確実に完了させたい作業にWorkManagerが推奨されています。iOSでも、充電中や利用活動が少ない時間帯へ処理を延期する背景作業方式が用意されています。

4.2 実行条件を指定する

画像の大量送信、データベース整理、地図情報の事前取得などは、通信接続中、充電中、保存領域に余裕がある場合だけ実行できます。条件を指定すれば、不利な状態での再試行を減らせます。

条件を厳しくしすぎると作業が長期間実行されないため、業務上の期限も考慮します。必須作業と任意作業を分け、重要度に応じて条件を設定します。

Android WorkManagerの実装例

import android.content.Context import androidx.work.Constraints import androidx.work.CoroutineWorker import androidx.work.ExistingWorkPolicy import androidx.work.NetworkType import androidx.work.OneTimeWorkRequestBuilder import androidx.work.WorkManager import androidx.work.WorkerParameters

class DataSyncWorker(
   context: Context,
   parameters: WorkerParameters
) : CoroutineWorker(context, parameters) {

   override suspend fun doWork(): Result {
       return try {
           syncPendingData()
           Result.success()
       } catch (error: Exception) {
           Result.retry()
       }
   }

   private suspend fun syncPendingData() {
       // 未送信データをまとめて同期する
   }
}

fun scheduleDataSync(context: Context) {
   val constraints = Constraints.Builder()
       .setRequiredNetworkType(
           NetworkType.CONNECTED
       )
       .setRequiresBatteryNotLow(true)
       .build()

   val request =
       OneTimeWorkRequestBuilder<DataSyncWorker>()
           .setConstraints(constraints)
           .build()

   WorkManager.getInstance(context)
       .enqueueUniqueWork(
           "data_sync",
           ExistingWorkPolicy.KEEP,
           request
       )
}

この例では、通信接続があり、バッテリー残量が低くない場合に同期します。同じ名前の処理を重複登録しないため、画面を開くたびに同じ同期作業が増える問題も防げます。

4.3 定期作業の間隔を長くする

定期作業は、実際に必要な更新頻度から設定します。利用者が一日に数回しか確認しない情報を、数分ごとに更新する必要はありません。

周期を短くしても、AndroidやiOSが必ずその時刻に実行するとは限りません。正確な時刻が不要な処理は柔軟にスケジュールし、サーバーからの通知やアプリ起動時の更新と組み合わせます。

4.4 失敗時に即時再試行しない

通信障害やサーバー停止時に、失敗直後から繰り返し再試行すると、通信と中央処理装置を継続的に使用します。障害が長引くほど、バッテリーと通信量を無駄に消費します。

再試行間隔を段階的に長くする方式を利用し、通信状況が改善するまで待ちます。WorkManagerは柔軟な再試行方針と指数的な待機時間を設定できます。

4.5 不要になった作業を取り消す

利用者がログアウトした、同期対象を削除した、機能を無効にした場合は、登録済みの背景作業を取り消します。不要な作業が残ると、古い利用者情報で通信を試みたり、失敗と再試行を繰り返したりします。

画面の状態だけでなく、作業管理機能に登録された処理も削除対象に含めます。利用者やデータの識別子を作業名へ含めると、対象を明確にして取り消しやすくなります。

5. ネットワーク通信を省電力化する

通信処理は、モバイルアプリにおける代表的な電力消費要因です。送受信するデータ量だけでなく、接続を開始する回数、通信環境、再試行方法を最適化します。

5.1 小さな通信をまとめる

一件の操作ごとに即座に通信するのではなく、業務上許される範囲で複数の変更をまとめて送信します。閲覧履歴、分析情報、未送信記録などは、一定量がたまった時点で送信できます。

AppleとAndroidの資料では、小さな通信を何度も行うのではなく、通信をまとめて無線機器の活動回数を減らす方法が推奨されています。

5.2 定期確認より通知を利用する

サーバーに変更があるかを数秒ごとに問い合わせる方式は、変化がない場合でも通信を続けます。更新頻度が低い情報ほど、無駄な問い合わせが増えます。

サーバー側から通知を送り、必要な場合だけデータを取得する方式へ変更します。ただし、通知を受信するたびに大量通信を開始するのではなく、重要度と利用者状態を確認します。

5.3 保存済みデータを再利用する

毎回同じ画像、設定、分類情報を取得すると、通信量と処理時間が増えます。変更頻度の低い情報は端末に保存し、更新時刻や版情報を使って必要な場合だけ再取得します。

保存したデータには有効期限を設定し、古い情報を無期限に表示しないようにします。オフライン優先構成を採用すると、通信状態が悪い環境での再試行も減らしやすくなります。

5.4 画面外のデータを取得しすぎない

一覧を開いた時点で、数百件の商品詳細や高解像度画像をすべて取得すると、利用者が見ないデータにも通信と処理が発生します。最初に必要な範囲だけを読み込みます。

利用者が画面を移動した時点で次のデータを事前取得すると、待ち時間と通信量の均衡を取りやすくなります。事前取得量は通信環境、保存容量、データの大きさに応じて調整します。

5.5 充電中やWi-Fi接続時に延期する

大容量の動画送信、地図更新、機械学習モデル取得などは、利用者が即時結果を必要としない場合、充電中またはWi-Fi接続時まで延期できます。

Appleは任意の通信処理を充電中やWi-Fi利用時など、より効率的な時刻へ延期することを推奨しています。Androidでも背景作業に通信種類や充電状態の条件を設定できます。

6. 位置情報によるバッテリー消費を抑える

位置情報は、地図、配車、配送、運動記録、店舗検索などに必要ですが、精度と取得頻度によっては大きな電力を消費します。機能ごとに必要な精度を明確にすることが重要です。

6.1 必要以上の精度を要求しない

都道府県や地域を判断するだけの機能に、数メートル単位の位置精度は必要ありません。高精度を要求すると、衛星測位など追加の機器が使用される可能性があります。

Appleは、必要以上に高い精度を指定すると不要な機器を動作させ、電力を浪費すると説明しています。Androidでも、機能の目的に合った精度と優先度を選択する必要があります。

6.2 取得間隔を短くしすぎない

地図上の案内では短い間隔が必要ですが、店舗検索や天気情報では、数秒ごとの位置更新は不要です。更新間隔を短くすると、位置取得だけでなく、取得後の通信や画面描画も増えます。

Androidでは最小更新間隔を過度に短くしないことが推奨されています。受け取った位置情報を利用して高負荷の入出力や通信を開始する場合は、その処理頻度も含めて調整します。

6.3 移動距離の条件を設定する

利用者がほとんど移動していない場合、同じ位置情報を繰り返し処理する必要はありません。一定距離以上移動した場合だけ更新を受け取るようにします。

iOSではdistanceFilterを設定し、指定距離を移動するまで新しい位置通知を抑えられます。目的に合った距離条件を設定すると、位置取得と後続処理の回数を減らせます。

iOSの位置情報設定例

import CoreLocation

final class EfficientLocationService:
   NSObject,
   CLLocationManagerDelegate {

   private let manager = CLLocationManager()

   override init() {
       super.init()

       manager.delegate = self
       manager.desiredAccuracy =
           kCLLocationAccuracyHundredMeters
       manager.distanceFilter = 200
       manager.pausesLocationUpdatesAutomatically = true
   }

   func start() {
       manager.requestWhenInUseAuthorization()
       manager.startUpdatingLocation()
   }

   func stop() {
       manager.stopUpdatingLocation()
   }

   func locationManager(
       _ manager: CLLocationManager,
       didUpdateLocations locations: [CLLocation]
   ) {
       guard let latest = locations.last else {
           return
       }

       processLocation(latest)
   }

   private func processLocation(
       _ location: CLLocation
   ) {
       // 必要な位置処理だけを実行する
   }
}

この例では最高精度を使用せず、200メートル以上の移動を更新条件としています。必要な位置情報を取得した後は、stop()を呼び出して更新を終了します。

6.4 一度だけ必要なら継続取得しない

現在地を一度表示するだけの画面で、位置情報更新を開始したままにすると、画面を離れた後も取得が続く可能性があります。一度の取得と継続的な追跡を分けます。

現在地を取得した後、目的を達成した時点で位置更新を停止します。画面の破棄、利用者による追跡停止、ログアウトなど、複数の終了条件を用意します。

6.5 背景位置情報を必要な機能に限定する

背景位置情報は、配送追跡、運動記録、ナビゲーションなど、アプリを表示していない間にも位置が必要な機能に限定します。店舗検索や一時的な現在地表示では、通常、前面利用中の権限で十分です。

Androidでは背景位置情報の取得頻度が基本ソフトによって制限されます。iOSでも、位置情報の背景実行を有効にする前に、利用目的と代替方式を検討する必要があります。

7. 画面描画と画像処理を最適化する

画面が表示されている間は、中央処理装置と画像処理装置が継続的に使用されます。再描画の範囲、画像の大きさ、動きの頻度を適切に管理します。

7.1 変更部分だけを再描画する

時計や進行状況のような小さな値が変わるたびに、画面全体を再構築すると不要な処理が増えます。状態を小さく分け、変更が必要な部品だけを更新します。

Flutterでは、構築処理の負荷を小さく保ち、不要な部品再構築を防ぐことが性能改善の中心です。定数として扱える部品は定数化し、変更状態を必要な範囲へ限定します。

7.2 画面外の一覧項目を生成しない

長い一覧のすべての項目を最初に生成すると、記憶領域と描画処理が増えます。現在表示している範囲と、その周辺だけを生成する遅延構築を利用します。

画像や動画を含む一覧では、画面外へ移動した項目の再生や動きを停止します。利用者が見ていない内容へ処理能力を使わないことが重要です。

7.3 表示サイズに合った画像を使用する

小さな縮小表示に対して、数千画素の高解像度画像を読み込むと、通信量、復号処理、記憶領域の使用量が増えます。表示領域に近い大きさの画像を取得します。

サーバー側で複数の画像サイズを用意し、端末の表示密度と画面領域に応じて選択します。拡大表示が必要な場合だけ、高解像度画像を追加で取得します。

7.4 不要な常時アニメーションを停止する

読み込みが完了しているのに動き続ける表示、画面外の動画、装飾だけの背景動作は、継続的な画面描画を発生させます。利用者が確認していない間は停止します。

アプリが背景へ移動した場合は、アニメーション制御機能、動画再生、ゲームループを停止します。前面へ戻った時点で必要なものだけを再開します。

7.5 高負荷な視覚効果を限定する

複数の透明層、ぼかし、影、切り抜き、保存層などを大量に組み合わせると、画面描画の負荷が増えます。重要な画面表現に限定し、一覧内で繰り返し使用しすぎないようにします。

Flutterの公式性能資料では、一部の保存層、透明処理、切り抜き処理が高い費用を持つ可能性が示されています。性能表示を使い、実際に処理時間が増えている箇所を確認します。

8. 中央処理装置とデータ処理を省電力化する

複雑な計算を実行するアプリでは、一回の処理を効率化するだけでなく、同じ処理を繰り返さない構造が必要です。画面更新と業務計算を分離し、必要な時点だけ実行します。

8.1 同じ計算結果を再利用する

入力データが変わっていないのに、画面を再構築するたびに並べ替え、集計、変換を実行すると、中央処理装置を無駄に使用します。入力と結果を対応付けて保存します。

ただし、保存結果を増やしすぎると記憶領域を圧迫します。計算費用が高く、同じ入力が繰り返される処理を優先して再利用します。

8.2 監視方式を定期確認方式より優先する

状態が変わったかを短い間隔で確認する処理は、変化がない場合でも中央処理装置を起動します。データ変更時に通知を受け取れる場合は、通知型の処理へ変更します。

データベース、状態管理、通信接続などで変更通知を利用し、一定間隔の確認を減らします。タイマーが必要な場合も、画面表示中だけ実行するなど、活動範囲を限定します。

8.3 大量処理を小さく分ける

大量の画像変換やデータ集計を一度に実行すると、長時間高い負荷が続き、画面操作も遅くなる可能性があります。処理を分割し、利用者操作を妨げないようにします。

分割処理の間隔を細かくしすぎると、かえって端末の活動時間が長くなります。短時間でまとめて完了する部分と、延期できる部分を分けます。

8.4 入出力をまとめる

一件ごとにデータベースへ書き込むより、複数件を一つの処理として保存したほうが、保存装置を起動する回数を減らせます。操作履歴や分析記録などで効果があります。

Appleは、ファイル書き込みなどの入出力が端末を休止状態から起こす可能性があるため、書き込み回数を減らし、まとめることを推奨しています。

8.5 外部ライブラリの処理を確認する

広告、分析、障害記録、位置情報、通知などの外部ライブラリが、独自の通信やタイマーを実行している場合があります。アプリ側の処理だけを改善しても、外部ライブラリが活動を続けていれば効果が限定されます。

利用していない機能を無効にし、送信間隔、位置情報利用、背景動作の設定を確認します。更新後に動作が変わる可能性があるため、ライブラリ更新時にも電力測定を行います。

9. タイマー・警報・通知を最適化する

定期実行は簡単に実装できますが、実行回数が増えるほど端末を休止状態から起動する可能性が高まります。必要な正確さと利用者価値を基準に設計します。

9.1 正確な警報を必要な場合だけ使用する

目覚まし、服薬時刻、利用者が設定した予定など、指定時刻に動作すること自体が機能の中心である場合は、正確な警報が必要です。一般的な同期や記録送信には必要ありません。

Androidは、正確な警報が電力と端末資源に大きな影響を与える可能性があり、利用者が意図した時刻機能に限定するよう案内しています。

9.2 不正確な実行時刻を許可する

数分から数十分のずれが問題にならない処理は、実行時刻に余裕を持たせます。基本ソフトは複数アプリの作業をまとめ、端末の活動回数を減らせます。

Androidの警報や背景作業では、正確さが不要な場合に柔軟なスケジュールを選択できます。長い処理は警報の受信処理内で直接行わず、WorkManagerなどへ引き渡します。

9.3 画面用タイマーを背景で停止する

残り時間、現在時刻、進行状況を表示するタイマーは、画面が見えている間だけ必要な場合があります。アプリが背景へ移動したら停止し、復帰時に現在時刻との差から表示を再計算します。

タイマーの回数を数えて状態を維持すると、背景停止中に値がずれる可能性があります。開始時刻や終了時刻を保存し、表示時に差分を計算する方式が効率的です。

9.4 通知のために定期通信しない

新着情報を確認するために短い間隔でサーバーへ問い合わせると、変化がない場合も通信します。遠隔通知を利用し、変更がある場合だけアプリへ知らせます。

通知受信後も、利用者がすぐに情報を見る必要がない場合は、端末状態を考慮して取得を遅延できます。通知ごとに重い同期を実行しないようにします。

9.5 不要な通知を減らす

通知は画面点灯、音、振動、アプリ処理などを引き起こす可能性があります。価値の低い通知を大量に送ると、利用体験とバッテリーの両方に悪影響を与えます。

利用者が通知種類と頻度を設定できるようにします。同じ出来事に対する重複通知を防ぎ、複数件を一つの要約通知へまとめます。Appleは、不要な通知などによって端末を起動しないよう案内しています。

10. Androidアプリのバッテリー消費を抑える

Androidでは、Doze、App Standby、WorkManager、背景実行制限などを前提に設計します。制限を回避しようとするのではなく、基本ソフトに作業の目的と条件を伝えます。

10.1 Dozeへ適切に対応する

端末が使用されず、充電されていない状態では、Dozeによって背景の中央処理装置と通信処理が延期されます。通常の背景同期は、保守時間帯まで実行されない可能性があります。

緊急性のない作業はDozeの動作を受け入れます。省電力制限の除外を利用者へ要求する方法は、機能上不可欠な場合に限定します。

10.2 App Standbyを考慮する

長期間利用されていないアプリは、App Standbyによって背景通信や作業の利用可能量が制限されます。アプリが頻繁に使われることを前提にした同期設計は、期待どおり動作しない場合があります。

利用者がアプリを開いた時点で必要な情報を更新し、背景処理だけに依存しない構成にします。Androidでは利用頻度などに応じてアプリを区分し、利用できる端末資源を調整します。

10.3 WorkManagerを適切に利用する

WorkManagerは、アプリ終了や端末再起動をまたいで完了させたい、延期可能な作業に適しています。画面表示中の短い非同期処理を、すべてWorkManagerへ登録する必要はありません。

作業の種類に合わない背景機能を選ぶと、電力効率や性能を悪化させる可能性があります。即時処理、利用者開始処理、延期可能処理を区別します。

10.4 ウェイクロックを短時間で解放する

ウェイクロックは、画面が消えていても中央処理装置を動作させるために使用されます。解放を忘れると、端末が休止状態へ入れず、大きなバッテリー消費につながります。

可能な限り、ウェイクロックを直接管理するのではなく、WorkManagerなど基本ソフトが管理する機能を利用します。Androidの測定機能では、長時間保持された部分ウェイクロックを調査できます。

10.5 バッテリー状態に応じて処理を調整する

重要度の低い同期、画像生成、事前取得などは、バッテリー残量が少ない場合に頻度を下げられます。充電開始後にまとめて実行する方法もあります。

Androidでは現在のバッテリー残量や充電状態を確認できます。ただし、すべての処理で状態を繰り返し問い合わせるのではなく、背景作業の条件指定を優先します。

11. iOSアプリのバッテリー消費を抑える

iOSでは、アプリの活動時間が基本ソフトによって管理されます。背景実行を無制限に継続する設計ではなく、目的に合った背景機能を選択する必要があります。

11.1 アプリの状態変化に合わせて停止する

アプリが背景へ移動したら、画面描画、動画、不要なタイマー、短い間隔の通信を停止します。前面へ戻った際に、必要な状態を再計算して再開します。

iOSは背景状態のアプリ活動を制限し、必要に応じてアプリを停止します。背景で長時間中央処理装置を使用することを前提にせず、中断可能な処理として設計します。

11.2 BackgroundTasksで延期可能な作業を登録する

情報更新、データベース整理、機械学習処理などは、BackgroundTasksを利用して基本ソフトへ登録できます。実際の実行時刻は、利用状況や端末状態に基づいて決定されます。

処理には終了処理を用意し、割り当て時間が終了する前に安全に中断できるようにします。作業完了後は結果を基本ソフトへ通知します。

iOSの背景更新登録例

import BackgroundTasks

final class BackgroundRefreshService {
   static let taskIdentifier =
       "com.example.app.refresh"

   func register() {
       BGTaskScheduler.shared.register(
           forTaskWithIdentifier:
               Self.taskIdentifier,
           using: nil
       ) { task in
           guard let refreshTask =
                   task as? BGAppRefreshTask else {
               task.setTaskCompleted(
                   success: false
               )
               return
           }

           self.handle(refreshTask)
       }
   }

   func schedule() {
       let request = BGAppRefreshTaskRequest(
           identifier: Self.taskIdentifier
       )

       request.earliestBeginDate =
           Date(timeIntervalSinceNow: 60 * 60)

       do {
           try BGTaskScheduler.shared.submit(
               request
           )
       } catch {
           print(
               "背景更新の登録に失敗しました"
           )
       }
   }

   private func handle(
       _ task: BGAppRefreshTask
   ) {
       schedule()

       let operation = Task {
           do {
               try await refreshData()
               task.setTaskCompleted(
                   success: true
               )
           } catch {
               task.setTaskCompleted(
                   success: false
               )
           }
       }

       task.expirationHandler = {
           operation.cancel()
       }
   }

   private func refreshData() async throws {
       // 必要なデータだけを更新する
   }
}

earliestBeginDateは、指定時刻に必ず実行されることを意味しません。即時性が必要な機能には別の方式を利用し、背景更新は遅延可能な処理に限定します。

11.3 背景実行モードを増やしすぎない

音声、位置情報、外部機器通信など、特定の用途には専用の背景実行モードがあります。これらを一般的な同期処理のために有効化するべきではありません。

Appleは、背景実行モードの過度な利用が端末性能とバッテリーに悪影響を与える可能性があるため、代替手段がない場合に限定するよう案内しています。

11.4 通信処理を基本ソフトへ委ねる

大容量の送受信は、背景対応のURLSessionを利用することで、アプリの処理が停止しても基本ソフトが転送を管理できます。アプリが常に活動し続ける必要を減らせます。

任意の通信は、充電中や効率的な通信状態まで延期できるようにします。小さな要求を個別に送るのではなく、可能な範囲でまとめます。

11.5 Power Profilerで測定する

InstrumentsのPower Profilerでは、中央処理装置、通信、画面などが電力へ与える影響を実機で調査できます。操作のどの段階で消費が増えるかを確認します。

開発時の記録だけでなく、利用者端末から提供された記録を分析できる場合もあります。再現手順を固定し、改善前後で同じ条件を比較します。

12. Flutterアプリのバッテリー消費を抑える

Flutterアプリでも、省電力の原則はネイティブアプリと同じです。不要な再構築、過度な画面描画、短い間隔のタイマー、背景通信、位置情報取得を減らします。

12.1 部品の再構築範囲を小さくする

大きな画面部品で状態を管理すると、一つの値の変更で多数の子部品が再構築されます。頻繁に変化する状態を小さな部品へ分離します。

変更されない部品には定数構築子を利用し、同じ部品を何度も生成しないようにします。Flutterの性能指針でも、構築処理を軽く保つことが推奨されています。

12.2 背景移動時にタイマーを止める

Timer.periodicを開始したままにすると、画面が見えなくなっても処理が続く場合があります。アプリ状態の変化を監視し、背景移動時に停止します。

Flutterでタイマーを管理する例

import 'dart:async';

import 'package:flutter/material.dart';

class EfficientTimerPage
   extends StatefulWidget {
 const EfficientTimerPage({super.key});

 @override
 State<EfficientTimerPage> createState() =>
     _EfficientTimerPageState();
}

class _EfficientTimerPageState
   extends State<EfficientTimerPage>
   with WidgetsBindingObserver {
 Timer? _timer;
 DateTime? _startedAt;
 Duration _elapsed = Duration.zero;

 @override
 void initState() {
   super.initState();

   WidgetsBinding.instance.addObserver(this);
   _startTimer();
 }

 void _startTimer() {
   _startedAt ??= DateTime.now();

   _timer?.cancel();
   _timer = Timer.periodic(
     const Duration(seconds: 1),
     (_) {
       final startedAt = _startedAt;

       if (startedAt == null || !mounted) {
         return;
       }

       setState(() {
         _elapsed =
             DateTime.now().difference(startedAt);
       });
     },
   );
 }

 void _stopTimer() {
   _timer?.cancel();
   _timer = null;
 }

 @override
 void didChangeAppLifecycleState(
   AppLifecycleState state,
 ) {
   switch (state) {
     case AppLifecycleState.resumed:
       _startTimer();
       break;

     case AppLifecycleState.inactive:
     case AppLifecycleState.hidden:
     case AppLifecycleState.paused:
     case AppLifecycleState.detached:
       _stopTimer();
       break;
   }
 }

 @override
 void dispose() {
   WidgetsBinding.instance
       .removeObserver(this);

   _stopTimer();
   super.dispose();
 }

 @override
 Widget build(BuildContext context) {
   return Scaffold(
     body: Center(
       child: Text(
         '${_elapsed.inSeconds}秒',
       ),
     ),
   );
 }
}

背景から復帰した際は、タイマーの実行回数ではなく開始時刻との差から経過時間を計算しています。そのため、背景中に一秒ごとの処理を継続する必要がありません。

12.3 重い計算を構築処理内で行わない

buildの中で大量の並べ替え、画像変換、複雑な文字解析を実行すると、状態変更のたびに同じ計算が繰り返されます。データ取得時や入力変更時に一度だけ計算します。

重い計算を別の分離処理へ移すと、画面停止を防げます。ただし、分離処理を短い間隔で大量に作成すると別の負荷が発生するため、処理量を確認します。

12.4 性能表示と中央処理装置記録を利用する

Flutter DevToolsのPerformance Viewでは、画面ごとの描画時間や処理の詰まりを確認できます。CPU Profilerでは、中央処理装置の時間を多く使っている関数を調査できます。

画面の滑らかさだけでなく、同じ操作を長時間繰り返した際の処理量も確認します。Flutterの試験機能では、起動時間、画面停止、バッテリー効率などの指標を追跡できる構成も案内されています。

12.5 ネイティブ側の処理も測定する

Flutter側の画面が軽くても、位置情報、動画、カメラ、通知などの追加部品がネイティブ側で継続処理を行っている場合があります。Dartの記録だけでは原因を確認できないことがあります。

AndroidではBattery Historian、iOSではPower Profilerを併用します。プラットフォームチャネルを通じて利用する独自処理も、開始と停止の対応を確認します。

13. キャッシュ・保存・同期を最適化する

端末保存を適切に利用すると通信を減らせますが、過度な保存や頻繁な書き込みは入出力負荷を増やします。データの性質に応じて保存期間と更新方法を決めます。

13.1 変更頻度の低い情報を保存する

商品分類、地域一覧、画面設定など、変更頻度の低い情報は端末へ保存し、毎回サーバーから取得しないようにします。通信が不安定な環境でも画面を表示しやすくなります。

保存データには更新時刻や版を付けます。起動ごとに全件を再取得するのではなく、変更がある場合だけ更新します。

13.2 保存処理をまとめる

文字入力の一文字ごとにデータベースへ保存すると、短時間に多数の書き込みが発生します。入力停止後、画面移動時、一定量の変更後などにまとめて保存します。

ただし、保存を遅らせすぎると、異常終了時に入力内容が失われます。データの重要度と書き込み頻度の均衡を決めます。Appleも、書き込みをまとめて入出力回数を減らす方法を推奨しています。

13.3 差分だけを同期する

毎回データベース全体を送受信するのではなく、前回同期後に変更された部分だけを処理します。通信量、データ変換、保存処理を減らせます。

削除情報や競合解決も差分同期の対象です。更新日時だけに依存すると端末時刻の違いで問題が発生するため、サーバーが管理する版番号や変更識別子を利用します。

13.4 古いキャッシュを削除する

キャッシュを無制限に増やすと、保存容量が不足し、検索や整理処理にも時間がかかります。保存期限、最大件数、最大容量を設定します。

利用頻度の低いデータから削除し、必要になった場合に再取得します。削除処理自体も毎回実行せず、充電中やアプリ起動後の適切な時点にまとめます。

13.5 オフライン操作を再送可能にする

通信がない状態で利用者操作を受け付け、後でまとめて送信できれば、不安定な通信環境での連続再試行を減らせます。操作内容を端末へ安全に保存し、接続回復後に同期します。

送信済みの操作を重複送信しないため、各操作へ一意の識別子を付けます。失敗時は指数的な再試行を利用し、短い間隔で通信を繰り返さないようにします。

14. バッテリー消費を測定・試験する

省電力化の成果は、実機で測定しなければ確認できません。開発用端末、低性能端末、通信状態、画面の明るさなどをそろえて比較します。

14.1 AndroidでBattery Historianを利用する

Batterystatsは、端末上で収集されたバッテリー関連情報を出力できます。Battery Historianは、その情報を時系列の画面として表示し、警報、位置情報、通信、ウェイクロックなどを確認できます。

頻繁な端末起動警報、継続的なGPS利用、短い間隔の作業や同期などを特定できます。測定前に記録を初期化し、決められた操作だけを実行すると比較しやすくなります。

14.2 iOSでPower Profilerを利用する

Power Profilerでは、アプリ使用中の電力影響を時系列で確認できます。通信、中央処理装置、画面など、消費へ影響する活動を操作手順と対応付けます。

測定は実機で行い、アプリ起動直後だけでなく、一定時間操作した状態や背景移動後も確認します。Appleは、開発中のデータと利用者端末からのデータを分析する方法を案内しています。

14.3 実際の利用時間で試験する

数秒間の測定では、メモリ漏れ、タイマーの停止忘れ、再試行の増加などを発見できない場合があります。地図や動画などは、実際の利用時間に近い長さで試験します。

前面利用、背景移動、画面消灯、通信切断、再接続、端末回転などを組み合わせます。特に、画面を閉じた後に位置情報や通信が停止しているかを確認します。

14.4 測定条件を統一する

端末の充電残量、画面の明るさ、通信種類、端末温度、他のアプリの動作が異なると、比較結果が不安定になります。同じ端末と同じ操作手順を使用します。

一回の測定だけで結論を出さず、複数回実行して傾向を確認します。改善対象以外の設定を変えず、変更前後の差を比較します。

14.5 継続的な性能基準を作る

新機能追加によって、過去に改善した消費電力が再び悪化することがあります。代表的な操作について、通信回数、背景処理時間、描画時間などの基準を設定します。

公開前の試験に省電力項目を含め、外部ライブラリや基本ソフトの更新後にも確認します。Flutterでは統合試験を通じて性能指標を記録する構成が案内されています。

15. バッテリー節約を導入する手順

バッテリー最適化は、問題が発生してから個別に修正するより、設計段階から処理頻度と停止条件を決めるほうが効果的です。すべての機能に電力予算を持たせます。

15.1 電力を使用する機能を一覧化する

通信、位置情報、動画、音声、カメラ、センサー、背景作業、タイマー、通知、データベース書き込みを一覧にします。それぞれについて、開始条件、実行間隔、終了条件を記録します。

終了条件が定義されていない処理は、停止忘れを起こしやすくなります。画面終了、アプリ背景移動、利用者による停止、ログアウトなどを整理します。

15.2 利用者価値と更新頻度を対応させる

情報を頻繁に更新するほど便利になるとは限りません。天気、商品情報、業務統計などは、数秒単位の更新が不要な場合があります。

即時性が必要な機能、数分の遅延を許容できる機能、充電中まで延期できる機能に分類します。この分類を背景作業、通信、通知の設定へ反映します。

15.3 最も消費の大きい処理から改善する

多数の小さな修正を同時に行うより、測定結果で影響の大きい処理から改善します。継続的な位置情報、頻繁な通信、解放されていないウェイクロックなどは優先度が高くなります。

改善後は同じ条件で再測定し、効果を確認します。効果がない場合は変更を戻し、別の原因を調査できるようにします。

15.4 省電力と利用体験の均衡を確認する

更新頻度を下げすぎると、古い情報が表示される可能性があります。位置精度を下げすぎると、検索結果や案内が不正確になります。

利用者が明示的に開始した機能では応答性を優先し、自動的な背景処理では省電力を優先するなど、状況ごとに基準を変えます。低電力モードを独自に用意する場合は、制限される機能を利用者へ説明します。

15.5 公開後も継続的に監視する

開発環境では問題がなくても、利用者の端末、通信環境、利用時間によって消費電力が増える場合があります。問い合わせ、評価、障害情報を確認し、特定機能との関係を調査します。

Androidでは電力関連の測定機能やAndroid vitals、iOSではXcodeとInstrumentsの分析機能を活用します。基本ソフトや外部ライブラリの更新後にも、背景処理、通信、位置情報を再確認します。

おわりに

モバイルアプリのバッテリー消費を抑えるには、中央処理装置の計算を速くするだけでなく、不要な処理を実行しない設計が必要です。短い間隔の通信、継続的な高精度位置情報、背景タイマー、不要な画面再描画、頻繁な保存処理は、利用者が気付かない場所で電力を消費します。

AndroidではWorkManager、Doze、App Standbyなどの仕組みを理解し、延期可能な作業を基本ソフトへ委ねます。iOSではBackgroundTasks、背景対応通信、Core Locationの精度と距離条件を適切に利用します。Flutterでも、アプリ状態に応じたタイマー停止、再構築範囲の縮小、ネイティブ側を含む測定が重要です。

最終的には、電力を使用する機能を一覧化し、実機で消費状況を測定し、影響の大きい処理から改善します。省電力化を一度だけの性能対応として扱わず、新機能追加や基本ソフト更新のたびに確認することで、長時間利用しても端末が熱くなりにくく、利用者が安心して継続できるモバイルアプリを構築できます。

LINE Chat