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

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