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