توضّح هذه الصفحة كيفية تقليل استخدام الذاكرة داخل تطبيقك بشكل استباقي. للحصول على معلومات عن كيفية إدارة نظام التشغيل Android للذاكرة، راجع نظرة عامة على إدارة الذاكرة
الذاكرة العشوائية (RAM) هي مورد قيّم لأي بيئة تطوير برامج، وتعدّ
أكثر قيمة لنظام التشغيل المتوافق مع الأجهزة الجوّالة الذي غالبًا ما تكون فيه الذاكرة الفعلية محدودة.
على الرغم من أن نظام تشغيل Android (ART) وجهاز Dalvik الافتراضيين يُجريان عمليات إعادة تحميل روتينية.
فإن هذا لا يعني أنه يمكنك تجاهل أوقات ومكان تخصيص تطبيقك للذاكرة وإصدارها.
لا تزال بحاجة إلى تجنب حدوث تسرُّبات الذاكرة الذي يحدث عادةً بسبب التمسك بالجسم
مراجع في متغيرات الأعضاء الثابتة - وإصدار أي
Reference
عناصر في
والوقت المناسب كما هو محدد في استدعاءات دورة الحياة.
مراقبة استخدام الذاكرة والذاكرة المتاحة
يجب العثور على مشاكل استخدام الذاكرة في تطبيقك قبل أن تتمكّن من حلّها. تساعدك أداة محلل الذاكرة في Android Studio في العثور على مشاكل الذاكرة وتحديدها بالطرق التالية:
- يمكنك الاطّلاع على كيفية تخصيص تطبيقك للذاكرة بمرور الوقت. يعرض أداة تحليل الذاكرة رسمًا بيانيًا في الوقت الفعلي لمقدار الذاكرة التي يستخدمها تطبيقك وعدد عناصر Java المخصّصة ووقت جمع القمامة .
- ابدأ أحداث جمع المهملات وخذ لقطة شاشة لمكبّر Java أثناء تنفيذ تطبيقك.
- يمكنك تسجيل عمليات تخصيص الذاكرة في تطبيقك، وفحص جميع العناصر المخصّصة، وعرض تتبع تسلسل استدعاء الدوال المتعلّق بكل عملية تخصيص، والانتقال إلى الرمز البرمجي المقابل في محرِّر "استوديو Android".
تحرير الذاكرة استجابةً للأحداث
يمكن لنظام التشغيل Android استرداد الذاكرة من تطبيقك أو إيقاف تطبيقك بالكامل إذا لزم الأمر لتوفير مساحة في الذاكرة
لأداء المهام المهمة، كما هو موضّح في
نظرة عامة على إدارة الذاكرة. لمزيد من المساعدة
تحقيق التوازن بين ذاكرة النظام وتجنب حاجة النظام لإيقاف عملية التطبيق، يمكنك تنفيذ
الـ
ComponentCallbacks2
واحدة في Activity
صفًا.
تُرسِل onTrimMemory()
طريقة الاستدعاء المقدَّمة إشعارًا إلى تطبيقك بشأن الأحداث المتعلّقة بدورة الحياة أو الذاكرة والتي تقدّم
فرصة جيدة لتطبيقك لتقليل استخدام الذاكرة بشكل طوعي. قد يؤدي إخلاء الذاكرة إلى تقليل
احتمالية إغلاق تطبيقك من خلال
قاتل منخفض الذاكرة.
يمكنك تنفيذ دالة الاستدعاء onTrimMemory()
للردّ على أحداث مختلفة مرتبطة بالذاكرة، كما هو موضّح في المثال التالي:
Kotlin
import android.content.ComponentCallbacks2 // Other import statements. class MainActivity : AppCompatActivity(), ComponentCallbacks2 { // Other activity code. /** * Release memory when the UI becomes hidden or when system resources become low. * @param level the memory-related event that is raised. */ override fun onTrimMemory(level: Int) { if (level >= ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN) { // Release memory related to UI elements, such as bitmap caches. } if (level >= ComponentCallbacks2.TRIM_MEMORY_BACKGROUND) { // Release memory related to background processing, such as by // closing a database connection. } } }
Java
import android.content.ComponentCallbacks2; // Other import statements. public class MainActivity extends AppCompatActivity implements ComponentCallbacks2 { // Other activity code. /** * Release memory when the UI becomes hidden or when system resources become low. * @param level the memory-related event that is raised. */ public void onTrimMemory(int level) { if (level >= ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN) { // Release memory related to UI elements, such as bitmap caches. } if (level >= ComponentCallbacks2.TRIM_MEMORY_BACKGROUND) { // Release memory related to background processing, such as by // closing a database connection. } } }
التحقق من حجم الذاكرة التي تحتاج إليها
للسماح بعمليات متعددة قيد التشغيل، يضع Android حدًا أقصى لحجم الذاكرة المخصصة لكل منها
التطبيق. يختلف الحدّ الأقصى الدقيق لحجم الذاكرة بين الأجهزة بناءً على مقدار ذاكرة الوصول العشوائي (RAM) المتوفّرة على الجهاز.
بوجه عام. إذا وصل تطبيقك إلى سعة الحِزمة وحاول تخصيص المزيد من الذاكرة، يُرسِل النظام
OutOfMemoryError
.
لتجنُّب نفاد الذاكرة، يمكنك طلب معلومات من النظام لتحديد مقدار مساحة الذاكرة المتوفّرة
على الجهاز الحالي. يمكنك الاستعلام عن هذا الرقم من النظام من خلال الاتصال بالرقم
getMemoryInfo()
.
ينتج عن ذلك
ActivityManager.MemoryInfo
يقدّم هذا الكائن معلومات عن حالة ذاكرة الجهاز الحالية، بما في ذلك البيانات المتوفّرة
الذاكرة وإجمالي الذاكرة وحد الذاكرة - مستوى الذاكرة الذي يبدأ عنده النظام
وإيقاف العمليات. يعرِض عنصر ActivityManager.MemoryInfo
أيضًا
lowMemory
،
وهو قيمة منطقية بسيطة تُعلمك ما إذا كانت ذاكرة الجهاز منخفضة.
يوضّح مثال مقتطف الرمز البرمجي التالي كيفية استخدام الطريقة getMemoryInfo()
في
تطبيقك.
Kotlin
fun doSomethingMemoryIntensive() { // Before doing something that requires a lot of memory, // check whether the device is in a low memory state. if (!getAvailableMemory().lowMemory) { // Do memory intensive work. } } // Get a MemoryInfo object for the device's current memory status. private fun getAvailableMemory(): ActivityManager.MemoryInfo { val activityManager = getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager return ActivityManager.MemoryInfo().also { memoryInfo -> activityManager.getMemoryInfo(memoryInfo) } }
Java
public void doSomethingMemoryIntensive() { // Before doing something that requires a lot of memory, // check whether the device is in a low memory state. ActivityManager.MemoryInfo memoryInfo = getAvailableMemory(); if (!memoryInfo.lowMemory) { // Do memory intensive work. } } // Get a MemoryInfo object for the device's current memory status. private ActivityManager.MemoryInfo getAvailableMemory() { ActivityManager activityManager = (ActivityManager) this.getSystemService(ACTIVITY_SERVICE); ActivityManager.MemoryInfo memoryInfo = new ActivityManager.MemoryInfo(); activityManager.getMemoryInfo(memoryInfo); return memoryInfo; }
استخدام بنى رموز أكثر كفاءة في استخدام الذاكرة
تستهلك بعض ميزات Android وفئات Java وإنشاءات الرموز البرمجية ذاكرة أكبر من غيرها. يمكنك تقليل مقدار الذاكرة التي يستخدمها تطبيقك من خلال اختيار بدائل أكثر فعالية في الرمز البرمجي.
استخدام الخدمات بشكل معتدل
ننصحك بشدة بعدم ترك الخدمات قيد التشغيل عندما يكون ذلك غير ضروري. إنّ ترك خدمات غير الضرورية تعمل هو أحد أسوأ الأخطاء التي يمكن أن يرتكبها تطبيق Android في إدارة الذاكرة. إذا كان تطبيقك يحتاج إلى خدمة للعمل في الخلفية، يجب عدم إبقاؤه مفعّلاً ما لم يكن بحاجة إلى تنفيذ مهمة. إيقاف الخدمة عند إكمال مهمتها وإلا، قد يتسبب في حدوث تسرب للذاكرة.
عند بدء خدمة، يفضّل النظام إبقاء عملية هذه الخدمة قيد التشغيل. يؤدي هذا السلوك إلى زيادة تكلفة عمليات الخدمة بشكل كبير لأنّ ذاكرة الوصول العشوائي التي تستخدمها إحدى الخدمات تظل غير متاحة للعمليات الأخرى. ويؤدي ذلك إلى تقليل عدد العمليات المخزّنة مؤقتًا التي يمكن للنظام الاحتفاظ بها في ذاكرة التخزين المؤقت LRU، ما يجعل عملية تبديل التطبيقات أقل فعالية. وقد يؤدي ذلك أيضًا إلى حدوث تداخل في النظام عندما تكون الذاكرة ممتلئة ولا يستطيع النظام الاحتفاظ بعدد كافٍ من العمليات لاستضافة جميع الخدمات التي تعمل حاليًا.
بشكل عام، تجنَّب استخدام الخدمات الثابتة بسبب الطلبات المستمرة التي تطلبها على
الذاكرة المتاحة. وبدلاً من ذلك، نقترح استخدام طريقة تنفيذ بديلة، مثل
WorkManager
لمزيد من المعلومات
حول كيفية استخدام WorkManager
لجدولة العمليات التي تتم في الخلفية، يُرجى الاطّلاع على
العمل المستمر.
استخدام حاويات البيانات المحسَّنة
بعض الفئات التي تقدّمها لغة البرمجة غير محسَّنة للاستخدام على
الأجهزة الجوّالة. على سبيل المثال، يمكن أن يكون تنفيذ
HashMap
العام غير فعّال في استخدام
الذاكرة لأنّه يحتاج إلى عنصر إدخال منفصل لكلّ عملية ربط.
يتضمن إطار عمل Android العديد من حاويات البيانات المحسَّنة، بما في ذلك
SparseArray
،
SparseBooleanArray
,
وLongSparseArray
.
على سبيل المثال، تكون صفوف SparseArray
أكثر كفاءة لأنّها تتجنّب ما إذا كان
بحاجة إلى
مربع تلقائي
المفتاح وأحيانًا القيمة، مما يؤدي إلى إنشاء كائن آخر أو كائنين لكل إدخال.
وإذا لزم الأمر، يمكنك دائمًا التبديل إلى الصفائف الأولية للحصول على بنية بيانات محدودة.
توخي الحذر بشأن التجريدات في الرموز البرمجية
غالبًا ما يستخدم المطوّرون العناصر المجردة كممارسة برمجة جيدة لأنّها يمكن أن تُحسِّن من مرونة الرمز البرمجي وسهولة صيانته. ومع ذلك، فإن عمليات التجريد أكثر تكلفة بكثير لأنها تتطلب عادةً المزيد من التعليمات البرمجية التي يلزم تنفيذها، مما يتطلب مزيدًا من الوقت وذاكرة الوصول العشوائي لتعيين الرمز في الذاكرة. وإذا لم تكن الرسومات التجريدية مفيدة بشكل كبير، تجنَّبها.
استخدام ملفات protobufs البسيطة للبيانات التسلسلية
البروتوكول الموارد الاحتياطية (protobufs) هي آلية محايدة لغة ومحايدة للمنصة وقابلة للتوسُّع مصمَّمة من قِبل Google لإنشاء تسلسل للبيانات المنظَّمة، وهو يشبه تنسيق XML، لكنّه أصغر وأسرع وأبسط. إذا كنت تستخدِم ملفات protobuf لبياناتك، استخدِم دائمًا ملفات protobuf خفيفة في الرمز البرمجي من جهة العميل. عادية تنشئ النماذج الأوّلية رمزًا مطوّلاً للغاية، ما قد يتسبب في العديد من المشاكل في تطبيقك، مثل زيادة استخدام ذاكرة الوصول العشوائي، وزيادة كبيرة في حجم APK، وبطء التنفيذ.
لمزيد من المعلومات، يُرجى الاطّلاع على ملف README الخاص بـ protobuf .
تجنُّب اضطرابات الذاكرة
لا تؤثّر أحداث جمع المهملات في أداء تطبيقك. ومع ذلك، فإن العديد من تجميع البيانات المهملة يمكن أن تؤدي الأحداث التي تحدث خلال فترة زمنية قصيرة إلى استنزاف البطارية بسرعة وكذلك بشكل هامشي زيادة الوقت اللازم لإعداد الإطارات بسبب التفاعلات الضرورية بين وحدة تجميع البيانات المهملة وسلاسل محادثات التطبيقات. كلما زاد الوقت الذي يقضيه النظام في جمع البيانات غير المرغوب فيها، زادت سرعة البطارية تصريف المياه.
وفي كثير من الأحيان، يمكن أن يؤدي انسحاب الذاكرة إلى وقوع عدد كبير من أحداث جمع البيانات غير المرغوب فيها. ضِمن التدريب، يصف اضطراب الذاكرة عدد العناصر المؤقتة المخصصة التي تحدث في مقدار الوقت.
على سبيل المثال، يمكنك تخصيص عناصر مؤقتة متعددة ضمن حلقة for
. أو
يمكنك إنشاء Paint
أو جديد
Bitmap
عنصرًا داخل
onDraw()
طريقة العرض. وفي كلتا الحالتين، ينشئ التطبيق الكثير من العناصر بسرعة وبحجم كبير. ويمكن أن تستهلك هذه العمليات
جميع الذاكرة المتاحة في الجيل الجديد بسرعة، ما يؤدي إلى حدوث حدث جمع القمامة
.
استخدِم أداة تحليل الذاكرة للعثور على الأماكن في في ما يتعلق بالتعليمة البرمجية حيث يكون اضطراب الذاكرة مرتفعًا قبل أن تتمكن من إصلاحها.
بعد تحديد المناطق التي تتضمّن مشاكل في الرمز البرمجي، حاوِل تقليل عدد عمليات التوزيع ضمن المناطق المهمة للأداء. ننصحك بنقل العناصر خارج الحلقات الداخلية أو نقلها إلى بنية تخصيص استنادًا إلى المصنع .
يمكنك أيضًا تقييم ما إذا كانت مجموعات العناصر تعود بالفائدة على حالة الاستخدام. أما باستخدام وحدة تجمع الكائنات، فبدلاً من إسقاط كائن ما على الأرض، فإنك تفرغ منه في بركة عندما لا يعود هناك حاجة إليه. وفي المرة التالية التي تحتاج فيها إلى مثيل كائن من هذا النوع، يمكنك الحصول عليه من الحزمة بدلاً من تخصيصه.
قيِّم الأداء بدقة لتحديد ما إذا كانت مجموعة العناصر مناسبة في حالة معيّنة. هناك حالات قد تؤدي فيها مجموعات الكائنات إلى زيادة الأداء سوءًا. وعلى الرغم من أن المسابح تتجنب مخصصات، تُحدث رسوم عامة أخرى. على سبيل المثال، تتطلّب عادةً صيانة المجموعة مزامنة لها، ما يتسبب في تكاليف غير قابلة للتجاهل. بالإضافة إلى ذلك، يمكن أن يؤدي محو مثيل العنصر المجمّع لتجنُّب تسرُّب الذاكرة أثناء الإصدار ثم بدء تشغيله أثناء اكتسابه إلى زيادة في التكلفة.
وضع المزيد من مثيلات الكائنات في المسبح أكثر من اللازم يضع أيضًا عبئًا على القمامة الأولية. على الرغم من أنّ مجموعات الكائنات تقلِّل من عدد استدعاءات جمع البيانات غير المرغوب فيها، ينتهي الأمر بها زيادة حجم العمل المطلوب لكل استدعاء، حيث يتناسب ذلك مع عدد وحدات البايت المباشرة (التي يمكن الوصول إليها).
إزالة المراجع والمكتبات التي تستهلك ذاكرة كبيرة
يمكن أن تستهلك بعض الموارد والمكتبات ضمن الرمز البرمجي ذاكرتك بدون أن تلاحظ ذلك. تشير رسالة الأشكال البيانية يمكن أن يؤثّر الحجم الإجمالي لتطبيقك، بما في ذلك المكتبات التابعة لجهات خارجية أو الموارد المضمَّنة، في مدى التي يستهلكها تطبيقك. يمكنك تحسين استهلاك التطبيق للذاكرة من خلال إزالة التكرارات أو المكونات غير الضرورية أو المتضخمة أو الموارد والمكتبات من التعليمات البرمجية.
تقليل الحجم الإجمالي لحِزم APK
يمكنك تقليل استخدام تطبيقك للذاكرة بشكل كبير عن طريق تقليل الحجم الإجمالي للتطبيق. يمكن أن يساهم حجم الصور النقطية والموارد وإطارات الرسوم المتحركة ومكتبات الجهات الخارجية في زيادة حجم لتطبيقك. ويوفر "استوديو Android" وحزمة تطوير البرامج (SDK) لنظام التشغيل Android أدوات متعددة للمساعدة في تقليل حجم مواردك وتبعياتك الخارجية. وتدعم هذه الأدوات طرقًا حديثة لتقليص التعليمات البرمجية، مثل تجميع R8:
لمزيد من المعلومات عن تقليل الحجم الكلي لتطبيقك، يُرجى الاطّلاع على مقالة تقليل حجم تطبيقك.
استخدام Hilt أو Dagger 2 لحقن التبعية
يمكن أن تعمل إطارات عمل حقن التبعيات على تبسيط الرمز البرمجي الذي تكتبه وتوفير بيئة تكيُّفية مفيدة للاختبار وتغييرات الإعدادات الأخرى.
إذا كنت تنوي استخدام إطار عمل إدخال التبعية في تطبيقك، ننصحك باستخدام وظيفة أو Dagger: Hilt هي مكتبة لنظام Android تهدف إلى حقن الاعتمادات، وهي تعمل على أساس Dagger. لا يستخدم تطبيق Dagger ميزة الانعكاس لفحص صورة الرمز. يمكنك استخدام تنفيذ وقت التجميع الثابت في تطبيقات Android باستخدام أداة Dagger بدون داعٍ. وقت التشغيل أو استخدام الذاكرة.
إن أطر عمل إدخال التبعية الأخرى التي تستخدم الانعكاس تقوم بتهيئة العمليات من خلال فحص التعليمات البرمجية للتعليقات التوضيحية. يمكن أن تتطلب هذه العملية المزيد من دورات وحدة المعالجة المركزية (CPU) وذاكرة الوصول العشوائي (RAM) بشكل كبير، ويمكن أن تتسبب تأخر ملحوظ عند تشغيل التطبيق.
توخ الحذر بشأن استخدام المكتبات الخارجية
لا تتم كتابة رمز المكتبة الخارجية في كثير من الأحيان لبيئات الأجهزة المحمولة ويمكن أن يكون غير فعال العمل على برنامج هاتف محمول. عندما تستخدم مكتبة خارجية، قد تحتاج إلى تحسين تلك المكتبة مكتبة للأجهزة المحمولة. خطط لهذا العمل مسبقًا وتحليل المكتبة من حيث وحجم الرمز وبصمة ذاكرة الوصول العشوائي (RAM) قبل استخدامه.
حتى بعض المكتبات المحسّنة للجوّال يمكن أن تتسبب في مشاكل بسبب اختلاف عمليات التنفيذ. على سبيل المثال، قد تستخدم مكتبة واحدة ملفات protobuf خفيفة بينما تستخدم مكتبة أخرى ملفات protobuf صغيرة، ما يؤدي إلى تنفيذ ملفَي protobuf مختلفَين في تطبيقك. ويمكن أن يحدث ذلك مع عمليات تنفيذ مختلفة لتسجيل السجلّات والإحصاءات وأُطر عمل تحميل الصور والتخزين المؤقت والعديد من الإجراءات الأخرى غير المتوقّعة.
على الرغم من أنّ أداة ProGuard يمكن أن تساعد في إزالة واجهات برمجة التطبيقات والموارد باستخدام علامات مناسبة، لا يمكنها إزالة التبعيات الداخلية الكبيرة للمكتبة. قد تتطلّب الميزات التي تريدها في
هذه المكتبات استخدام مكتبات تابعة من مستوى أقل. وتصبح هذه المشكلة أكثر خطورة عند استخدام
فئة فرعية من Activity
من مكتبة، والتي يمكن أن تتضمّن مجموعات كبيرة من التبعيات، عندما تستخدم المكتبات ميزة "العرض المرجعي"، وهي ميزة
شائعة وتتطلّب تعديل ProGuard يدويًا لكي تعمل.
تجنب استخدام مكتبة مشتركة لميزة واحدة أو اثنتين فقط من بين العشرات. لا تجمع مقدار من التعليمات البرمجية ومقدار النفقات العامة التي لا تستخدمها. عند التفكير في استخدام مكتبة، ابحث عن طريقة تنفيذ تتطابق بشكل كبير مع ما تحتاجه. بخلاف ذلك، يمكنك إنشاء عملية التنفيذ الخاصة بك.