التحقق من سلوك التطبيق في وقت تشغيل Android (ART)

وقت تشغيل Android (ART) هو وقت التشغيل التلقائي للأجهزة التي تعمل بالإصدار 5.0 من نظام التشغيل Android (مستوى واجهة برمجة التطبيقات 21) والإصدارات الأحدث. يوفر وقت التشغيل هذا عددًا من الميزات التي تعمل على تحسين الأداء وسلاسة نظام التشغيل Android وتطبيقاته. يمكنك العثور على مزيد من المعلومات حول الميزات الجديدة في شكل ART على الرابط نقدّم لك لوحة ART.

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

معالجة مشاكل جمع البيانات غير المرغوب فيها (GC)

ضمن Dalvik، غالبًا ما تجد التطبيقات أنه من المفيد طلب System.gc() بشكل صريح لمطالبة بجمع البيانات غير المرغوب فيها (GC). ومن المفترض ألا يكون هذا الإجراء ضروريًا مع ART، خاصةً إذا كنت تستدعي جمع البيانات غير المرغوب فيها لمنع حدوث ظهور من نوع GC_FOR_ALLOC أو لتقليل التجزئة. يمكنك التحقق من وقت التشغيل قيد الاستخدام من خلال الاتصال بـ System.getProperty("java.vm.version"). إذا كانت السمة ART قيد الاستخدام، تكون قيمة السمة "2.0.0" أو أعلى.

تستخدم تقنية ART أداة جمع النسخ المتزامن (CC) الذي يضغط بشكل متزامن كومة Java. لهذا السبب، يجب تجنُّب استخدام الأساليب غير المتوافقة مع ضغط GC (مثل حفظ المؤشرات لبيانات مثيل الكائن). وهذا مهم بشكل خاص للتطبيقات التي تستخدم الواجهة الأصلية لـ Java (JNI). لمزيد من المعلومات، يُرجى الاطّلاع على المقالة منع مشاكل JNI.

منع مشاكل JNI

معيار JNI الخاص بـ ART أكثر صرامة إلى حد ما من Dalvik. إنها لفكرة جيدة بشكل خاص استخدام وضع CheckJNI لاكتشاف المشكلات الشائعة. إذا كان تطبيقك يستخدم كود C/C++ ، فيجب عليك مراجعة المقالة التالية:

تصحيح الأخطاء في Android JNI باستخدام CheckJNI

التحقق من رمز JNI لمشاكل جمع البيانات غير المرغوب فيها

قد ينقل جامع النسخ المتزامن (CC) الكائنات في الذاكرة لضغطها. إذا كنت تستخدم التعليمات البرمجية C/C++ ، لا تُجرِ أي عمليات غير متوافقة مع ضغط GC. لقد أجرينا تحسينات على أداة CheckJNI لتحديد بعض المشاكل المحتملة (كما هو موضّح في مقالة تغييرات في المراجع المحلية ضمن ICS).

من النقاط التي يجب الانتباه لها على وجه الخصوص استخدام الدالتين Get...ArrayElements() وRelease...ArrayElements(). في أوقات التشغيل التي تتضمّن وحدة GC غير مضغوطة، تعرض دوال Get...ArrayElements() عادةً مرجعًا إلى الذاكرة الفعلية التي تدعم كائن الصفيف. إذا أجريت تغييرًا على أحد عناصر الصفيف التي تم عرضها، يتم تغيير كائن الصفيفة بنفسه (وعادةً ما يتم تجاهل الوسيطات إلى Release...ArrayElements()). في المقابل، إذا كان ضغط GC قيد الاستخدام، قد تعرض دوال Get...ArrayElements() نسخة من الذاكرة. وإذا أسأت استخدام المرجع عند استخدام ضغط GC، فقد يؤدي ذلك إلى تلف الذاكرة أو حدوث مشاكل أخرى. على سبيل المثال:

  • إذا أجريت أي تغييرات على عناصر الصفيف المعروضة، عليك طلب دالة Release...ArrayElements() المناسبة عند الانتهاء للتأكّد من نسخ التغييرات التي أجريتها بشكل صحيح إلى كائن المصفوفة الأساسي.
  • عند رفع إصبعك عن عناصر مصفوفة الذاكرة، عليك استخدام الوضع المناسب، بناءً على التغييرات التي أجريتها:
    • إذا لم يتم إجراء أي تغييرات على عناصر الصفيف، استخدِم الوضع JNI_ABORT لتحرير الذاكرة بدون نسخ التغييرات إلى كائن الصفيف الأساسي.
    • إذا أجريت تغييرات على المصفوفة ولم تعُد بحاجة إلى المرجع، استخدِم الرمز 0 (الذي يعدّل كائن المصفوفة ويحرر نسخة من الذاكرة).
    • إذا أجريت تغييرات على المصفوفة التي تريد تطبيقها وكنت تريد الاحتفاظ بنسخة من المصفوفة، استخدِم JNI_COMMIT (الذي يعدّل عنصر الصفيف الأساسي ويحتفظ بالنسخة).
  • عند الاتصال بـ Release...ArrayElements()، يتم عرض المؤشر نفسه الذي تم عرضه في الأصل من خلال Get...ArrayElements(). على سبيل المثال، ليس من الآمن إضافة المؤشر الأصلي (للبحث عبر عناصر الصفيف المعروضة) ثم تمرير المؤشر المتزايد إلى Release...ArrayElements(). قد يؤدي تمرير هذا المؤشر المعدّل إلى إخلاء مساحة في الذاكرة غير الصحيحة، ما يؤدي إلى تلف الذاكرة.

