تُعدّ الطلبات التي يرسلها تطبيقك إلى الشبكة سببًا رئيسيًا في استنزاف البطارية، لأنّها تؤدي إلى تشغيل أجهزة الراديو الخلوية أو أجهزة Wi-Fi التي تستهلك الطاقة. بالإضافة إلى الطاقة اللازمة لإرسال الحزم واستقبالها، تستهلك هذه الأجهزة طاقة إضافية فقط عند تشغيلها وإبقائها في وضع الاستيقاظ. فمثلاً، يمكن لطلب شبكة بسيط كل 15 ثانية أن يُبقي جهاز الإرسال اللاسلكي في الجوّال قيد التشغيل باستمرار ويستهلك طاقة البطارية بسرعة.
هناك ثلاثة أنواع عامة من التحديثات المنتظمة:
- الأنشطة التي يبدأها المستخدم: إجراء تحديث استنادًا إلى سلوك معيّن للمستخدم، مثل إيماءة السحب للأسفل لإعادة التحميل
- بدء الإجراء من التطبيق: إجراء تحديث على أساس متكرر.
- بدء الخادم: إجراء تحديث استجابة لإشعار من الخادم.
يتناول هذا الموضوع كلّاً من هذه الميزات ويناقش طرقًا إضافية يمكن من خلالها تحسينها لتقليل استنزاف البطارية.
تحسين الطلبات التي يبدأها المستخدم
تحدث الطلبات التي يبدأها المستخدم عادةً استجابةً لبعض سلوكيات المستخدم. على سبيل المثال، قد يتيح تطبيق يُستخدم لقراءة أحدث المقالات الإخبارية للمستخدم إجراء إيماءة السحب للأسفل لإعادة التحميل للبحث عن مقالات جديدة. يمكنك استخدام الأساليب التالية للاستجابة للطلبات التي يبدأها المستخدم مع تحسين استخدام الشبكة.
تقييد طلبات المستخدمين
قد ترغب في تجاهل بعض الطلبات التي يبدأها المستخدم إذا لم تكن هناك حاجة إليها، مثل إيماءات السحب للأسفل لإعادة التحميل المتعددة على مدار فترة زمنية قصيرة للتحقق من وجود بيانات جديدة بينما لا تزال البيانات الحالية حديثة. وقد يؤدي تنفيذ كل طلب إلى إهدار قدر كبير من الطاقة من خلال إبقاء الراديو في وضع النشاط. هناك نهج أكثر كفاءة يتمثل في تقييد الطلبات التي يبدأها المستخدم بحيث يمكن تقديم طلب واحد فقط خلال فترة زمنية، مما يقلل من عدد مرات استخدام الراديو.
استخدام ذاكرة تخزين مؤقت
من خلال تخزين بيانات تطبيقك مؤقتًا، فإنك تقوم بإنشاء نسخة محلية من المعلومات التي يحتاج تطبيقك إلى الرجوع إليها. ويمكن لتطبيقك بعد ذلك الوصول إلى النسخة المحلية نفسها من المعلومات عدة مرات بدون الحاجة إلى فتح اتصال بالشبكة لتقديم طلبات جديدة.
يجب تخزين البيانات مؤقتًا بأكبر قدر ممكن، بما في ذلك الموارد الثابتة وعمليات التنزيل عند الطلب، مثل الصور بالحجم الكامل. يمكنك استخدام عناوين HTTP للتخزين المؤقت لضمان عدم عرض تطبيقك لبيانات قديمة نتيجة استراتيجية التخزين المؤقت التي تتبعها. لمزيد من المعلومات حول تخزين استجابات الشبكة مؤقتًا، يُرجى الاطّلاع على تجنُّب عمليات التنزيل المتكررة.
على نظام التشغيل Android 11 والإصدارات الأحدث، يمكن لتطبيقك استخدام مجموعات البيانات الكبيرة نفسها التي تستخدمها التطبيقات الأخرى في حالات الاستخدام، مثل تعلُّم الآلة وتشغيل الوسائط. عندما يحتاج تطبيقك إلى الوصول إلى مجموعة بيانات مشتركة، يمكنه أولاً التحقّق من وجود نسخة مخزّنة مؤقتًا قبل محاولة تنزيل نسخة جديدة. لمزيد من المعلومات عن مجموعات البيانات المشتركة، يُرجى الاطّلاع على الوصول إلى مجموعات البيانات المشتركة.
استخدام معدّل نقل بيانات أعلى لتنزيل المزيد من البيانات بمعدّل أقل
عند الاتصال عبر شبكة لاسلكية، عادةً ما يكون النطاق الترددي الأعلى على حساب استهلاك أكبر للبطارية، ما يعني أنّ شبكة الجيل الخامس تستهلك عادةً طاقة أكبر من شبكة LTE، والتي بدورها تستهلك طاقة أكبر من شبكة الجيل الثالث.
وهذا يعني أنّه على الرغم من اختلاف حالة الاتصال اللاسلكي الأساسية استنادًا إلى التكنولوجيا اللاسلكية، فإنّ التأثير النسبي في البطارية الناتج عن وقت الذيل لتغيير الحالة يكون أكبر بشكل عام في الاتصالات اللاسلكية ذات معدل نقل البيانات الأعلى. لمزيد من المعلومات حول وقت الذيل، يُرجى الاطّلاع على آلة حالة الراديو.
وفي الوقت نفسه، يعني معدّل نقل البيانات الأعلى أنّه يمكنك الجلب المُسبق بشكل أكبر، وتنزيل المزيد من البيانات خلال الوقت نفسه. قد لا يكون ذلك بديهيًا، ولكن نظرًا لأنّ تكلفة البطارية في وقت الذيل مرتفعة نسبيًا، من الأكثر كفاءة أيضًا إبقاء الراديو نشطًا لفترات أطول خلال كل جلسة نقل لتقليل معدل تكرار التحديثات.
على سبيل المثال، إذا كان جهاز LTE اللاسلكي يستهلك ضعف معدل نقل البيانات وضعف تكلفة الطاقة التي يستهلكها جهاز 3G، عليك تنزيل أربعة أضعاف كمية البيانات خلال كل جلسة، أو ما يصل إلى 10 ميغابايت. عند تنزيل هذا القدر من البيانات، من المهم مراعاة تأثير الجلب المُسبَق على مساحة التخزين المحلية المتاحة ومسح ذاكرة التخزين المؤقت للجلب المُسبَق بانتظام.
يمكنك استخدام
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 وتستردّ عناوين الأخبار الأخيرة. يمكن جدولة هذا العامل
ليتم تشغيله كل ساعة، بشرط أن يكون الجهاز متصلاً بشبكة
لا تفرض تكلفة استخدام وأن بطارية الجهاز ليست منخفضة، مع استراتيجية إعادة محاولة مخصصة
إذا كانت هناك أي مشاكل في استرداد البيانات، كما هو موضح أدناه:
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 السحابية"
مراسلة Firebase السحابية (FCM) هي آلية بسيطة تُستخدم لنقل البيانات من خادم إلى مثيل تطبيق معيّن. باستخدام FCM، يمكن للخادم إرسال إشعار إلى تطبيقك الذي يعمل على جهاز معيّن بأنّ هناك بيانات جديدة متاحة له.
مقارنةً بالاقتراع، حيث يجب أن يرسل تطبيقك إشارات إلى الخادم بانتظام للاستعلام عن بيانات جديدة، يسمح هذا النموذج المستند إلى الأحداث لتطبيقك بإنشاء اتصال جديد فقط عندما يعلم أن هناك بيانات لتنزيلها. يقلل النموذج من الاتصالات غير الضرورية ويقلل من زمن الوصول عند تحديث المعلومات داخل تطبيقك.
يتم تنفيذ خدمة FCM باستخدام اتصال TCP/IP دائم. يؤدي ذلك إلى تقليل عدد الاتصالات المستمرة ويتيح للمنصة تحسين معدل نقل البيانات وتقليل التأثير المرتبط بعمر البطارية.