تأثیر به روز رسانی های منظم را به حداقل برسانید

درخواست‌هایی که برنامه شما به شبکه ارائه می‌کند یکی از دلایل اصلی تخلیه باتری هستند، زیرا رادیوهای تلفن همراه یا Wi-Fi پر مصرف را روشن می‌کنند. فراتر از توان مورد نیاز برای ارسال و دریافت بسته ها، این رادیوها فقط برای روشن شدن و بیدار ماندن انرژی بیشتری مصرف می کنند. چیزی به سادگی درخواست شبکه در هر 15 ثانیه می تواند رادیو موبایل را به طور مداوم روشن نگه دارد و به سرعت باتری را مصرف کند.

سه نوع کلی از به روز رسانی های منظم وجود دارد:

  • توسط کاربر آغاز شده است. انجام به‌روزرسانی بر اساس برخی رفتارهای کاربر، مانند حرکت کشش برای تازه کردن.
  • برنامه راه اندازی شده است. انجام به روز رسانی به صورت مکرر.
  • آغاز شده توسط سرور انجام یک به روز رسانی در پاسخ به یک اعلان از یک سرور.

این مبحث به هر یک از این موارد می پردازد و راه های دیگری را که می توان آنها را برای کاهش تخلیه باتری بهینه کرد، مورد بحث قرار می دهد.

بهینه سازی درخواست های آغاز شده توسط کاربر

درخواست های آغاز شده توسط کاربر معمولاً در پاسخ به برخی رفتارهای کاربر رخ می دهد. برای مثال، اپلیکیشنی که برای خواندن آخرین مقالات خبری استفاده می‌شود، ممکن است به کاربر اجازه دهد تا برای بررسی مقاله‌های جدید، حرکت کشش به تازه‌سازی را انجام دهد. شما می توانید از تکنیک های زیر برای پاسخ به درخواست های آغاز شده توسط کاربر در حین بهینه سازی استفاده از شبکه استفاده کنید.

درخواست های کاربر دریچه گاز

ممکن است بخواهید برخی از درخواست‌های آغاز شده توسط کاربر را در صورت عدم نیاز به آن‌ها نادیده بگیرید، مانند چندین حرکت کشش برای به‌روزرسانی در یک دوره زمانی کوتاه برای بررسی داده‌های جدید در حالی که داده‌های فعلی هنوز تازه هستند. عمل به هر درخواست می تواند با بیدار نگه داشتن رادیو مقدار قابل توجهی از انرژی را هدر دهد. یک رویکرد کارآمدتر این است که درخواست‌های آغاز شده توسط کاربر را کاهش دهیم تا فقط یک درخواست در یک دوره زمانی انجام شود و تعداد دفعات استفاده از رادیو را کاهش دهد.

از کش استفاده کنید

با ذخیره کردن داده های برنامه خود، یک کپی محلی از اطلاعاتی که برنامه شما باید به آنها ارجاع دهد ایجاد می کنید. سپس برنامه شما می‌تواند چندین بار بدون نیاز به باز کردن اتصال شبکه برای درخواست‌های جدید، به همان نسخه محلی اطلاعات دسترسی پیدا کند.

باید داده ها را تا حد امکان به طور تهاجمی ذخیره کنید، از جمله منابع استاتیک و دانلودهای درخواستی مانند تصاویر در اندازه کامل. می‌توانید از هدرهای کش HTTP استفاده کنید تا مطمئن شوید که استراتژی ذخیره‌سازی شما منجر به نمایش داده‌های قدیمی نمی‌شود. برای اطلاعات بیشتر در مورد ذخیره پاسخ های شبکه، به اجتناب از دانلودهای اضافی مراجعه کنید.

در Android 11 و بالاتر، برنامه شما می‌تواند از همان مجموعه داده‌های بزرگی استفاده کند که سایر برنامه‌ها برای موارد استفاده مانند یادگیری ماشینی و پخش رسانه استفاده می‌کنند. هنگامی که برنامه شما نیاز به دسترسی به یک مجموعه داده مشترک دارد، می‌تواند قبل از دانلود نسخه جدید، ابتدا نسخه ذخیره‌شده آن را بررسی کند. برای کسب اطلاعات بیشتر درباره مجموعه داده‌های مشترک، به مجموعه داده‌های مشترک دسترسی داشته باشید.

از پهنای باند بیشتر برای دانلود کمتر داده های بیشتر استفاده کنید

