تُعد الطلبات التي يرسلها تطبيقك إلى الشبكة سببًا رئيسيًا لاستنزاف البطارية، لأنّها تشغّل أجهزة الراديو التي تستهلك الطاقة، سواء كانت شبكة الجوّال أو شبكة 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) هي آلية بسيطة تُستخدم لنقل البيانات من خادم إلى مثيل تطبيق معيّن. باستخدام "المراسلة عبر السحابة الإلكترونية من Firebase"، يمكن لخادمك إرسال إشعار إلى تطبيقك الذي يعمل على جهاز معيّن بأنّه تتوفّر بيانات جديدة له.
مقارنةً بالاستقصاء الذي يجب أن يرسل فيه تطبيقك طلبات إلى الخادم بانتظام للاستعلام عن بيانات جديدة، يتيح هذا النموذج المستند إلى الأحداث لتطبيقك إنشاء اتصال جديد فقط عندما يعلم أنّ هناك بيانات لتنزيلها. يقلّل النموذج من عمليات الربط غير الضرورية ويحدّ من وقت الاستجابة عند تعديل المعلومات داخل تطبيقك.
يتم تنفيذ خدمة "المراسلة عبر السحابة الإلكترونية من Firebase" باستخدام اتصال TCP/IP دائم. يقلّل ذلك من عدد الاتصالات المستمرة ويسمح للمنصة بتحسين استخدام النطاق الترددي وتقليل التأثير المرتبط بذلك على عمر البطارية.