定期的な更新による影響を最小限に抑える

アプリがネットワークに対して行うリクエストが原因でバッテリーが消耗する主な原因 消費電力の多いモバイル無線または Wi-Fi 無線を使用するためです。パワーを超えて 必要な場合、これらの無線通信は単に追加の電力を消費します。 オンにし、覚醒状態を維持します。15 件ごとのネットワーク リクエストといった単純なもの 数秒でモバイル無線通信が継続してオンになり、バッテリーがすぐに消耗します。 あります。

定期的な更新には、主に次の 3 つのタイプがあります。

  • ユーザー開始型。次のようなユーザーの行動に基づいて更新を実行する 「プルして更新する」というジェスチャーです。
  • アプリ起動。定期的な更新の実行。
  • サーバー開始。からの通知に応じて更新を実行する 作成します。

このトピックでは、これらの項目を一つずつ取り上げて、それらを使用して バッテリーの消耗を抑えるために 最適化されます

ユーザーが開始したリクエストを最適化する

ユーザー開始リクエストは通常、ユーザーの行動に応じて発生します。対象 たとえば、最新のニュース記事を読むために使用されるアプリで、ユーザーが プルして更新するジェスチャーを行って新しい記事をチェックする。こちらの ユーザーによって開始されたリクエストに応答すると同時に、 ネットワーク使用状況を表します。

ユーザー リクエストのスロットリング

ユーザーが開始したリクエストについては、そのリクエストが たとえば、短時間に複数のプルして更新する操作を 新しいデータがないかチェックできますデータに基づく 無線通信をウェイクアップしたままにして、大量の電力を浪費する可能性があります。 より効率的なアプローチは、ユーザーが開始したリクエストをスロットリングして、 一定期間に 1 回のリクエストしか行えないため、 使用されます。

キャッシュを使用する

アプリのデータをキャッシュに保存することで、情報のローカルコピーを作成する 指定することもできます。これにより、アプリは同じローカル ネットワークを開かずに情報を何度もコピー 新しいリクエストを送信します

データは、静的データを含め、できるだけ積極的にキャッシュに保存してください。 オンデマンド ダウンロードなどのリソースが含まれます。HTTP を使用して キャッシュ ヘッダーを設定して、キャッシュ戦略によってアプリが 古いデータが表示されていますネットワーク レスポンスのキャッシュ保存について詳しくは、このモジュールの 冗長な構成を避ける ダウンロードできます。

Android 11 以降では、他のモデルと同じ大規模なデータセットをアプリで アプリが ML やメディア再生などのユースケースに使用されるようになりました。アプリが共有データセットにアクセスする必要がある場合、新しいコピーをダウンロードする前に、キャッシュに保存されているバージョンを確認できます。共有データセットについて詳しくは 共有データセットにアクセスするをご覧ください。

より広い帯域幅を使用して、より多くのデータをより少ない頻度でダウンロードする

無線通信経由で接続する場合は、一般に、帯域幅が広いほど電池コストが高くなります。つまり、通常、5G は LTE よりも多くのエネルギーを消費し、LTE は 3G よりもコストがかかります。

つまり、基盤となる無線通信の状態は無線技術に応じて変化しますが、一般に、状態変化のテールタイムがもたらす相対的な電池への影響は、無線の帯域幅が高いほど大きくなります。このモジュールの tail-time については、無線通信の状態 あります

その一方で、帯域幅が広い方がより積極的にプリフェッチできるので、同じ時間でより多くのデータをダウンロードできます。おそらくより少ない テールタイムのバッテリーコストが比較的高いため、 転送のたびに無線通信を長時間アクティブにしておく方が効率的 更新の頻度を減らすことをおすすめします。

例: LTE 無線の帯域幅が 2 倍、エネルギー コストが 2 倍 各セッションで 4 倍のデータをダウンロードする必要があること、つまり、 保存できますこのような大量のデータをダウンロードする場合は、 プリフェッチが利用可能なローカル ストレージに及ぼす影響を考慮して、 キャッシュを定期的に更新することをおすすめします。

こちらの ConnectivityManager で登録 デフォルト ネットワークのリスナー、 登録: TelephonyManager PhoneStateListener を 現在のデバイスの接続タイプを特定します。接続タイプが判明したら それに応じてプリフェッチ ルーティンを変更できます。

Kotlin

val cm = getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
val tm = getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager

private var hasWifi = false
private var hasCellular = false
private var cellModifier: Float = 1f

private val networkCallback = object : ConnectivityManager.NetworkCallback() {
    // Network capabilities have changed for the network
    override fun onCapabilitiesChanged(
            network: Network,
            networkCapabilities: NetworkCapabilities
    ) {
        super.onCapabilitiesChanged(network, networkCapabilities)
        hasCellular = networkCapabilities
    .hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)
        hasWifi = networkCapabilities
    .hasTransport(NetworkCapabilities.TRANSPORT_WIFI)
    }
}

