وئام من البرامج النصية داخل تطبيق أندرويد



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

ومع ذلك ، هناك طرق بديلة لتنزيل وتنفيذ البرامج النصية على جهاز محمول. لمزيد من التفاصيل تحت القط!

لذلك ، على الرغم من أننا لا نستطيع تحديث تطبيقات dex في وقت التشغيل ، إلا أنه يمكننا استخدام مترجمين بلغات البرمجة النصية المكتوبة بالكامل في Java. لذا فإن Oracle ، بدءًا من الإصدار 6 ، يتضمن محرك jhascript Rhino في JVM. حدث هذا بفضل تطبيق مواصفات JSR-223 ، والتي تعلن عن دعم Java بلغات البرمجة النصية.

يوجد حاليًا العديد من المحركات المدمجة في لغات البرمجة الشائعة مثل: Lua (Luaj) و Python (Jython) و Ruby (Jruby) و java-script (Rhino، ...). كل واحد منهم يسمح لك بتنفيذ البرامج النصية والوصول إلى وظائف مكتوبة بلغة جافا.
كدليل على الاحتمالات ، أقترح تنفيذ "بيئة" التنمية. سأترك رابطًا إلى المصادر في نهاية المقال. لكي لا أفسد المثال ، سأركز على Lua ، رغم أنه لا يوجد ما يمنعك من توصيل جميع المحركات في نفس الوقت والتبديل بينها. الإصدار الحالي من JLua في وقت الكتابة متاح في mvnrepository: org.luaj: luaj-jse: 3.0.1 .
يجب أن تحتوي كل بيئة تطوير تحترم نفسها على حقل لإدخال برنامج نصي ، وحقل لعرض النتيجة ، وزر يسمح لك بإكمال بنات أفكارك.

واجهة المستخدم لبيئة التطوير التي تحترم نفسها:

<?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent"> <EditText android:id="@+id/scriptInput" android:layout_width="0dp" android:layout_height="0dp" android:gravity="top|start" android:hint="@string/write_script" android:inputType="textMultiLine" android:padding="4dp" android:textColor="#000000" app:layout_constraintBottom_toTopOf="@id/scriptOutput" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> <TextView android:id="@+id/scriptOutput" android:layout_width="0dp" android:layout_height="0dp" android:hint="@string/script_output" android:padding="4dp" android:textColor="#000000" app:layout_constraintBottom_toTopOf="@id/executeButton" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@id/scriptInput" /> <Button android:id="@+id/executeButton" android:layout_width="0dp" android:layout_height="48dp" android:text="@string/run_script" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" /> </androidx.constraintlayout.widget.ConstraintLayout> 

من أجل تنفيذ البرنامج النصي Lua ، نحتاج إلى الحصول على البيئة العالمية التي سيتم تنفيذها فيه - Globals . يسمح لك Luaj بتخصيصه ، على سبيل المثال ، عن طريق تعيين المتغيرات أو إضافة المجلدات إلى فئات Java. ستكون إحدى الفرص المهمة لنا هنا هي إعداد تدفقات إخراج الرسائل ، لأنه يتم استخدام java.lang.System.out ، java.lang.System.err بشكل افتراضي ، وهو ليس مناسبًا جدًا عندما تحتاج إلى عرض نتيجة التنفيذ في TextView. لتغيير هذا تحتاج إلى إعادة تعريف القيم Globals # STDOUT و Globals # STDERR .

وبالتالي ، الآن علينا فقط تحميل صريرنا في البيئة وتنفيذها.

هذا ما يبدو في مثالي:

 private fun runLua(script: String) { val charset = StandardCharsets.UTF_8 val globals = JsePlatform.standardGlobals() val outStream = ByteArrayOutputStream() val outPrintStream = PrintStream(outStream, true, charset.name()) globals.STDOUT = outPrintStream globals.STDERR = outPrintStream try { globals.load(script).call() scriptOutput.setTextColor(Color.BLACK) scriptOutput.text = String(outStream.toByteArray(), charset) } catch (e: LuaError) { scriptOutput.setTextColor(Color.RED) scriptOutput.text = e.message } finally { outPrintStream.close() } } 

الآن دعونا نحاول توسيع مجموعة الوظائف المتاحة مع إمكانية إظهار Toast باستخدام الربط أعلاه لفئات Java. اجعل الأمر سهلاً باستخدام CoerceJavaToLua :

 globals.set("bubble", CoerceJavaToLua.coerce(Bubble(this))) ... private class Bubble(private val context: Context) { // called from lua fun show(message: String) { Toast.makeText(context, message, Toast.LENGTH_SHORT).show() } } 

النتيجة التي حصلت عليها هي:



وبالتالي ، مع مثال صغير ، درسنا إمكانية تشغيل البرامج النصية داخل تطبيق الهاتف المحمول. يمكن للقارئ الفضائي تخمين إمكانية تنزيل البرامج النصية من الأصول أو موارد التطبيق أو من الخادم. ما يمكن أن يكون مفيدا ، على سبيل المثال ، في الألعاب. لحسن الحظ ، فإن luaj متوافق مع أحد أكثر أطر عمل java للألعاب شيوعًا - Libgdx. بشكل عام ، يقتصر النطاق هنا فقط من خيال المطور.

مصادر العينة
لواج
جايثون
Jruby
الكركدن ( غلاف أندرويد )

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


All Articles