معالجة الخطأ

يعرض مؤشر JNI الخاص بـ ART أخطاء في عدد من الحالات التي لا يعرض فيها Dalvik. (مرة أخرى، يمكنك اكتشاف العديد من هذه الحالات عن طريق الاختبار باستخدام CheckJNI).

على سبيل المثال، إذا تم استدعاء RegisterNatives باستخدام طريقة غير موجودة (ربما بسبب إزالة الطريقة باستخدام أداة مثل ProGuard)، فإن ART الآن يعرض NoSuchMethodError بشكل صحيح:

08-12 17:09:41.082 13823 13823 E AndroidRuntime: FATAL EXCEPTION: main
08-12 17:09:41.082 13823 13823 E AndroidRuntime: java.lang.NoSuchMethodError:
    no static or non-static method
    "Lcom/foo/Bar;.native_frob(Ljava/lang/String;)I"
08-12 17:09:41.082 13823 13823 E AndroidRuntime:
    at java.lang.Runtime.nativeLoad(Native Method)
08-12 17:09:41.082 13823 13823 E AndroidRuntime:
    at java.lang.Runtime.doLoad(Runtime.java:421)
08-12 17:09:41.082 13823 13823 E AndroidRuntime:
    at java.lang.Runtime.loadLibrary(Runtime.java:362)
08-12 17:09:41.082 13823 13823 E AndroidRuntime:
    at java.lang.System.loadLibrary(System.java:526)

يسجِّل ART أيضًا خطأً (مرئيًا في logcat) في حال استدعاء RegisterNatives بدون طرق:

W/art     ( 1234): JNI RegisterNativeMethods: attempt to register 0 native
methods for <classname>

بالإضافة إلى ذلك، تعرض دالتا JNI GetFieldID() وGetStaticFieldID() الآن بشكل صحيح NoSuchFieldError بدلاً من عرض قيمة فارغة. وبالمثل، يتم الآن عرض NoSuchMethodError بشكل صحيح للسمتين GetMethodID() وGetStaticMethodID(). وقد يؤدي هذا إلى إخفاقات CheckJNI بسبب الاستثناءات التي لم تتم معالجتها أو الاستثناءات التي يتم طرحها لمستخدمي Java بالرمز الأصلي. وهذا يجعل من المهم بشكل خاص اختبار التطبيقات المتوافقة مع ART باستخدام وضع CheckJNI.

تتوقع ART من مستخدمي طُرق JNI CallNonvirtual...Method() (مثل CallNonvirtualVoidMethod()) أن يستخدموا فئة الإعلان الخاصة بالطريقة، وليس فئة فرعية، وفقًا لما تقتضيه مواصفات JNI.

منع المشاكل المتعلقة بحجم التكدس

كان لدى Dalvik تكدسات منفصلة للرموز الأصلية ولغة Java، مع حجم تكديس تلقائي من Java يبلغ 32 كيلوبايت، وحجم مكدس أصلي تلقائي من 1 ميغابايت. يحتوي ART على تكديس موحّد للحصول على منطقة محلية أفضل. في العادة، يجب أن يكون حجم حزمة ART Thread مطابقًا تقريبًا لحجم حزمة Dalvik. ومع ذلك، إذا حددت أحجام التكدس بشكل صريح، فقد تحتاج إلى إعادة النظر في تلك القيم للتطبيقات التي يتم تشغيلها في ART.

  • في لغة Java، راجِع الطلبات التي يتم توجيهها إلى الدالة الإنشائية Thread التي تحدِّد حجمًا واضحًا لحزمة البيانات. على سبيل المثال، ستحتاج إلى زيادة المقاس إذا ظهر StackOverflowError.
  • في لغة C/C++ ، راجِع استخدام pthread_attr_setstack() وpthread_attr_setstacksize() لسلاسل المحادثات التي تشغِّل أيضًا رمز Java من خلال JNI. في ما يلي مثال على الخطأ الذي يتم تسجيله عندما يحاول أحد التطبيقات الاتصال بمؤشر JNI AttachCurrentThread() عندما يكون حجم سلسلة pthread صغيرًا جدًا:
    F/art: art/runtime/thread.cc:435]
        Attempt to attach a thread with a too-small stack (16384 bytes)