private val phoneStateListener = object : PhoneStateListener() {
override fun onPreciseDataConnectionStateChanged(
    dataConnectionState: PreciseDataConnectionState
) {
  cellModifier = when (dataConnectionState.networkType) {
      TelephonyManager.NETWORK_TYPE_LTE or TelephonyManager.NETWORK_TYPE_HSPAP -> 4f
      TelephonyManager.NETWORK_TYPE_EDGE or TelephonyManager.NETWORK_TYPE_GPRS -> 1/2f
      else -> 1f

  }
}

private class NetworkState {
    private var defaultNetwork: Network? = null
    private var defaultCapabilities: NetworkCapabilities? = null
    fun setDefaultNetwork(network: Network?, caps: NetworkCapabilities?) = synchronized(this) {
        defaultNetwork = network
        defaultCapabilities = caps
    }
    val isDefaultNetworkWifi
        get() = synchronized(this) {
            defaultCapabilities?.hasTransport(TRANSPORT_WIFI) ?: false
        }
    val isDefaultNetworkCellular
        get() = synchronized(this) {
            defaultCapabilities?.hasTransport(TRANSPORT_CELLULAR) ?: false
        }
    val isDefaultNetworkUnmetered
        get() = synchronized(this) {
            defaultCapabilities?.hasCapability(NET_CAPABILITY_NOT_METERED) ?: false
        }
    var cellNetworkType: Int = TelephonyManager.NETWORK_TYPE_UNKNOWN
        get() = synchronized(this) { field }
        set(t) = synchronized(this) { field = t }
    private val cellModifier: Float
        get() = synchronized(this) {
            when (cellNetworkType) {
                TelephonyManager.NETWORK_TYPE_LTE or TelephonyManager.NETWORK_TYPE_HSPAP -> 4f
                TelephonyManager.NETWORK_TYPE_EDGE or TelephonyManager.NETWORK_TYPE_GPRS -> 1 / 2f
                else -> 1f
            }
        }
    val prefetchCacheSize: Int
        get() = when {
            isDefaultNetworkWifi -> MAX_PREFETCH_CACHE
            isDefaultNetworkCellular -> (DEFAULT_PREFETCH_CACHE * cellModifier).toInt()
            else -> DEFAULT_PREFETCH_CACHE
        }
}
private val networkState = NetworkState()
private val networkCallback = object : ConnectivityManager.NetworkCallback() {
    // Network capabilities have changed for the network
    override fun onCapabilitiesChanged(
            network: Network,
            networkCapabilities: NetworkCapabilities
    ) {
        networkState.setDefaultNetwork(network, networkCapabilities)
    }

    override fun onLost(network: Network?) {
        networkState.setDefaultNetwork(null, null)
    }
}

private val telephonyCallback = object : TelephonyCallback(), TelephonyCallback.PreciseDataConnectionStateListener {
    override fun onPreciseDataConnectionStateChanged(dataConnectionState: PreciseDataConnectionState) {
        networkState.cellNetworkType = dataConnectionState.networkType
    }
}

connectivityManager.registerDefaultNetworkCallback(networkCallback)
telephonyManager.registerTelephonyCallback(telephonyCallback)


private val prefetchCacheSize: Int
get() {
    return when {
        hasWifi -> MAX_PREFETCH_CACHE
        hasCellular -> (DEFAULT_PREFETCH_CACHE * cellModifier).toInt()
        else -> DEFAULT_PREFETCH_CACHE
    }
}

}

Java

ConnectivityManager cm =
 (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
TelephonyManager tm =
  (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);

private boolean hasWifi = false;
private boolean hasCellular = false;
private float cellModifier = 1f;

private ConnectivityManager.NetworkCallback networkCallback = new ConnectivityManager.NetworkCallback() {
@Override
public void onCapabilitiesChanged(
    @NonNull Network network,
    @NonNull NetworkCapabilities networkCapabilities
) {
        super.onCapabilitiesChanged(network, networkCapabilities);
        hasCellular = networkCapabilities
    .hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR);
        hasWifi = networkCapabilities
    .hasTransport(NetworkCapabilities.TRANSPORT_WIFI);
}
};

private PhoneStateListener phoneStateListener = new PhoneStateListener() {
@Override
public void onPreciseDataConnectionStateChanged(
    @NonNull PreciseDataConnectionState dataConnectionState
    ) {
    switch (dataConnectionState.getNetworkType()) {
        case (TelephonyManager.NETWORK_TYPE_LTE |
            TelephonyManager.NETWORK_TYPE_HSPAP):
            cellModifier = 4;
            Break;
        case (TelephonyManager.NETWORK_TYPE_EDGE |
            TelephonyManager.NETWORK_TYPE_GPRS):
            cellModifier = 1/2.0f;
            Break;
        default:
            cellModifier = 1;
            Break;
    }
}
};

