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

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

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

يتم تنفيذ المراسلة عبر السحابة الإلكترونية من Firebase باستخدام اتصال TCP/IP دائم. وهذا يقلل من عدد الاتصالات الدائمة ويسمح للنظام الأساسي بتحسين معدل نقل البيانات وتقليل التأثير المرتبط بها على عمر البطارية.