تقليل تأثير التحديثات المنتظمة

إنّ الطلبات التي يرسلها تطبيقك إلى الشبكة هي أحد الأسباب الرئيسية لاستهلاك البطارية، ويعود السبب في ذلك إلى أنّها تفعّل أجهزة شبكة الجوّال أو Wi-Fi التي تستهلك طاقة. بالإضافة إلى الطاقة اللازمة لإرسال الحِزم واستلامها، تستهلك أجهزة الراديو هذه طاقة إضافية عند تشغيلها وإبقاءها في وضع "الاستعداد". يمكن أن يؤدي طلب الشبكة كل 15 ثانية إلى إبقاء الراديو الجوّال مفعّلاً باستمرار واستخدام طاقة البطارية بسرعة.

هناك ثلاثة أنواع عامة من التحديثات العادية:

  • الطلبات التي يبدأها المستخدم: إجراء تحديث استنادًا إلى سلوك بعض المستخدمين، مثل إيماءة السحب للتحديث.
  • الطلبات التي يبدأها التطبيق: إجراء تعديل بشكل متكرّر
  • الطلبات التي يبدأها الخادم: إجراء تعديل استجابةً لإشعار من خادم

يتناول هذا الموضوع كلًّا من هذه الإعدادات ويناقش طرقًا إضافية يمكن من خلالها تحسينها لتقليل استهلاك البطارية.

تحسين الطلبات التي بدأها المستخدم

تحدث الطلبات التي يبدأها المستخدم عادةً استجابةً لبعض سلوكيات المستخدم. على سبيل المثال، قد يسمح تطبيق يُستخدَم لقراءة أحدث المقالات الإخبارية للمستخدم بتنفيذ لفتة السحب لإعادة التحميل من أجل البحث عن مقالات جديدة. يمكنك استخدام الأساليب التالية للردّ على الطلبات التي يجريها المستخدم مع تحسين استخدام الشبكة.

الحد من طلبات المستخدمين

قد ترغب في تجاهل بعض الطلبات التي بدأها المستخدم إذا لم تكن بحاجة إليها، مثل إيماءات السحب المتعددة لإعادة التحميل خلال فترة زمنية قصيرة للتحقق من البيانات الجديدة بينما لا تزال البيانات الحالية حديثة. قد يؤدي تنفيذ كل طلب إلى استهلاك قدر كبير من الطاقة من خلال إبقاء الراديو في وضع التشغيل. وهناك نهج أكثر فعالية يتمثل في تقييد الطلبات التي يجريها المستخدم بحيث يمكن إجراء طلب واحد فقط على مدار فترة زمنية، ما يقلل من عدد مرات استخدام الراديو.

استخدام ذاكرة تخزين مؤقت

عن طريق تخزين بيانات تطبيقك مؤقتًا، يتم إنشاء نسخة محلية من المعلومات التي يحتاج تطبيقك إلى الرجوع إليها. يمكن لتطبيقك بعد ذلك الوصول إلى النسخة المحلية نفسها من المعلومات عدة مرات بدون الحاجة إلى فتح اتصال بالشبكة لتقديم طلبات جديدة.

يجب تخزين البيانات في ذاكرة التخزين المؤقت بشكلٍ فعّال قدر الإمكان، بما في ذلك موارد الثابتة والتنزيلات عند الطلب، مثل الصور بالحجم الكامل. يمكنك استخدام عناوين ملف التخزين المؤقت لبروتوكول HTTP لضمان عدم تسبُّب استراتيجية التخزين المؤقت في ملف التخزين المؤقت في عرض بيانات قديمة على تطبيقك. لمزيد من المعلومات عن تخزين الردود من الشبكة مؤقتًا، يُرجى الاطّلاع على مقالة تجنُّب عمليات التنزيل المتكرّرة.

على نظام التشغيل Android 11 والإصدارات الأحدث، يمكن لتطبيقك استخدام مجموعات البيانات الكبيرة نفسها التي تستخدمها التطبيقات الأخرى في حالات الاستخدام، مثل تعلُّم الآلة وتشغيل الوسائط. عندما يحتاج تطبيقك إلى الوصول إلى مجموعة بيانات مشتركة، يمكنه أولاً التحقق من وجود نسخة مخزَّنة مؤقتًا قبل محاولة تنزيل نسخة جديدة. للاطّلاع على مزيد من المعلومات عن مجموعات البيانات المشتركة، راجِع مقالة الوصول إلى مجموعات البيانات المشتركة.

استخدام معدل نقل بيانات أكبر لتنزيل المزيد من البيانات بمعدل أقل

عند الاتصال عبر شبكة لاسلكية، يؤدي استخدام نطاق تردد عالٍ إلى استهلاك طاقة أكبر في البطارية، ما يعني أنّ شبكة الجيل الخامس تستهلك عادةً طاقة أكثر مقارنةً بشبكة 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 دائم. ويؤدي ذلك إلى تقليل عدد الاتصالات المستمرة والسماح للمنصة بتحسين معدل نقل البيانات وتقليل التأثير المرتبط بذلك في عمر البطارية.