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