رمز التحول في أندرويد 2. تحليل AST



سأتحدث في هذا المقال عن كيفية حل المشكلات التي واجهتها في الجزء السابق أثناء تنفيذ المشروع .


أولاً ، عند تحليل فئة قابلة للتحويل ، يجب أن تفهم بطريقة ما ما إذا كانت هذه الفئة هي خليفة Activity أو Fragment ، حتى يمكننا أن نقول بثقة أن الفصل مناسب لتحويلنا.


ثانياً ، في ملف .class @State لجميع الحقول مع التعليق التوضيحي @State ، تحتاج إلى تحديد النوع بشكل صريح من أجل استدعاء الأسلوب المقابل في الحزمة لحفظ / استعادة الحالة ، ويمكنك تحديد النوع بالتحديد عن طريق تحليل جميع آباء الفئة والواجهات التي يقومون بتنفيذها.


وبالتالي ، تحتاج فقط إلى أن تكون قادراً على تحليل شجرة بناء الجملة من الملفات المحولة.


تحليل AST


من أجل تحليل الفصل للميراث من فئة أساسية (في حالتنا هو Activity/Fragment ) ، يكفي أن يكون المسار الكامل إلى ملف .class قيد الدراسة. علاوة على ذلك ، كل هذا يتوقف على تطبيق المحول: إما تحميل الفئة من خلال ClassLoader ، أو تحليلها من خلال ASM باستخدام ClassReader و ClassVisitor ، والحصول على جميع المعلومات اللازمة حول الفئة.


ملف الوصول


ضع في اعتبارك أن الفصل الذي نحتاج إليه يمكن أن يكون خارج نطاق المشروع ، ولكن في بعض المكتبات (على سبيل المثال ، يكون Activity في Android SDK). لذلك ، قبل بدء التحول ، تحتاج إلى الحصول على قائمة المسارات إلى جميع ملفات .class المتاحة.


للقيام بذلك ، قم بإجراء تغييرات صغيرة على المحولات :


 @Override Set<? super QualifiedContent.Scope> getReferencedScopes() { return ImmutableSet.of( QualifiedContent.Scope.EXTERNAL_LIBRARIES, QualifiedContent.Scope.SUB_PROJECTS ) } 

تسمح لك طريقة getReferencedScopes بالوصول إلى الملفات من النطاقات المحددة ، وهذا سيكون مجرد وصول للقراءة دون إمكانية التحول. فقط ما نحتاجه. في طريقة transform ، يمكن الحصول على هذه الملفات بنفس الطريقة التي يتم الحصول عليها من النطاقات الرئيسية:


 transformInvocation.referencedInputs.each { transformInput -> transformInput.directoryInputs.each { directoryInput -> // .  directoryInput.file.absolutePath } transformInput.jarInputs.each { jarInput -> // .  jarInput.file.absolutePath } } 

وهناك شيء آخر ، يجب استلام الملفات من Andoid SDK بشكل منفصل:


 project.extensions.findByType(BaseExtension.class).bootClasspath[0].toString() 

شكرا جوجل ، مريحة للغاية.


تعبئة الطبقة


يعد ملء قائمة بجميع ملفات .class المتاحة لنا بأيدي كئيبة إلى حد ما: نظرًا لأننا نحصل على أدلة أو ملفات jar كمدخلات ، فأنت بحاجة إلى الالتفاف عليها جميعًا والحصول على ملفات .class بشكل صحيح. هنا استعملت مكتبة javassist المذكورة سابقًا. انها تفعل كل شيء تحت غطاء محرك السيارة و plus لديه api مريحة للعمل مع الطبقات المستلمة. في النهاية ، تحتاج فقط إلى نقل المسار إلى الملفات وملء ClassPool :


 ClassPool.getDefault().appendClassPath("  ") 

قبل بدء التحويل ، يتم تعبئة ClassPool من جميع مصادر الملفات الممكنة:


 fillPoolAndroidInputs(classPool) fillPoolReferencedInputs(transformInvocation, classPool) fillPoolInputs(transformInvocation, classPool) 

التفاصيل في المحولات .


تحليل الصف


الآن بعد ClassPool ، يبقى التخلص من التعليقات التوضيحية @Stater . للقيام بذلك ، قم بإزالة علامة الاختيار في أسلوب visitAnnotation الخاص visitAnnotation ، وفحص ببساطة الفئة الفائقة لكل فئة بحثًا عن وجود Activity/Fragment في التسلسل الهرمي للميراث. الحصول على أي فئة بالاسم من فئة pool javassist بسيط جدًا:


 CtClass currentClass = ClassPool.getDefault().get(className.replace("/", ".")) 

وبالفعل مع CtClass يمكنك الحصول على CtClass أو currentClass.interfaces . من خلال مقارنة الطبقة الفائقة ، قمت بفحص النشاط / الشظية.


وأخيرًا ، للتخلص من StateType وعدم تحديد نوع الحقل الذي يجب حفظه بشكل صريح ، فعلت الشيء نفسه تقريبًا. للراحة ، تمت كتابة معين (مع الاختبارات ) يقوم بتوزيع الواصف الحالي في النوع المدعوم من قبل الحزمة.


نتيجة لذلك ، لم يتغير تحويل الشفرة ؛ لقد تم تغيير آلية تحديد نوع المتغير فقط.


لذلك ، بدمج .class للعمل مع ملفات .class ، تمكنت من تنفيذ الفكرة الأصلية المتمثلة في حفظ المتغيرات في الحزم باستخدام تعليق توضيحي واحد فقط.


إنتاجية


هذه المرة ، لاختبار الأداء ، قمت بتوصيل المكون الإضافي بمشروع حقيقي ، لأن ملء فئة البلياردو يعتمد على عدد الملفات في المشروع والمكتبات المختلفة.
فحص كل هذا من خلال ./gradlew clean build --scan . تأخذ transformClassesWithStaterTransformForDebug حوالي 2.5 ثانية. قمت بقياس Activity واحد مع 50 حقل @State ومع 10 مثل هذه Activity ، لا تتغير السرعة كثيرًا.

Source: https://habr.com/ru/post/ar470209/


All Articles