تغييرات نماذج العناصر

سمح Dalvik بشكل غير صحيح للفئات الفرعية بتجاوز الطرق الخاصة بالحزمة. يصدر ART تحذيرًا في مثل هذه الحالات:

Before Android 4.1, method void com.foo.Bar.quux()
would have incorrectly overridden the package-private method in
com.quux.Quux

إذا كنت تنوي إلغاء طريقة فئة ما في حزمة مختلفة، يمكنك تحديد الطريقة على أنّها public أو protected.

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

Class.getSuperclass() == java.lang.Object.class

بدلاً من المتابعة حتى تعرض الطريقة null.

يتلقى الخادم الوكيل InvocationHandler.invoke() الآن null إذا لم تكن هناك وسيطات بدلاً من صفيف فارغ. تم توثيق هذا السلوك سابقًا ولكن لم يتم التعامل معه بشكل صحيح في Dalvik. تواجه الإصدارات السابقة من Mockito صعوبات في ذلك، لذا استخدم إصدار Mockito محدث عند الاختبار باستخدام ART.

حلّ مشاكل الترجمة في AOT

يجب أن يعمل تجميع Java (AOT) الخاص بـ ART مع جميع أكواد Java القياسية. يتم تجميع البيانات باستخدام أداة dex2oat الخاصة بـ ART. إذا واجهت أي مشاكل متعلّقة بـ dex2oat عند التثبيت، يُرجى إعلامنا (يُرجى الاطّلاع على الإبلاغ عن المشاكل) لنتمكّن من حلّها في أسرع وقت ممكن. هناك مشكلتان يجب ملاحظتهما:

  • يجري ART عملية تحقق أكثر إحكامًا باستخدام رمز بايت في وقت التثبيت مقارنةً بأداة Dalvik. يجب أن تكون التعليمات البرمجية التي أنتجتها أدوات إنشاء Android جيدة. ومع ذلك، قد تنتج عن بعض أدوات ما بعد المعالجة (خاصة الأدوات التي تُستخدم إخفاء مفاتيح فك التشفير) ملفات غير صالحة يسمح "دالفيك" لها باستخدامها ولكن ترفضها أداة ART. لقد عملنا مع مورّدي الأدوات للعثور على هذه المشاكل وحلّها. وفي كثير من الحالات، يمكن أن يؤدي الحصول على أحدث الإصدارات من أدواتك وإعادة إنشاء ملفات DEX إلى حل هذه المشاكل.
  • تشمل بعض المشاكل المعتادة التي يتم الإبلاغ عنها من خلال أداة التحقّق من نوع ART ما يلي:
    • مسار عنصر التحكّم غير صالح.
    • غير متوازن monitorenter/monitorexit
    • حجم قائمة نوع المَعلمة 0-length
  • تعتمد بعض التطبيقات على تنسيق ملف .odex المثبَّت في /system/framework أو /data/dalvik-cache أو في دليل الإخراج المحسَّن في DexClassLoader. وقد أصبحت هذه الملفات الآن ملفات ELF وليست بتنسيق موسّع من ملفات DEX. تحاول أداة ART اتّباع قواعد التسمية والقفل نفسها التي يتّبعها تطبيق Dalvik، ولكن يجب ألا تعتمد التطبيقات على تنسيق الملف، وقد يخضع التنسيق للتغيير بدون إشعار.

    ملاحظة: في الإصدار Android 8.0 (المستوى 26 لواجهة برمجة التطبيقات) والإصدارات الأحدث، تم إيقاف دليل الإخراج المحسَّن DexClassLoader. لمزيد من المعلومات، يمكنك الاطّلاع على المستندات الخاصة بالدالة الإنشائية DexClassLoader().

الإبلاغ عن المشاكل

إذا واجهت أي مشاكل لا تتعلق بمشاكل JNI في التطبيق، يمكنك الإبلاغ عنها من خلال أداة Android Open Source Project ISSUE Tracker على الرابط https://code.google.com/p/android/issues/list. تضمين "adb bugreport" ورابط إلى التطبيق في "متجر Google Play" في حال توفّره بخلاف ذلك، يمكنك إرفاق ملف APK يعيد إظهار المشكلة، إن أمكن. تجدر الإشارة إلى أنّ المشاكل (بما في ذلك المرفقات) مرئية للجميع.