정기 업데이트의 영향 최소화

앱이 네트워크에 전송하는 요청은 배터리 소모의 주요 원인입니다. 전력을 소모하는 셀룰러 또는 Wi-Fi 무선 기능을 켜기 때문입니다. 강력한 성능 그 이상 이 무선 통신 장치는 켜진 상태를 유지합니다. 15초마다 네트워크 요청 같은 간단한 작업 무선 기능이 계속 켜져 있고 배터리가 빠르게 소모될 수 있습니다. 강력합니다.

일반적인 업데이트에는 세 가지 유형이 있습니다.

  • 사용자에 의해 시작. 당겨 새로고침 동작과 같은 일부 사용자 동작에 따라 업데이트를 실행합니다.
  • 앱에서 시작된 경우. 반복적으로 업데이트를 수행합니다.
  • 서버에서 시작된 경우. 에서 알림에 대한 응답으로 업데이트 수행 만들 수 있습니다

이 주제에서는 이러한 각 요소를 살펴보고 추가 방법을 설명합니다. 배터리 소모를 줄이도록 최적화되었습니다.

사용자가 시작한 요청 최적화

사용자 시작 요청은 일반적으로 특정 사용자 동작에 대한 응답으로 발생합니다. 대상 예를 들어 최신 뉴스 기사를 읽는 데 사용되는 앱에서 사용자가 당겨서 새로고침 동작을 수행하여 새 기사를 확인할 수 있습니다. 이 다음과 같은 기법을 활용하여 사용자가 시작한 요청에 응답하면서 네트워크 사용에 영향을 미칩니다

사용자 요청 제한

사용자가 시작한 일부 요청은 무시해도 좋습니다. 예를 들어 짧은 시간에 여러 번 당겨서 새로고침하는 동작을 하면 현재 데이터가 아직 최신인 동안 새 데이터를 확인합니다. 각 요청에 따라 작업하면 무선 기능을 켜진 상태로 유지하여 상당한 양의 전력을 낭비할 수 있습니다. 더 효율적인 접근 방식은 사용자 시작 요청을 제한하여 일정 기간 동안 하나의 요청만 실행되도록 하고 무선이 사용되는 빈도를 줄이는 것입니다.

캐시 사용

앱 데이터를 캐시하면 정보의 로컬 사본이 생성됩니다. 참조해야 합니다. 그러면 앱이 동일한 로컬 네트워크에 네트워크를 열지 않고도 정보 사본을 여러 번 사용하여 새 요청을 만듭니다

정적 리소스와 원본 크기 이미지와 같은 주문형 다운로드를 비롯하여 데이터를 최대한 많이 캐시해야 합니다. HTTP를 사용하여 캐싱 전략으로 인해 앱이 발생하지 않도록 하기 위한 캐시 헤더 표시합니다. 네트워크 응답 캐싱에 대한 자세한 내용은 중복 방지 다운로드를 선택합니다.

Android 11 이상에서는 앱이 다른 애플리케이션과 동일한 대규모 데이터 세트를 사용할 수 있습니다. 앱이 머신러닝 및 미디어 재생과 같은 사용 사례에 활용할 수 있습니다. 앱이 공유 데이터 세트에 액세스해야 할 때 새 사본을 다운로드하기 전에 먼저 캐시된 버전을 확인할 수 있습니다. 공유 데이터 세트에 대해 자세히 알아보려면 공유 데이터 세트 액세스를 참조하세요.

많은 데이터를 낮은 빈도로 다운로드하기 위해 큰 대역폭 사용

무선 라디오를 통해 연결되면 일반적으로 더 높은 대역폭이 배터리 비용이 높기 때문에 5G는 일반적으로 더 많은 에너지를 소비합니다. 3G보다 더 비쌉니다.

즉, 기본 무선 상태는 무선 통신 기술에 따라 달라지지만 일반적으로 상태 변경 테일-타임에 따른 배터리 영향은 더 높은 대역폭의 무선 통신에서 더 큽니다. 테일 시간에 관한 자세한 내용은 무선 상태 머신을 참고하세요.

동시에 대역폭이 넓을수록 더 많은 데이터를 미리 가져올 수 있습니다. 공격적으로 더 많은 데이터를 다운로드하는 것입니다. 다소 낮음 테일 타임 배터리 비용이 상대적으로 더 높기 때문에 각 전송 중에 무선 통신을 장시간 활성 상태로 유지하는 것이 더 효율적입니다. 업데이트 빈도를 줄일 수 있습니다

예를 들어 LTE 무선 통신이 대역폭과 에너지 비용은 두 배라면 각 세션 동안 네 배 더 많은 데이터를 다운로드해야 합니다. 잠재적으로 10MB에 이르렀습니다 이렇게 많은 양의 데이터를 다운로드할 때, 데이터를 미리 가져오는 것이 사용 가능한 로컬 저장소에 미치는 영향을 고려하는 것과 미리 가져오기용 캐시를 정기적으로 플러시하는 것이 중요합니다.

이 등록하려면 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
    }
}

}

자바

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)

자바

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 클라우드 메시징으로 서버 업데이트 전송

Firebase 클라우드 메시징 (FCM)은 경량형이며 서버에서 특정 앱 인스턴스로 데이터를 전송하는 데 사용되는 메커니즘입니다. FCM을 사용하면 서버가 특정 기기에서 실행 중인 앱에 새 데이터가 있는지 확인할 수 있습니다.

앱이 정기적으로 서버에 핑하여 다음을 쿼리해야 하는 폴링과 비교됩니다. 이 이벤트 기반 모델을 통해 앱에서 새 연결을 만들 수 있습니다. 다운로드할 데이터가 있는 경우에만 작동합니다. 모델은 불필요함을 최소화하고 앱 내의 정보를 업데이트할 때 지연 시간을 줄여줍니다.

FCM은 지속적인 TCP/IP 연결을 사용하여 구현합니다. 이렇게 하면 영구 연결 수가 많으며 플랫폼에서 대역폭을 최적화할 수 있도록 합니다. 배터리 수명에 미치는 영향을 최소화할 수 있습니다.