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

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

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

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

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

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

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

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

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

キャッシュを使用する

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

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

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

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

無線通信を介して接続する場合、一般に帯域幅は バッテリー価格が高いこと。つまり、一般的に 5G の方が多くのエネルギーを消費します。 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

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

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

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 接続を使用して実装されます。これにより、 維持し、プラットフォームで帯域幅を最適化 バッテリー駆動時間への影響を最小限に抑えます。