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

アプリがネットワークに対して行うリクエストが原因でバッテリーが消耗する主な原因 消費電力の多いモバイル無線または Wi-Fi 無線を使用するためです。パケットを送受信するのに必要な電力以外にも、このような無線通信は、オンにしてウェイク状態を維持するだけで電力を消費します。15 件ごとのネットワーク リクエストといった単純なもの 数秒でモバイル無線通信が継続してオンになり、バッテリーがすぐに消耗します。 あります。

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

  • ユーザー開始型。プルして更新する操作など、ユーザーの操作に基づいて更新を実行する。
  • アプリ起動。定期的な更新の実行。
  • サーバー開始。サーバーからの通知に応じて更新を実行する。

このトピックでは、これらの項目をそれぞれ確認し、それらを使用して バッテリーの消耗を抑えるために 最適化されます

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

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

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

ユーザーが開始したリクエストの中には、必要がないリクエストもあります。たとえば、現在のデータがまだ最新の状態であるにもかかわらず、短時間に複数のプルトゥー リフレッシュ ジェスチャーで新しいデータを確認するリクエストなどです。リクエストごとに処理すると、無線通信がウェイク状態を維持するため、大量の電力を消費する可能性があります。より効率的なアプローチは、ユーザーが開始したリクエストをスロットリングして、一定期間に 1 つのリクエストのみを実行できるようにし、無線の使用頻度を減らすことです。

キャッシュを使用する

アプリのデータをキャッシュに保存することで、情報のローカルコピーを作成する 指定することもできます。アプリは、ネットワーク接続を開いて新しいリクエストを行うことなく、情報の同じローカルコピーに複数回アクセスできます。

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

Android 11 以降では、機械学習やメディア再生などのユースケースで、他のアプリが使用している大規模なデータセットをアプリで使用できます。Google アプリが共有データセットにアクセスする必要がある場合、まずキャッシュ バージョンをチェックできる ダウンロードしてください。共有データセットの詳細については、共有データセットにアクセスするをご覧ください。

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

無線通信経由で接続する場合は、一般に、帯域幅が広いほど電池コストが高くなります。つまり、通常、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;
}

アプリ開始型リクエストを最適化する

アプリから開始されたリクエストは通常、スケジュールに従って発生します。たとえば、 バックエンドサービスにエクスポートできます。アプリが開始したリクエストを処理する場合は、リクエストの優先度、リクエストをまとめて処理できるかどうか、デバイスが充電中または定額制のネットワークに接続されるまでリクエストを延期できるかどうかを検討してください。これらのリクエストは注意深く また、Terraform などのライブラリを使用して、 WorkManager

ネットワーク リクエストを一括処理する

モバイル デバイスでは、無線通信をオンにして接続を確立するプロセス、 無線通信がスリープ状態にならないようにすると 大量の電力が消費されますこのため、 個々のリクエストを無作為に処理すると、消費電力が非常に大きくなる可能性があります。 バッテリー駆動時間が短くなります複数のネットワークのセットをキューに入れることが、 まとめて処理できますこの方法の場合、無線通信を 1 回だけオンにする消費電力だけで、アプリがリクエストするすべてのデータを取得できます。

WorkManager を使用する

WorkManager ライブラリを使用すると、効率的なスケジュールで処理を実行できます。 特定の条件(ネットワークの可用性など)が満たされているかどうかを考慮する 確認できます。たとえば、最新のニュースの見出しを取得する DownloadHeadlinesWorker という Worker サブクラスがあるとします。このワーカー デバイスは 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);

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

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

サーバー開始型リクエストは通常、サーバーからの通知に応答して発生します。たとえば、最新のニュース記事を閲覧するアプリは、ユーザーのパーソナライズ設定に適した新しい一連の記事に関する通知を受信し、それらをダウンロードします。

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

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

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

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