وقتی از طریق یک رادیو بی‌سیم متصل می‌شوید، پهنای باند بالاتر معمولاً به قیمت هزینه باتری بالاتر است، به این معنی که 5G معمولاً انرژی بیشتری نسبت به LTE مصرف می‌کند که به نوبه خود گران‌تر از 3G است.

این به این معنی است که در حالی که وضعیت رادیویی زیربنایی بر اساس فناوری رادیویی متفاوت است، به طور کلی تأثیر نسبی باتری تغییر وضعیت برای رادیوهای با پهنای باند بالاتر بیشتر است. برای اطلاعات بیشتر در مورد دم تایم، به ماشین حالت رادیویی مراجعه کنید.

در عین حال، پهنای باند بالاتر به این معنی است که می‌توانید با شدت بیشتری واکشی از پیش انجام دهید و داده‌های بیشتری را در همان زمان دانلود کنید. شاید کمتر بصری باشد، زیرا هزینه باتری دم تایم نسبتاً بالاتر است، همچنین فعال نگه داشتن رادیو برای مدت طولانی در طول هر جلسه انتقال برای کاهش فرکانس به روز رسانی کارآمدتر است.

به عنوان مثال، اگر یک رادیو LTE دو برابر پهنای باند و دو برابر هزینه انرژی 3G داشته باشد، باید چهار برابر بیشتر داده در طول هر جلسه دانلود کنید - یا به طور بالقوه تا 10 مگابایت. هنگام دانلود این حجم از داده، مهم است که تأثیر واکشی اولیه خود را بر فضای ذخیره‌سازی محلی موجود در نظر بگیرید و کش واکشی اولیه خود را مرتباً شستشو دهید.

می توانید از ConnectivityManager برای ثبت شنونده برای شبکه پیش فرض و از TelephonyManager برای ثبت PhoneStateListener برای تعیین نوع اتصال فعلی دستگاه استفاده کنید. هنگامی که نوع اتصال مشخص شد، می‌توانید روال‌های واکشی اولیه خود را بر این اساس تغییر دهید:

کاتلین

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 دارید که آخرین عناوین اخبار را بازیابی می کند. این کارگر می‌تواند هر ساعت کار کند، مشروط بر اینکه دستگاه به یک شبکه بدون اندازه‌گیری وصل باشد و باتری دستگاه کم نباشد، با یک استراتژی تکرار سفارشی در صورت وجود هر گونه مشکل در بازیابی داده‌ها، همانطور که در زیر نشان داده شده است:

کاتلین

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);

علاوه بر WorkManager، پلتفرم اندروید چندین ابزار دیگر را برای کمک به شما در ایجاد یک برنامه زمانی کارآمد برای تکمیل وظایف شبکه، مانند نظرسنجی، فراهم می‌کند. برای کسب اطلاعات بیشتر در مورد استفاده از این ابزارها، به راهنمای پردازش پس‌زمینه مراجعه کنید.

بهینه سازی درخواست های آغاز شده توسط سرور

درخواست‌های آغاز شده توسط سرور معمولاً در پاسخ به اعلان از یک سرور رخ می‌دهند. برای مثال، برنامه‌ای که برای خواندن آخرین مقالات خبری استفاده می‌شود ممکن است اعلانی در مورد دسته جدیدی از مقالات متناسب با اولویت‌های شخصی‌سازی کاربر دریافت کند که سپس آن‌ها را دانلود می‌کند.

به‌روزرسانی‌های سرور را با Firebase Cloud Messaging ارسال کنید

Firebase Cloud Messaging (FCM) یک مکانیسم سبک وزن است که برای انتقال داده ها از یک سرور به یک نمونه برنامه خاص استفاده می شود. با استفاده از FCM، سرور شما می‌تواند به برنامه شما که روی دستگاه خاصی اجرا می‌شود اطلاع دهد که داده‌های جدیدی برای آن موجود است.

در مقایسه با نظرسنجی، جایی که برنامه شما باید مرتباً به سرور برای جستجوی داده‌های جدید پینگ کند، این مدل رویداد محور به برنامه شما اجازه می‌دهد فقط زمانی که بداند داده‌ای برای دانلود وجود دارد، یک اتصال جدید ایجاد کند. این مدل اتصالات غیر ضروری را به حداقل می‌رساند و تأخیر را هنگام به‌روزرسانی اطلاعات در برنامه شما کاهش می‌دهد.

FCM با استفاده از اتصال TCP/IP دائمی پیاده سازی می شود. این تعداد اتصالات مداوم را به حداقل می رساند و به پلتفرم اجازه می دهد تا پهنای باند را بهینه کند و تأثیرات مربوط به عمر باتری را به حداقل برساند.