cm.registerDefaultNetworkCallback(networkCallback);
tm.listen(
phoneStateListener,
PhoneStateListener.LISTEN_PRECISE_DATA_CONNECTION_STATE
);

public int getPrefetchCacheSize() {
if (hasWifi) {
    return MAX_PREFETCH_SIZE;
}
if (hasCellular) {
    return (int) (DEFAULT_PREFETCH_SIZE * cellModifier);
    }
return DEFAULT_PREFETCH_SIZE;
}

アプリが開始したリクエストを最適化する

アプリが開始するリクエストは、通常、バックエンド サービスにログや分析情報を送信するアプリなど、スケジュールに従って発生します。アプリが開始したリクエストを処理する場合は、リクエストの優先度、リクエストをまとめて処理できるかどうか、デバイスが充電中または定額制のネットワークに接続されるまでリクエストを延期できるかどうかを検討してください。これらのリクエストは、慎重なスケジューリングと WorkManager などのライブラリを使用して最適化できます。

バッチ ネットワーク リクエスト

モバイル デバイスの場合、無線通信をオンにして接続を確立し、無線通信のウェイク状態を維持するプロセスによって、大量の電力が消費されます。そのため、個々のリクエストをランダムなタイミングで処理すると、膨大な量の電力が消費され、バッテリー寿命が削減されます。複数のネットワークのセットをキューに入れることが、 まとめて処理できますこれにより、システムは ラジオを一度つけるだけで、その場合でも できます。

WorkManager を使用する

WorkManager ライブラリを使用すると、効率的なスケジュールで処理を実行できます。 特定の条件(ネットワークの可用性など)が満たされているかどうかを考慮する 確認できます。たとえば、 Worker サブクラスが呼び出される 最新のニュースの見出しを取得する DownloadHeadlinesWorker。このワーカー デバイスは 1 時間ごとに実行されるようにスケジュールできます。ただし、 カスタムの再試行戦略を使用して、定額制ネットワークとデバイスのバッテリー残量が少なくない 以下のように、データの取得中に問題が発生した場合:

Kotlin

val constraints = Constraints.Builder()
    .setRequiredNetworkType(NetworkType.UNMETERED)
    .setRequiresBatteryNotLow(true)
    .build()
val request =
    PeriodicWorkRequestBuilder<DownloadHeadlinesWorker>(1, TimeUnit.HOURS)
        .setConstraints(constraints)
        .setBackoffCriteria(BackoffPolicy.LINEAR, 1L, TimeUnit.MINUTES)
        .build()
WorkManager.getInstance(context).enqueue(request)

Java

Constraints constraints = new Constraints.Builder()
        .setRequiredNetworkType(NetworkType.UNMETERED)
        .setRequiresBatteryNotLow(true)
        .build();
WorkRequest request = new PeriodicWorkRequest.Builder(DownloadHeadlinesWorker.class, 1, TimeUnit.HOURS)
        .setBackoffCriteria(BackoffPolicy.LINEAR, 1L, TimeUnit.MINUTES)
        .build();
WorkManager.getInstance(this).enqueue(request);

Android プラットフォームには、WorkManager の他にもいくつかのツールが用意されています。 ネットワーキング タスクを効率的に完了するためのスケジュールを 使用されます。これらのツールの使用方法について詳しくは、 バックグラウンド処理ガイド

サーバー開始リクエストを最適化する

サーバー開始リクエストは通常、 あります。たとえば、最新のニュース記事を読むために使用されるアプリは、 ユーザーの記事の内容に合った新しい記事の パーソナライズ設定をダウンロードし

Firebase Cloud Messaging を使用してサーバーの更新情報を送信する

Firebase Cloud Messaging(FCM)は、サーバーから特定のアプリ インスタンスにデータを送信する際に使用できる軽量なメカニズムです。FCM を使用すると、サーバーは特定のデバイスで実行されているアプリに、 新しいデータがあります

アプリが定期的にサーバーに ping してクエリを発行する必要があるポーリングと このイベント ドリブン モデルにより、アプリから新しい接続を作成できます。 ダウンロードするデータがあることを認識した場合にのみ発生します。このモデルでは、不要な接続を最小限に抑えるとともに、アプリ内の情報を更新するまでの時間を短縮します。

FCM は永続的な TCP/IP 接続を使用して実装されます。これにより、 維持し、プラットフォームで帯域幅を最適化 バッテリー駆動時間への影響を最小限に抑えます。