الفرق بين المراجعتين ل"Cordova/plugins android"

من موسوعة حسوب
اذهب إلى التنقل اذهب إلى البحث
(أنشأ الصفحة ب'<noinclude>{{DISPLAYTITLE:إضافات أندرويد في Cordova}}</noinclude> تصنيف: Cordova يقدم هذا القسم تفاصيل عن كيفية تق...')
 
سطر 1: سطر 1:
 
<noinclude>{{DISPLAYTITLE:إضافات أندرويد في Cordova}}</noinclude>
 
<noinclude>{{DISPLAYTITLE:إضافات أندرويد في Cordova}}</noinclude>
[[تصنيف: Cordova]]
+
[[تصنيف: Cordova]]
يقدم هذا القسم تفاصيل عن كيفية تقديم شيفرات الإضافات الأصلية (native plugin code) على منصة أندرويد. قبل قراءة هذه الصفحة، راجع صفحة دليل تطوير الإضافات [../../hybrid/plugins/index.html  Plugin Development Guide] للحصول على نظرة عامة على بنية الإضافات وواجهات [[JavaScript|JavaScript]] الخاصة بها. يستمر هذا القسم في تطوير مثال الإضافة echo الوارد في دليل تطوير الإضافات، والذي يتواصل بين المعرض [[Cordova/webviews|webview]] الخاص بكوردوفا وبين المنصة الأصلية.  لمثال آخر، راجع أيضًا التعليقات في [https://github.com/apache/cordova-android/blob/master/framework/src/org/apache/cordova/CordovaPlugin.java  CordovaPlugin.java].  
+
يقدم يوضح هذا القسم كيفية تقديم (implement) أكواد الإضافات الأصلية (native plugin code) على منصة أندرويد.
  
تستند إضافات أندرويد على نظام منصة Cordova-Android، التي أُنشئت من معرض [[Cordova/webviews|webview]] أندرويد مع جسر أصلي (native bridge). يتكون الجزء الأصلي من إضافات أندرويد من صنف [[Java|جافا]] واحد، والذي يوسع الصنف <code>CordovaPlugin</code>، ويعيد تعريف إحدى توابعه <code>execute</code>.  
+
قبل قراءة هذه الصفحة، راجع صفحة [[Cordova/plugins|دليل تطوير الإضافات]] لتكوين نظرة عامة على بنية الإضافات وواجهات [[JavaScript]] الخاصة بها. يواصل هذا القسم تطوير مثال الإضافة echo الوارد في [[Cordova/plugins|دليل تطوير الإضافات]]، والذي يربط الاتصال من المعرض [[Cordova/webviews|webview]] الخاص بكوردوفا إلى المنصة الأصلية (native platform)، والعكس بالعكس. للحصول على مثال آخر، راجع التعليقات في [https://github.com/apache/cordova-android/blob/master/framework/src/org/apache/cordova/CordovaPlugin.java CordovaPlugin.java].
  
== إعداد صنف الإضافة (Plugin Class Mapping) ==  
+
تستند إضافات أندرويد على منصة <code>Cordova-Android</code>، التي تُنشؤُ من المعرض [[Cordova/webviews|Android WebView]] إضافة إلى جسر أصلي (native bridge). يتألف الجزء الأصلي من إضافات أندرويد من صنف [[Java|جافا]] واحد على الأقل، والذي يوسع الصنف <code>CordovaPlugin</code> ويعيد تعريف أحد توابعه <code>execute</code>.
 +
==إعداد صنف الإضافة (Plugin Class Mapping)==
 +
تستخدم واجهة [[JavaScript]] الخاصة بالإضافة التابع <code>cordova.exec</code> على النحو التالي:<syntaxhighlight lang="javascript">exec(<successFunction>, <failFunction>, <service>, <action>, [<args>]);‎</syntaxhighlight>هذه الشيفرة ترسل طلبية (request) من [[Cordova/webviews|webview]] إلى الجانب الأصلي (native side) لأندرويد، وتستدعي التابع <code>action</code> فعليا على الصنف <code>service</code>، مع وسائط إضافية تُمرر في المصفوفة <code>args</code>.
  
تستخدم واجهة [[JavaScript|JavaScript]] الخاصة بالإضافة التابع <code>cordova.exec</code> على النحو التالي:
+
سواء أقمت بتوزيع الإضافة على هيئة ملف [[Java|جافا]]، أو كملف مضغوط <code>jar</code>، يجب تحديد الإضافة في ملف <code>res/xml/config.xml</code> الخاص بتطبيق <code>Cordova-Android</code>. انظر صفحة [[Cordova/plugins|الإضافات]] لمزيد من المعلومات حول كيفية استخدام ملف <code>plugin.xml</code> لإدارج العنصر <code>feature</code>:<syntaxhighlight lang="xml"><feature name="<service_name>">
<syntaxhighlight>exec(<successFunction>, <failFunction>, <service>, <action>, [<args>]);‎</syntaxhighlight>
 
 
 
هذا يرسل طلبية (request) من [[Cordova/webviews|webview]] إلى الجانب الأصلي (native side) لأندرويد، ويستدعي فعليا التابع <code>action</code> على الصنف <code>service</code>، مع وسائط إضافية تُمرر في المصفوفة <code>args</code>.
 
 
 
سواء أقمت بتوزيع الإضافة على هيئة ملف [[Java|جافا]]، أو كملف مضغوط jar، يجب تحديد الإضافة في ملف <code>res/xml/config.xml</code> الخاص بتطبيق Cordova-Android. انظر صفحة الإضافات لمزيد من المعلومات حول كيفية استخدام ملف <code>plugin.xml</code> لإدارج العنصر <code>feature</code>:  
 
<syntaxhighlight><feature name="<service_name>">
 
 
     <param name="android-package" value="<full_name_including_namespace>" />
 
     <param name="android-package" value="<full_name_including_namespace>" />
</feature>‎</syntaxhighlight>  
+
</feature>‎</syntaxhighlight>يتطابق <code>service_name</code> مع الاسم المستخدم عند استدعاء دالة [[Java|الجافا]] <code>exec</code> . قيمته تساوي الاسم الكامل المؤهل لصنف [[Java|جافا]] (ava class's fully qualified namespace identifier). بخلاف ذلك، يمكن أن تُصرّف (compiled) الإضافة لكنها لن تكون متاحة لكوردوفا.
 
+
==تهيئة الإضافات ودورة الحياة (Plugin Initialization and Lifetime)==
يتطابق service_name مع الاسم المستخدم عند استدعاء دالة [[Java|جافا]] <code>exec</code> . القيمة تساوي الاسم المؤهل الكامل لصنف [[Java|جافا]]. بخلاف ذلك، قد تُصرّف (compile) الإضافة لكنها لن تكون متاحة لـ Cordova.  
+
يتم إنشاء نسخة (instance) واحدة من كائن الإضافة خلال دورة حياة المعرض <code>WebView</code>. لا يتم إنشاء نسخ للإضافات حتى تتم الإشارة إليها أولاً عبر استدعاء من [[Java|جافا]]، إلا إذا في حال إضافة وسم <code><param></code> مع خاصية <code>onload</code> مساوية للقيمة <code>name</code>، إلى <code>"true"</code> في <code>config.xml</code>. فمثلا،<syntaxhighlight><feature name="Echo">
 
 
== تهيئة الإضافات ودورة الحياة (Plugin Initialization and Lifetime) ==  
 
 
 
يتم إنشاء نسخة (instance) واحدة من كائن الإضافة خلال دورة حياة المعرض <code>WebView</code>. لا يتم إنشاء نسخ للإضافات حتى تتم الإشارة إليها أولاً عبر استدعاء من [[Java|جافا]]، إلا إذا في حال إضافة وسم <code><param></code> مع خاصية <code>onload</code> مساوية للقيمة <code>name</code>، إلى <code>"true"</code> في <code>config.xml</code>. فمثلا،  
 
<syntaxhighlight><feature name="Echo">
 
 
     <param name="android-package" value="<full_name_including_namespace>" />
 
     <param name="android-package" value="<full_name_including_namespace>" />
 
     <param name="onload" value="true" />
 
     <param name="onload" value="true" />
</feature>‎</syntaxhighlight>  
+
</feature>‎</syntaxhighlight>يجب أن تستخدم الإضافات التابع <code>initialize</code> في مرحلة الانطلاق.<syntaxhighlight>@Override
 
 
يجب أن تستخدم الإضافات التابع <code>initialize</code> في مرحلة الانطلاق.  
 
<syntaxhighlight>@Override
 
 
public void initialize(CordovaInterface cordova, CordovaWebView webView) {
 
public void initialize(CordovaInterface cordova, CordovaWebView webView) {
 
     super.initialize(cordova, webView);
 
     super.initialize(cordova, webView);
 
     // your init code here
 
     // your init code here
}‎</syntaxhighlight>  
+
}‎</syntaxhighlight>يمكن للإضافات الوصول إلى أحداث دورة حياة أندرويد، ويمكنها التعامل معها من خلال توسيع إحدى التوابع (<code>onResume</code>، <code>onDestroy</code>، إلخ). الإضافات ذات الطلبيات الطويلة (long-running requests)، أو [[Cordova/Activity|النشاط]]ات الخلفية، مثل قارئات الوسائط، أو المستمعات (listeners)، أو ذات الحالة الداخلية (internal state) يجب أن تقوم بتقديم التابع <code>onReset()</code>. يتم تنفيذها عند انتقال <code>WebView</code> إلى صفحة جديدة، أو عند تحديث الصفحة، وهو ما يؤدي إلى إعادة تحميل [[Java|جافا]].
 
+
==كتابة إضافة [[Java|جافا]]ا لأندرويد==
يمكن للإضافات الوصول إلى أحداث دورة حياة أندرويد، ويمكنها التعامل معها من خلال توسيع إحدى التوابع (<code>onResume</code>، <code>onDestroy</code>، إلخ). الإضافات ذات الطلبيات الطويلة (long-running requests)، أو [[Cordova/Activity|النشاط]]ات الخلفية، مثل قارئات الوسائط، أو المستمعات (listeners)، أو ذات الحالة الداخلية (internal state) يجب أن تقوم بتقديم التابع <code>onReset()</code>. يتم تنفيذها عند انتقال <code>WebView</code> إلى صفحة جديدة، أو عند تحديث الصفحة، وهو ما يؤدي إلى إعادة تحميل [[Java|جافا]].  
+
يرسل استدعاء من [[JavaScript]] طلبية إضافة (plugin request) إلى الجانب الأصلي (native side)، ويتم تعيين ملف [[Java|جافا]] المقابل الخاص بالإضافة في ملف <code>config.xml</code>، ولكن كيف سيبدو الشكل النهائي لصنف أندرويد [[Java|جافا]] الخاص بالإضافة؟ أي شيء يتم إرساله إلى الإضافة عبر دالة [[JavaScript|جافااسكريبت]] <code>exec</code> سيُمرّر إلى تابع الصنف <code>execute</code> الخاص بالإضافة. معظم تطبيقات <code>execute</code> تبدو كالتالي:<syntaxhighlight>@Override
 
 
== كتابة إضافة [[Java|جافا]]ا لأندرويد ==  
 
 
 
يرسل استدعاء من [[JavaScript|JavaScript]] طلبية إضافة (plugin request) إلى الجانب الأصلي (native side)، ويتم تعيين ملف [[Java|جافا]] المقابل الخاص بالإضافة في ملف <code>config.xml</code>، ولكن كيف سيبدو الشكل النهائي لصنف أندرويد [[Java|جافا]] الخاص بالإضافة؟ أي شيء يتم إرساله إلى الإضافة عبر دالة [[JavaScript|[[Java|جافا]]اسكريبت]] <code>exec</code> سيُمرّر إلى تابع الصنف <code>execute</code> الخاص بالإضافة. معظم تطبيقات <code>execute</code> تبدو كالتالي:  
 
<syntaxhighlight>@Override
 
 
public boolean execute(String action, JSONArray args, CallbackContext callbackContext) throws JSONException {
 
public boolean execute(String action, JSONArray args, CallbackContext callbackContext) throws JSONException {
 
     if ("beep".equals(action)) {
 
     if ("beep".equals(action)) {
سطر 47: سطر 30:
 
     }
 
     }
 
     return false;  // Returning false results in a "MethodNotFound" error.
 
     return false;  // Returning false results in a "MethodNotFound" error.
}‎</syntaxhighlight>  
+
}‎</syntaxhighlight>يتوافق الوسيط <code>exec</code> الخاص بدالة جفف <code>action</code> مع تابع صنف خاص (private class method)، والذي يُرسل مع الوسائط الاختيارية.
 
 
يتوافق الوسيط <code>exec</code> الخاص بدالة جفف <code>action</code> مع تابع صنف خاص (private class method)، والذي يُرسل مع الوسائط الاختيارية.  
 
  
عند إمساك الاستثناءات والأخطاء، من المهم أن تطابق الأخطاء المُعاادة إلى [[JavaScript|JavaScript]] أسماء الاستثناءات في [[Java|جافا]] قدر الممكن.  
+
عند إمساك الاستثناءات والأخطاء، من المهم أن تطابق الأخطاء المُعاادة إلى [[JavaScript]] أسماء الاستثناءات في [[Java|جافا]] قدر الممكن.
 
+
==المهام الفرعية (Threading)==
== المهام الفرعية (Threading) ==  
+
لا تعمل إضافات [[JavaScript]] في المهمة الرئيسية (main thread) لواجهة <code>WebView</code>؛ ولكنها تُجرى في المهمة الفرعية <code>WebCore</code>، مثل التابع <code>execute</code>.  إن كنت بحاجة إلى التفاعل مع واجهة المستخدم، يجب عليك استخدام التابع [http://developer.android.com/reference/android/app/Activity.html#runOnUiThread(java.lang.Runnable) Activity's  runOnUiThread] كما يلي:<syntaxhighlight>@Override
 
 
لا تعمل إضافات [[JavaScript|JavaScript]] في المهمة الرئيسية (main thread) لواجهة <code>WebView</code>؛ ولكنها تُجرى في المهمة الفرعية <code>WebCore</code>، مثل التابع <code>execute</code>.  إن كنت بحاجة إلى التفاعل مع واجهة المستخدم، يجب عليك استخدام التابع [http://developer.android.com/reference/android/app/Activity.html#runOnUiThread(java.lang.Runnable) Activity's  runOnUiThread] كما يلي:  
 
<syntaxhighlight>@Override
 
 
public boolean execute(String action, JSONArray args, final CallbackContext callbackContext) throws JSONException {
 
public boolean execute(String action, JSONArray args, final CallbackContext callbackContext) throws JSONException {
 
     if ("beep".equals(action)) {
 
     if ("beep".equals(action)) {
سطر 69: سطر 47:
 
     }
 
     }
 
     return false;
 
     return false;
}‎</syntaxhighlight>  
+
}‎</syntaxhighlight>إذا لم تكن بحاجة إلى التفاعل مع المهمة الفرعية الخاصة بواجهة المستخدم، ولكنك لا ترغب في إعاقة (block) المهمة الفرعية <code>WebCore</code>، فعليك تنفيذ الأكواد البرمجية باستخدام [http://developer.android.com/reference/java/util/concurrent/ExecutorService.html ExecutorService] الناتجة عن <code>cordova.getThreadPool()</code> على النحو التالي:<syntaxhighlight>@Override
 
 
إذا لم تكن بحاجة إلى التفاعل مع المهمة الفرعية الخاصة بواجهة المستخدم، ولكنك لا ترغب في إعاقة (block) المهمة الفرعية <code>WebCore</code>، فعليك تنفيذ الأكواد البرمجية باستخدام [http://developer.android.com/reference/java/util/concurrent/ExecutorService.html ExecutorService] الناتجة عن <code>cordova.getThreadPool()</code> على النحو التالي:  
 
<syntaxhighlight>@Override
 
 
public boolean execute(String action, JSONArray args, final CallbackContext callbackContext) throws JSONException {
 
public boolean execute(String action, JSONArray args, final CallbackContext callbackContext) throws JSONException {
 
     if ("beep".equals(action)) {
 
     if ("beep".equals(action)) {
سطر 85: سطر 60:
 
     }
 
     }
 
     return false;
 
     return false;
}‎</syntaxhighlight>  
+
}‎</syntaxhighlight>
 +
==إضافة مكتبات الارتباط (Adding Dependency Libraries)==
 +
إن كان لإضافة أندرويد خاصتك ارتباطات إضافية، فيجب إدراجها في ملف <code>plugin.xml</code>، وهناك طريقتان لفعل ذلك.
  
== إضافة مكتبات الارتباط (Adding Dependency Libraries) ==
+
الطريقة المثلى هي استخدام وسم <code><framework /></code> (انظر صفحة [../../../plugin_ref/spec.html#framework  Plugin Specification] لمزيد من التفاصيل). يسمح تحديد المكتبات بهذه الطريقة بحلها عبر آلية [https://docs.gradle.org/current/userguide/dependency_management.html Dependency Management logic] الخاصة بأداة Gradle. يسمح ذلك باستخدام المكتبات الشائعة مثل: gson و android-support-v4 و google-play-services بواسطة عدة إضافات دون تعارض.
  
إن كان لإضافة أندرويد خاصتك ارتباطات إضافية، فيجب إدراجها في ملف <code>plugin.xml</code>، وهناك طريقتان لفعل ذلك.
+
الخيار الثاني هو استخدام وسم <code><lib-file /></code> لتحديد موضع ملف مضغوط jar (انظر صفحة [../../../plugin_ref/spec.html#lib-file  Plugin Specification] لمزيد من التفاصيل). لا تستخدم هذه الطريقة إلا إن كنت على يقين من عدم اعتماد إضافة أخرى على تلك المكتبة على سبيل المثال إن كانت المكتبة مخصوصة بالإضافة الخاصة بك). وإلا فإنك تخاطر بالتسبب في إطلاق أخطاء بنائية لمستخدمي الإضافة في حال أضافت إضافة أخرى المكتبة نفسها. تجدر الإشارة إلى أن مطوري تطبيقات Cordova ليسوا بالضرورة مطورين أصليين (أي لا يطورون بالضروة في اللغات الأصلية، مثل [[Java|جافا]])، لذا فإنّ أخطاء البناء الخاصة بالمنصة النظام الأصلية قد تكون مصدر إحباط لهم.
 
+
==مثال لإضافة أندرويد==
الطريقة المثلى هي استخدام وسم <code><framework /></code> (انظر صفحة [../../../plugin_ref/spec.html#framework  Plugin Specification] لمزيد من التفاصيل). يسمح تحديد المكتبات بهذه الطريقة بحلها عبر آلية [https://docs.gradle.org/current/userguide/dependency_management.html  Dependency Management logic] الخاصة بأداة Gradle. يسمح ذلك باستخدام المكتبات الشائعة مثل: gson و android-support-v4 و google-play-services بواسطة عدة إضافات دون تعارض.
+
لمطابقة ميزة [[JavaScript|جافااسكريبت]] echo المقدمة في واجهة في صفحة الإضافات، استخدم الملف <code>plugin.xml</code> لإدراج مواصفات <code>feature</code> في ملف <code>config.xml</code> الخاص بالمنصة المحلية:<syntaxhighlight><platform name="android">
 
 
الخيار الثاني هو استخدام وسم <code><lib-file /></code> لتحديد موضع ملف مضغوط jar (انظر صفحة [../../../plugin_ref/spec.html#lib-file  Plugin Specification] لمزيد من التفاصيل). لا تستخدم هذه الطريقة إلا إن كنت على يقين من عدم اعتماد إضافة أخرى على تلك المكتبة على سبيل المثال إن كانت المكتبة مخصوصة بالإضافة الخاصة بك). وإلا فإنك تخاطر بالتسبب في إطلاق أخطاء بنائية لمستخدمي الإضافة في حال أضافت إضافة أخرى المكتبة نفسها. تجدر الإشارة إلى أن مطوري تطبيقات Cordova ليسوا بالضرورة مطورين أصليين (أي لا يطورون بالضروة في اللغات الأصلية، مثل [[Java|جافا]])، لذا فإنّ أخطاء البناء الخاصة بالمنصة النظام الأصلية قد تكون مصدر إحباط لهم.  
 
 
 
== مثال لإضافة أندرويد ==  
 
 
 
لمطابقة ميزة [[JavaScript|[[Java|جافا]]اسكريبت]] echo المقدمة في واجهة في صفحة الإضافات، استخدم الملف <code>plugin.xml</code> لإدراج مواصفات <code>feature</code> في ملف <code>config.xml</code> الخاص بالمنصة المحلية:  
 
<syntaxhighlight><platform name="android">
 
 
     <config-file target="config.xml" parent="/*">
 
     <config-file target="config.xml" parent="/*">
 
         <feature name="Echo">
 
         <feature name="Echo">
سطر 105: سطر 75:
 
     </config-file>
 
     </config-file>
 
     <source-file src="src/android/Echo.java" target-dir="src/org/apache/cordova/plugin" />
 
     <source-file src="src/android/Echo.java" target-dir="src/org/apache/cordova/plugin" />
</platform>‎</syntaxhighlight>  
+
</platform>‎</syntaxhighlight>ثم أضف ما يلي إلى ملف <code>src/android/Echo.java</code>:<syntaxhighlight>package org.apache.cordova.plugin;
 
 
ثم أضف ما يلي إلى ملف <code>src/android/Echo.java</code>:  
 
<syntaxhighlight>package org.apache.cordova.plugin;
 
 
import org.apache.cordova.CordovaPlugin;
 
import org.apache.cordova.CordovaPlugin;
 
import org.apache.cordova.CallbackContext;
 
import org.apache.cordova.CallbackContext;
سطر 134: سطر 101:
 
     }
 
     }
 
}
 
}
}‎</syntaxhighlight>  
+
}‎</syntaxhighlight>توسع عمليات الاستيراد (import) الموجودة في أعلى الملف الصنف من <code>CordovaPlugin</code>، والذي يُعاد تعريف تابعه <code>execute()</code> لتلقي الرسائل من <code>exec()</code>.  يختبر التابع <code>execute()</code> في البداية قيمة <code>action</code>، والتي ليس لها في هذه الحالة سوى قيمة <code>echo</code> واحدة صالحة.  أي إجراء (action) آخر سيُعيد <code>false</code> ويطلق خطأ <code>INVALID_ACTION</code>، والذي يُحوّل إلى دالة الخطأ المستدعاة من جانب [[JavaScript]].
 
 
توسع عمليات الاستيراد (import) الموجودة في أعلى الملف الصنف من <code>CordovaPlugin</code>، والذي يُعاد تعريف تابعه <code>execute()</code> لتلقي الرسائل من <code>exec()</code>.  يختبر التابع <code>execute()</code> في البداية قيمة <code>action</code>، والتي ليس لها في هذه الحالة سوى قيمة <code>echo</code> واحدة صالحة.  أي إجراء (action) آخر سيُعيد <code>false</code> ويطلق خطأ <code>INVALID_ACTION</code>، والذي يُحوّل إلى دالة الخطأ المستدعاة من جانب [[JavaScript|JavaScript]].  
 
  
بعد ذلك، يسترد التابع سلسلة echo باستخدام التابع <code>args</code> الخاص بالكائن المعطى <code>getString</code>، لتحديد الوسيط الأول الممرر إلى التابع.  بعد تمرير القيمة إلى التابع الخاص <code>echo</code>، يتم فحص الوسيط للتأكد من أنه لا يساوي <code>null</code> أو سلسلة نصية فارغة، وإلا سيستدعي التابع <code>callbackContext.error()</code> دالة الخطأ (error callback) في [[JavaScript|JavaScript]].  إذا مرت التحقيقات بنجاح، يمرر <code>callbackContext.success()</code> السلسلة النصية الأصلية <code>message</code> مرة أخرى إلى دالة النجاح (success callback) في [[JavaScript|JavaScript]] كوسيط.  
+
بعد ذلك، يسترد التابع سلسلة echo باستخدام التابع <code>args</code> الخاص بالكائن المعطى <code>getString</code>، لتحديد الوسيط الأول الممرر إلى التابع.  بعد تمرير القيمة إلى التابع الخاص <code>echo</code>، يتم فحص الوسيط للتأكد من أنه لا يساوي <code>null</code> أو سلسلة نصية فارغة، وإلا سيستدعي التابع <code>callbackContext.error()</code> دالة الخطأ (error callback) في [[JavaScript]].  إذا مرت التحقيقات بنجاح، يمرر <code>callbackContext.success()</code> السلسلة النصية الأصلية <code>message</code> مرة أخرى إلى دالة النجاح (success callback) في [[JavaScript]] كوسيط.
 +
==تكامل أندرويد (Android Integration)==
 +
يوفر أندرويد نظام مقاصد ([http://developer.android.com/reference/android/content/Intent.html Intent]) يسمح للعمليات (processes) بالتواصل مع بعضها البعض.  يمكن للإضافات الوصول إلى كائنات <code>CordovaInterface</code>، والتي لديها القدرة على الوصول إلى [[Cordova/Activity|نشاط]] ([http://developer.android.com/reference/android/app/Activity.html Activity] ) أندرويد الذي بُشغّل التطبيق.  هذا هو السياق ([http://developer.android.com/reference/android/content/Context.html Context]) المطلوب لإطلاق مقصد أندرويد جديد.  يتيح [http://developer.android.com/reference/android/content/Intent.html Intent] للإضافات بدء تشغيل [[Cordova/Activity|نشاط]] (<code>CordovaInterface</code>) للحصول على نتيجة، وتعيين استدعاء الإضافة (callback plugin) لاستخدامه عند عودة [http://developer.android.com/reference/android/app/Activity.html Activity] إلى التطبيق.
  
== تكامل أندرويد (Android Integration) ==  
+
اعتبارًا من Cordova 2.0، لم يعد بإمكان الإضافات الوصول إلى [http://developer.android.com/reference/android/content/Intent.html Intent] مباشرةً، وتم إيقاف العضو القديم [http://developer.android.com/reference/android/content/Context.html Context]. كل توابع <code>ctx</code> صارت موجودة الآن في السياق (<code>ctx</code>)، بحيث يمكن لكلا التابعين [http://developer.android.com/reference/android/content/Context.html Context] و <code>getContext()</code> إعادة الكائن المطلوب.
 +
==أذونات أندرويد==
 +
كانت أذونات أندرويد تُعالج حتى وقت قريب وقتَ التثبيت (install-time)، بدلاً من وقت التشغيل (runtime).  من الضروري التصريح بهذه الأذونات في أي تطبيق يستخدم الأذونات، ويجب إضافة هذه الأذونات إلى بيان أندرويد (Android Manifest).  يمكن تحقيق ذلك باستخدام <code>getActivity()</code> لإدارج تلك الأذونات في ملف <code>config.xml</code>. يستخدم المثال التالي إذن جهات الاتصال (Contacts permission). <code>AndroidManifest.xml</code>
 +
===أذونات وقت التشغيل (Cordova-Android 5.0.0 فما فوق)===
 +
قدم إصدار Android 6.0 "Marshmallow" نموذج أذونات جديد حيث يمكن للمستخدم تشغيل وإيقاف الأذونات حسب الضرورة.  وهذا يعني أن التطبيقات يجب أن تتكيف مع هذه التغييرات، وهذا هو محور إصدار Cordova-Android 5.0.0.
  
يوفر أندرويد نظام مقاصد ([http://developer.android.com/reference/android/content/Intent.html  Intent]) يسمح للعمليات (processes) بالتواصل مع بعضها البعض.  يمكن للإضافات الوصول إلى كائنات <code>CordovaInterface</code>، والتي لديها القدرة على الوصول إلى [[Cordova/Activity|نشاط]] ([http://developer.android.com/reference/android/app/Activity.html  Activity] ) أندرويد الذي بُشغّل التطبيق.  هذا هو السياق ([http://developer.android.com/reference/android/content/Context.html  Context]) المطلوب لإطلاق مقصد أندرويد جديد.  يتيح [http://developer.android.com/reference/android/content/Intent.html  Intent] للإضافات بدء تشغيل [[Cordova/Activity|نشاط]] (<code>CordovaInterface</code>) للحصول على نتيجة، وتعيين استدعاء الإضافة (callback plugin) لاستخدامه عند عودة [http://developer.android.com/reference/android/app/Activity.html  Activity] إلى التطبيق.
+
يمكن العثور على الأذونات التي تحتاج إلى المعالجة وقت التشغيل في توثيق مطوري برامج أندرويد<syntaxhighlight><config-file target="AndroidManifest.xml" parent="/*">
 
 
اعتبارًا من Cordova 2.0، لم يعد بإمكان الإضافات الوصول إلى [http://developer.android.com/reference/android/content/Intent.html  Intent] مباشرةً، وتم إيقاف العضو القديم [http://developer.android.com/reference/android/content/Context.html  Context]. كل توابع <code>ctx</code> صارت موجودة الآن في السياق (<code>ctx</code>)، بحيث يمكن لكلا التابعين [http://developer.android.com/reference/android/content/Context.html  Context] و <code>getContext()</code> إعادة الكائن المطلوب.
 
 
 
== أذونات أندرويد ==
 
 
 
كانت أذونات أندرويد تُعالج حتى وقت قريب وقتَ التثبيت (install-time)، بدلاً من وقت التشغيل (runtime).  من الضروري التصريح بهذه الأذونات في أي تطبيق يستخدم الأذونات، ويجب إضافة هذه الأذونات إلى بيان أندرويد (Android Manifest).  يمكن تحقيق ذلك باستخدام <code>getActivity()</code> لإدارج تلك الأذونات في ملف <code>config.xml</code>. يستخدم المثال التالي إذن جهات الاتصال (Contacts permission).
 
<code>AndroidManifest.xml</code>
 
=== أذونات وقت التشغيل (Cordova-Android 5.0.0 فما فوق) ===
 
 
 
قدم إصدار Android 6.0 "Marshmallow" نموذج أذونات جديد حيث يمكن للمستخدم تشغيل وإيقاف الأذونات حسب الضرورة.  وهذا يعني أن التطبيقات يجب أن تتكيف مع هذه التغييرات، وهذا هو محور إصدار Cordova-Android 5.0.0.
 
 
 
يمكن العثور على الأذونات التي تحتاج إلى المعالجة وقت التشغيل في توثيق مطوري برامج أندرويد <syntaxhighlight><config-file target="AndroidManifest.xml" parent="/*">
 
 
     <uses-permission android:name="android.permission.READ_CONTACTS" />
 
     <uses-permission android:name="android.permission.READ_CONTACTS" />
</config-file>‎</syntaxhighlight>.  
+
</config-file>‎</syntaxhighlight>.
 
 
بالنسبة للإضافات، يمكن طلب الإذن عن طريق استدعاء تابع الأذونات ذو الإمضاء التالي:
 
[http://developer.android.com/guide/topics/security/permissions.html#perm-groups  here]
 
 
 
من المعتاد تعيينه إلى متغير محلي ثابت.
 
<syntaxhighlight>cordova.requestPermission(CordovaPlugin plugin, int requestCode, String permission);‎</syntaxhighlight>
 
 
 
من المتعارف أيضًا تعريف كود الطلب requestCode على النحو التالي:
 
<syntaxhighlight>public static final String READ = Manifest.permission.READ_CONTACTS;‎</syntaxhighlight>
 
  
ثم ينبغي التحقق من الإذن في التابع exec، :  
+
بالنسبة للإضافات، يمكن طلب الإذن عن طريق استدعاء تابع الأذونات ذو الإمضاء التالي: [http://developer.android.com/guide/topics/security/permissions.html#perm-groups here]
<syntaxhighlight>public static final int SEARCH_REQ_CODE = 0;‎</syntaxhighlight>
 
  
في هذه الحالة، سنستدعي requestPermission وحسب:  
+
من المعتاد تعيينه إلى متغير محلي ثابت.<syntaxhighlight>cordova.requestPermission(CordovaPlugin plugin, int requestCode, String permission);‎</syntaxhighlight>من المتعارف أيضًا تعريف كود الطلب requestCode على النحو التالي:<syntaxhighlight>public static final String READ = Manifest.permission.READ_CONTACTS;‎</syntaxhighlight>ثم ينبغي التحقق من الإذن في التابع exec، :<syntaxhighlight>public static final int SEARCH_REQ_CODE = 0;‎</syntaxhighlight>في هذه الحالة، سنستدعي requestPermission وحسب:<syntaxhighlight>if(cordova.hasPermission(READ))
<syntaxhighlight>if(cordova.hasPermission(READ))
 
 
{
 
{
 
     search(executeArgs);
 
     search(executeArgs);
سطر 178: سطر 126:
 
{
 
{
 
     getReadPermission(SEARCH_REQ_CODE);
 
     getReadPermission(SEARCH_REQ_CODE);
}‎</syntaxhighlight>  
+
}‎</syntaxhighlight>سيؤدي هذا إلى استدعاء [[Cordova/Activity|النشاط]] (activity) وظهور مِحَثّ (prompt) يطلب الإذن.  بمجرد حصول المستخدم على الإذن، يجب معالجة النتيجة باستخدام التابع<syntaxhighlight>protected void getReadPermission(int requestCode)
 
 
سيؤدي هذا إلى استدعاء [[Cordova/Activity|النشاط]] (activity) وظهور مِحَثّ (prompt) يطلب الإذن.  بمجرد حصول المستخدم على الإذن، يجب معالجة النتيجة باستخدام التابع <syntaxhighlight>protected void getReadPermission(int requestCode)
 
 
{
 
{
 
     cordova.requestPermission(this, requestCode, READ);
 
     cordova.requestPermission(this, requestCode, READ);
}‎</syntaxhighlight>، والذي يجب أن يعاد تعريفه في كل إضافة.  يمكن العثور على مثال على ذلك أدناه:  
+
}‎</syntaxhighlight>، والذي يجب أن يعاد تعريفه في كل إضافة.  يمكن العثور على مثال على ذلك أدناه:
<code>onRequestPermissionResult</code>
 
  
ستعود التعليمة switch أعلاه من المحث وبناءًا على قيمة الوسيط المعطى requestCode قد تستدعي التابع المعني.  تجدر الإشارة إلى أن محثات (switch) الأذونات قد تتعطل في حالة عدم معالجة التنفيذ بشكل صحيح، وأنه يجب تجنب ذلك.
+
<code>onRequestPermissionResult</code>
  
بالإضافة إلى الاستئذان للحصول على إذن واحد، من الممكن أيضًا طلب الأذونات لمجموعة بأكملها، عن طريق تعريف مصفوفة الأذونات، كما هو الحال في الإضافة Geolocation:  
+
ستعود التعليمة switch أعلاه من المحث وبناءًا على قيمة الوسيط المعطى requestCode قد تستدعي التابع المعني.  تجدر الإشارة إلى أن محثات (switch) الأذونات قد تتعطل في حالة عدم معالجة التنفيذ بشكل صحيح، وأنه يجب تجنب ذلك.
<syntaxhighlight>public void onRequestPermissionResult(int requestCode, String[] permissions,
+
 
 +
بالإضافة إلى الاستئذان للحصول على إذن واحد، من الممكن أيضًا طلب الأذونات لمجموعة بأكملها، عن طريق تعريف مصفوفة الأذونات، كما هو الحال في الإضافة Geolocation:<syntaxhighlight>public void onRequestPermissionResult(int requestCode, String[] permissions,
 
                                         int[] grantResults) throws JSONException
 
                                         int[] grantResults) throws JSONException
 
{
 
{
سطر 212: سطر 158:
 
             break;
 
             break;
 
     }
 
     }
}‎</syntaxhighlight>  
+
}‎</syntaxhighlight>يعد ذلك، كل ما عليك القيام به لطلب الإذن، هو ما يلي:<syntaxhighlight>String [] permissions = { Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION };‎</syntaxhighlight>يطلب هذا التعبير الأذونات المحددة في المصفوفة.  من المستحسن إتاحة مصفوفة أذونات بشكل عام، نظرًا لأنه يمكن استخدامها من طرف الإضافات التي تستخدم إضافتك كواحدة من ارتباطاتها، على الرغم من أن هذا ليس ضروريا.
 +
==تصحيح إضافات أندرويد (Debugging Android Plugins)==
 +
يمكن تصحيح أخطاء أندرويد باستخدام Eclipse أو Android Studio، على الرغم من أنه يُوصى باستخدام Android Studio.  نظرًا لاستخدام Cordova-Android حاليًا كمكتبة، وأن الإضافات تُدعم كشيفرة مصدرية، فمن الممكن تصحيح شيفرة [[Java|جافا]] داخل تطبيقات Cordova تمامًا مثل تطبيقات أندرويد الأصلية.
 +
==إطلاق أنشطة أخرى==
 +
هناك بعض الإجراءات الخاصة التي يجب اتخاذها إن كانت الإضافة ستطلق [[Cordova/Activity|نشاط]]ًا (Activity) يدفع Cordova<syntaxhighlight>cordova.requestPermissions(this, 0, permissions);‎</syntaxhighlight>إلى الخلفية. سينهي نظام التشغيل أندرويد الأنشطة الموجودة في الخلفية عندما تنحسر الذاكرة المتاحة في الجهاز. في هذه الحالة، ستُنهى نسخة (instance‏) [http://developer.android.com/reference/android/app/Activity.html Activity] كذلك. إن كانت الإضافةة تنتظر نتيجة من [[Cordova/Activity|النشاط]] (<code>CordovaPlugin</code>) الذي أطلقته، فسيتم إنشاء نسخة جديدة من الإضافة عند إعادة Cordova [http://developer.android.com/reference/android/app/Activity.html Activity] إلى الواجهة، وعند الحصول على النتيجة. لكن لن تُحفظ حالة الإضافة أو تستعاد تلقائيًا، وسيُفقد السياق [http://developer.android.com/reference/android/app/Activity.html Activity] الخاص بالإضافة. هناك تابعان يمكن أن يعرفها <code>CallbackContext</code> للتعامل مع هذه المسألة:
  
يعد ذلك، كل ما عليك القيام به لطلب الإذن، هو ما يلي:
+
<code>CordovaPlugin</code>
<syntaxhighlight>String [] permissions = { Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION };‎</syntaxhighlight>
 
 
 
يطلب هذا التعبير الأذونات المحددة في المصفوفة.  من المستحسن إتاحة مصفوفة أذونات بشكل عام، نظرًا لأنه يمكن استخدامها من طرف الإضافات التي تستخدم إضافتك كواحدة من ارتباطاتها، على الرغم من أن هذا ليس ضروريا.
 
 
 
== تصحيح إضافات أندرويد (Debugging Android Plugins) ==
 
 
 
يمكن تصحيح أخطاء أندرويد باستخدام Eclipse أو Android Studio، على الرغم من أنه يُوصى باستخدام Android Studio.  نظرًا لاستخدام Cordova-Android حاليًا كمكتبة، وأن الإضافات تُدعم كشيفرة مصدرية، فمن الممكن تصحيح شيفرة [[Java|جافا]] داخل تطبيقات Cordova تمامًا مثل تطبيقات أندرويد الأصلية.
 
 
 
== إطلاق أنشطة أخرى ==
 
 
 
هناك بعض الإجراءات الخاصة التي يجب اتخاذها إن كانت الإضافة ستطلق [[Cordova/Activity|نشاط]]ًا (Activity) يدفع Cordova <syntaxhighlight>cordova.requestPermissions(this, 0, permissions);‎</syntaxhighlight> إلى الخلفية. سينهي نظام التشغيل أندرويد الأنشطة الموجودة في الخلفية عندما تنحسر الذاكرة المتاحة في الجهاز. في هذه الحالة، ستُنهى نسخة (instance‏) [http://developer.android.com/reference/android/app/Activity.html  Activity] كذلك. إن كانت الإضافةة تنتظر نتيجة من [[Cordova/Activity|النشاط]] (<code>CordovaPlugin</code>) الذي أطلقته، فسيتم إنشاء نسخة جديدة من الإضافة عند إعادة Cordova [http://developer.android.com/reference/android/app/Activity.html  Activity] إلى الواجهة، وعند الحصول على النتيجة. لكن لن تُحفظ حالة الإضافة أو تستعاد تلقائيًا، وسيُفقد السياق [http://developer.android.com/reference/android/app/Activity.html  Activity] الخاص بالإضافة. هناك تابعان يمكن أن يعرفها <code>CallbackContext</code> للتعامل مع هذه المسألة:
 
<code>CordovaPlugin</code>  
 
  
 
من المهم ملاحظة أنه يجب لا ينبغي استخدام التابعين المذكورين أعلاه إلا إن كانت الإضافة ستطلق [[Cordova/Activity|نشاط]]ًا (<syntaxhighlight>/**
 
من المهم ملاحظة أنه يجب لا ينبغي استخدام التابعين المذكورين أعلاه إلا إن كانت الإضافة ستطلق [[Cordova/Activity|نشاط]]ًا (<syntaxhighlight>/**
سطر 248: سطر 186:
 
  * @param callbackContext  Replacement Context to return the plugin result to
 
  * @param callbackContext  Replacement Context to return the plugin result to
 
  */
 
  */
public void onRestoreStateForActivityResult(Bundle state, CallbackContext callbackContext) {}‎</syntaxhighlight>) تتوخى نتيجة منه، ويجب ألأ تستعيد إلا الحالة اللازمة لمعالجة نتيجة [[Cordova/Activity|النشاط]]. لن تتم استعادة حالة الإضافة إلا في حالة الحصول على نتيجة من [[Cordova/Activity|النشاط]] طلبتها الإضافة باستخدام التابع [http://developer.android.com/reference/android/app/Activity.html Activity] الخاص بالكائن <code>CordovaInterface</code> وكان نظام التشغيل قد أنهى [[Cordova/Activity|نشاط]] كوردوفا أثناء وجوده في الخلفية.  
+
public void onRestoreStateForActivityResult(Bundle state, CallbackContext callbackContext) {}‎</syntaxhighlight>) تتوخى نتيجة منه، ويجب ألأ تستعيد إلا الحالة اللازمة لمعالجة نتيجة [[Cordova/Activity|النشاط]]. لن تتم استعادة حالة الإضافة إلا في حالة الحصول على نتيجة من [[Cordova/Activity|النشاط]] طلبتها الإضافة باستخدام التابع [http://developer.android.com/reference/android/app/Activity.html Activity] الخاص بالكائن <code>CordovaInterface</code> وكان نظام التشغيل قد أنهى [[Cordova/Activity|نشاط]] كوردوفا أثناء وجوده في الخلفية.
  
كجزء من <code>startActivityForResult()</code>، سيُمرّر إلى الإضافة سياق بديل CallbackContext. من المهم أن تدرك أن السياق CallbackContext ليس هو نفسه الذي تم إنهاؤه مع [[Cordova/Activity|النشاط]]. إذ يُفقد الاستدعاء الأصلي (original callback)، ولن يتم تشغيلها في تطبيق [[JavaScript|JavaScript]]. بدلاً من ذلك، سيُعيد <code>onRestoreStateForActivityResult()</code> النتيجة كجزء من الحدث <code>CallbackContext</code> الذي يتم تفعيله عند استئناف تشغيل التطبيق. الحدث [../../../cordova/events/events.html#resume  resume] يتبغ البنية التالية:  
+
كجزء من <code>startActivityForResult()</code>، سيُمرّر إلى الإضافة سياق بديل CallbackContext. من المهم أن تدرك أن السياق CallbackContext ليس هو نفسه الذي تم إنهاؤه مع [[Cordova/Activity|النشاط]]. إذ يُفقد الاستدعاء الأصلي (original callback)، ولن يتم تشغيلها في تطبيق [[JavaScript]]. بدلاً من ذلك، سيُعيد <code>onRestoreStateForActivityResult()</code> النتيجة كجزء من الحدث <code>CallbackContext</code> الذي يتم تفعيله عند استئناف تشغيل التطبيق. الحدث [../../../cordova/events/events.html#resume  resume] يتبغ البنية التالية: [../../../cordova/events/events.html#resume  resume]
[../../../cordova/events/events.html#resume  resume]  
+
*سيطابق<syntaxhighlight>{
* سيطابق <syntaxhighlight>{
 
 
     action: "resume",
 
     action: "resume",
 
     pendingResult: {
 
     pendingResult: {
سطر 259: سطر 196:
 
         result: any
 
         result: any
 
     }
 
     }
}‎</syntaxhighlight> العنصر <code>pluginServiceName</code> من ملف plugin.xml.  
+
}‎</syntaxhighlight>العنصر <code>pluginServiceName</code> من ملف plugin.xml.
* [../../../plugin_ref/spec.html#name  name element] سيكون سلسلة نصية تصف حالة ناتج الإضافة (PluginResult) المٌمررة إلى CallbackContext. راجع صفحة PluginResult.java للتعرف على قيم السلسلة النصية الموافقة لحالات الإضافات  
+
*[../../../plugin_ref/spec.html#name  name element] سيكون سلسلة نصية تصف حالة ناتج الإضافة (PluginResult) المٌمررة إلى CallbackContext. راجع صفحة PluginResult.java للتعرف على قيم السلسلة النصية الموافقة لحالات الإضافات
* <code>pluginStatus</code> سيساوي النتيجة التي مررتها الإضافة إلى CallbackContext (سلسلة نصية، عدد، كائن JSON، إلخ.)  
+
*<code>pluginStatus</code> سيساوي النتيجة التي مررتها الإضافة إلى CallbackContext (سلسلة نصية، عدد، كائن JSON، إلخ.)
 
+
سيتم تمرير محتوى <code>result</code> إلى لك عمليات الاستدعاء (callbacks) التي سجلتها تطبيقات [[JavaScript]] مع الحدث [../../../cordova/events/events.html#resume  resume]. هذا يعني أن النتيجة ستذهب مباشرة إلى تطبيق Cordova؛ لن يكون للإضافة خاصتك فرصة لمعالجة النتيجة ب[[JavaScript|جافااسكريبت]] قبل أن يتسلمها التطبيق. وبالتالي، يجب أن تسعى إلى جعل النتيجة المعادة من الشيفرة الأصلية كاملة قدر الإمكان، وألا تعتمد على دوال [[JavaScript|جافااسكريبت]] عند إطلاق [[Cordova/Activity|الأنشطة]].
سيتم تمرير محتوى <code>result</code> إلى لك عمليات الاستدعاء (callbacks) التي سجلتها تطبيقات [[JavaScript|JavaScript]] مع الحدث [../../../cordova/events/events.html#resume  resume]. هذا يعني أن النتيجة ستذهب مباشرة إلى تطبيق Cordova؛ لن يكون للإضافة خاصتك فرصة لمعالجة النتيجة ب[[JavaScript|[[Java|جافا]]اسكريبت]] قبل أن يتسلمها التطبيق. وبالتالي، يجب أن تسعى إلى جعل النتيجة المعادة من الشيفرة الأصلية كاملة قدر الإمكان، وألا تعتمد على دوال [[JavaScript|[[Java|جافا]]اسكريبت]] عند إطلاق [[Cordova/Activity|الأنشطة]].
 
 
 
تأكد من توضيح كيفية تفسير تطبيق Cordova للنتيجة التي يتلقاها عند الحدث [../../../cordova/events/events.html#resume  resume]. يُناط بتطبيقات Cordova حفظ حالاتها، وتذكر الطلبيات التي أرسلتها، والوسائط التي مررتها إذا لزم الأمر. ومع ذلك، يجب عليك توضيح معاني قيم [../../../cordova/events/events.html#resume  resume] ونوع البيانات التي ستعاد في الحقل <code>pluginStatus</code> كجزء من الواجهة البرمجية للإضافة.  
 
  
هذه سلسلة الأحداث الكاملة لبدء [[Cordova/Activity|نشاط]] معين:
+
تأكد من توضيح كيفية تفسير تطبيق Cordova للنتيجة التي يتلقاها عند الحدث [../../../cordova/events/events.html#resume  resume]. يُناط بتطبيقات Cordova حفظ حالاتها، وتذكر الطلبيات التي أرسلتها، والوسائط التي مررتها إذا لزم الأمر. ومع ذلك، يجب عليك توضيح معاني قيم [../../../cordova/events/events.html#resume  resume] ونوع البيانات التي ستعاد في الحقل <code>pluginStatus</code> كجزء من الواجهة البرمجية للإضافة.
* تطبيق Cordova يستدعي الإضافة خاصتك
 
* تطلق الإضافة [[Cordova/Activity|نشاط]]ًا قصد الحصول على نتيجة
 
* ينهي نظام التشغيل أندرويد كلًا من [[Cordova/Activity|النشاط]] ونسخة (instance) الإضافة
 
* يستدعى الحدث onSaveInstanceState()‎
 
* يتفاعل المستخدم مع [[Cordova/Activity|نشاط]]ك، ثم ينتهي [[Cordova/Activity|النشاط]]
 
* يعاد إنشاء [[Cordova/Activity|نشاط]] Cordova، ويتم تلقي نتيجة [[Cordova/Activity|النشاط]]
 
* يستدعى الحدث onRestoreStateForActivityResult()‎
 
* يتم استدعاء [../../../cordova/events/events.html#resume  resume] ثم تمرر الإضافة نتيجة إلى السياق CallbackContext الجديد
 
* يُفعّل الحدث <code>onActivityResult()</code> ويُتلقى من قبل تطبيق Cordova
 
  
 +
هذه سلسلة الأحداث الكاملة لبدء [[Cordova/Activity|نشاط]] معين:
 +
*تطبيق Cordova يستدعي الإضافة خاصتك
 +
*تطلق الإضافة [[Cordova/Activity|نشاط]]ًا قصد الحصول على نتيجة
 +
*ينهي نظام التشغيل أندرويد كلًا من [[Cordova/Activity|النشاط]] ونسخة (instance) الإضافة
 +
*يستدعى الحدث onSaveInstanceState()‎
 +
*يتفاعل المستخدم مع [[Cordova/Activity|نشاط]]ك، ثم ينتهي [[Cordova/Activity|النشاط]]
 +
*يعاد إنشاء [[Cordova/Activity|نشاط]] Cordova، ويتم تلقي نتيجة [[Cordova/Activity|النشاط]]
 +
*يستدعى الحدث onRestoreStateForActivityResult()‎
 +
*يتم استدعاء [../../../cordova/events/events.html#resume  resume] ثم تمرر الإضافة نتيجة إلى السياق CallbackContext الجديد
 +
*يُفعّل الحدث <code>onActivityResult()</code> ويُتلقى من قبل تطبيق Cordova
 
يوفر أندرويد إعدادات للمطوِّرين لتصحيح أخطاء إنهاء [[Cordova/Activity|الأنشطة]] عندما يكون هناك انحسار في مساحة الذاكرة. قم بتمكين الإعداد "Don't keep activities" في قائمة خيارات المطور على جهازك أو المحاكي لمحاكاة سيناريوهات انحسار الذاكرة. إن كانت الإضافة تُطلق [[Cordova/Activity|نشاطات]] خارجية، فيجب عليك إجراء بعض الاختبارات مع تمكين هذا الإعداد لضمان التعامل بشكل صحيح مع سيناريوهات انحسار الذاكرة.
 
يوفر أندرويد إعدادات للمطوِّرين لتصحيح أخطاء إنهاء [[Cordova/Activity|الأنشطة]] عندما يكون هناك انحسار في مساحة الذاكرة. قم بتمكين الإعداد "Don't keep activities" في قائمة خيارات المطور على جهازك أو المحاكي لمحاكاة سيناريوهات انحسار الذاكرة. إن كانت الإضافة تُطلق [[Cordova/Activity|نشاطات]] خارجية، فيجب عليك إجراء بعض الاختبارات مع تمكين هذا الإعداد لضمان التعامل بشكل صحيح مع سيناريوهات انحسار الذاكرة.
 
==مصادر==
 
==مصادر==
 
*[https://cordova.apache.org/docs/en/latest/guide/platforms/android/plugin.html قسم إضافات أندرويد في التوثيق الرسمي لكوردوفا.]
 
*[https://cordova.apache.org/docs/en/latest/guide/platforms/android/plugin.html قسم إضافات أندرويد في التوثيق الرسمي لكوردوفا.]

مراجعة 22:15، 21 نوفمبر 2018

يقدم يوضح هذا القسم كيفية تقديم (implement) أكواد الإضافات الأصلية (native plugin code) على منصة أندرويد.

قبل قراءة هذه الصفحة، راجع صفحة دليل تطوير الإضافات لتكوين نظرة عامة على بنية الإضافات وواجهات JavaScript الخاصة بها. يواصل هذا القسم تطوير مثال الإضافة echo الوارد في دليل تطوير الإضافات، والذي يربط الاتصال من المعرض webview الخاص بكوردوفا إلى المنصة الأصلية (native platform)، والعكس بالعكس. للحصول على مثال آخر، راجع التعليقات في CordovaPlugin.java.

تستند إضافات أندرويد على منصة Cordova-Android، التي تُنشؤُ من المعرض Android WebView إضافة إلى جسر أصلي (native bridge). يتألف الجزء الأصلي من إضافات أندرويد من صنف جافا واحد على الأقل، والذي يوسع الصنف CordovaPlugin ويعيد تعريف أحد توابعه execute.

إعداد صنف الإضافة (Plugin Class Mapping)

تستخدم واجهة JavaScript الخاصة بالإضافة التابع cordova.exec على النحو التالي:

exec(<successFunction>, <failFunction>, <service>, <action>, [<args>]);

هذه الشيفرة ترسل طلبية (request) من webview إلى الجانب الأصلي (native side) لأندرويد، وتستدعي التابع action فعليا على الصنف service، مع وسائط إضافية تُمرر في المصفوفة args. سواء أقمت بتوزيع الإضافة على هيئة ملف جافا، أو كملف مضغوط jar، يجب تحديد الإضافة في ملف res/xml/config.xml الخاص بتطبيق Cordova-Android. انظر صفحة الإضافات لمزيد من المعلومات حول كيفية استخدام ملف plugin.xml لإدارج العنصر feature:

<feature name="<service_name>">
    <param name="android-package" value="<full_name_including_namespace>" />
</feature>

يتطابق service_name مع الاسم المستخدم عند استدعاء دالة الجافا exec . قيمته تساوي الاسم الكامل المؤهل لصنف جافا (ava class's fully qualified namespace identifier). بخلاف ذلك، يمكن أن تُصرّف (compiled) الإضافة لكنها لن تكون متاحة لكوردوفا.

تهيئة الإضافات ودورة الحياة (Plugin Initialization and Lifetime)

يتم إنشاء نسخة (instance) واحدة من كائن الإضافة خلال دورة حياة المعرض WebView. لا يتم إنشاء نسخ للإضافات حتى تتم الإشارة إليها أولاً عبر استدعاء من جافا، إلا إذا في حال إضافة وسم <param> مع خاصية onload مساوية للقيمة name، إلى "true" في config.xml. فمثلا،

<feature name="Echo">
    <param name="android-package" value="<full_name_including_namespace>" />
    <param name="onload" value="true" />
</feature>‎

يجب أن تستخدم الإضافات التابع initialize في مرحلة الانطلاق.

@Override
public void initialize(CordovaInterface cordova, CordovaWebView webView) {
    super.initialize(cordova, webView);
    // your init code here
}‎

يمكن للإضافات الوصول إلى أحداث دورة حياة أندرويد، ويمكنها التعامل معها من خلال توسيع إحدى التوابع (onResume، onDestroy، إلخ). الإضافات ذات الطلبيات الطويلة (long-running requests)، أو النشاطات الخلفية، مثل قارئات الوسائط، أو المستمعات (listeners)، أو ذات الحالة الداخلية (internal state) يجب أن تقوم بتقديم التابع onReset(). يتم تنفيذها عند انتقال WebView إلى صفحة جديدة، أو عند تحديث الصفحة، وهو ما يؤدي إلى إعادة تحميل جافا.

كتابة إضافة جافاا لأندرويد

يرسل استدعاء من JavaScript طلبية إضافة (plugin request) إلى الجانب الأصلي (native side)، ويتم تعيين ملف جافا المقابل الخاص بالإضافة في ملف config.xml، ولكن كيف سيبدو الشكل النهائي لصنف أندرويد جافا الخاص بالإضافة؟ أي شيء يتم إرساله إلى الإضافة عبر دالة جافااسكريبت exec سيُمرّر إلى تابع الصنف execute الخاص بالإضافة. معظم تطبيقات execute تبدو كالتالي:

@Override
public boolean execute(String action, JSONArray args, CallbackContext callbackContext) throws JSONException {
    if ("beep".equals(action)) {
        this.beep(args.getLong(0));
        callbackContext.success();
        return true;
    }
    return false;  // Returning false results in a "MethodNotFound" error.
}‎

يتوافق الوسيط exec الخاص بدالة جفف action مع تابع صنف خاص (private class method)، والذي يُرسل مع الوسائط الاختيارية.

عند إمساك الاستثناءات والأخطاء، من المهم أن تطابق الأخطاء المُعاادة إلى JavaScript أسماء الاستثناءات في جافا قدر الممكن.

المهام الفرعية (Threading)

لا تعمل إضافات JavaScript في المهمة الرئيسية (main thread) لواجهة WebView؛ ولكنها تُجرى في المهمة الفرعية WebCore، مثل التابع execute. إن كنت بحاجة إلى التفاعل مع واجهة المستخدم، يجب عليك استخدام التابع Activity's runOnUiThread كما يلي:

@Override
public boolean execute(String action, JSONArray args, final CallbackContext callbackContext) throws JSONException {
    if ("beep".equals(action)) {
        final long duration = args.getLong(0);
        cordova.getActivity().runOnUiThread(new Runnable() {
            public void run() {
                ...
                callbackContext.success(); // Thread-safe.
            }
        });
        return true;
    }
    return false;
}‎

إذا لم تكن بحاجة إلى التفاعل مع المهمة الفرعية الخاصة بواجهة المستخدم، ولكنك لا ترغب في إعاقة (block) المهمة الفرعية WebCore، فعليك تنفيذ الأكواد البرمجية باستخدام ExecutorService الناتجة عن cordova.getThreadPool() على النحو التالي:

@Override
public boolean execute(String action, JSONArray args, final CallbackContext callbackContext) throws JSONException {
    if ("beep".equals(action)) {
        final long duration = args.getLong(0);
        cordova.getThreadPool().execute(new Runnable() {
            public void run() {
                ...
                callbackContext.success(); // Thread-safe.
            }
        });
        return true;
    }
    return false;
}‎

إضافة مكتبات الارتباط (Adding Dependency Libraries)

إن كان لإضافة أندرويد خاصتك ارتباطات إضافية، فيجب إدراجها في ملف plugin.xml، وهناك طريقتان لفعل ذلك.

الطريقة المثلى هي استخدام وسم <framework /> (انظر صفحة [../../../plugin_ref/spec.html#framework Plugin Specification] لمزيد من التفاصيل). يسمح تحديد المكتبات بهذه الطريقة بحلها عبر آلية Dependency Management logic الخاصة بأداة Gradle. يسمح ذلك باستخدام المكتبات الشائعة مثل: gson و android-support-v4 و google-play-services بواسطة عدة إضافات دون تعارض.

الخيار الثاني هو استخدام وسم <lib-file /> لتحديد موضع ملف مضغوط jar (انظر صفحة [../../../plugin_ref/spec.html#lib-file Plugin Specification] لمزيد من التفاصيل). لا تستخدم هذه الطريقة إلا إن كنت على يقين من عدم اعتماد إضافة أخرى على تلك المكتبة على سبيل المثال إن كانت المكتبة مخصوصة بالإضافة الخاصة بك). وإلا فإنك تخاطر بالتسبب في إطلاق أخطاء بنائية لمستخدمي الإضافة في حال أضافت إضافة أخرى المكتبة نفسها. تجدر الإشارة إلى أن مطوري تطبيقات Cordova ليسوا بالضرورة مطورين أصليين (أي لا يطورون بالضروة في اللغات الأصلية، مثل جافا)، لذا فإنّ أخطاء البناء الخاصة بالمنصة النظام الأصلية قد تكون مصدر إحباط لهم.

مثال لإضافة أندرويد

لمطابقة ميزة جافااسكريبت echo المقدمة في واجهة في صفحة الإضافات، استخدم الملف plugin.xml لإدراج مواصفات feature في ملف config.xml الخاص بالمنصة المحلية:

<platform name="android">
    <config-file target="config.xml" parent="/*">
        <feature name="Echo">
            <param name="android-package" value="org.apache.cordova.plugin.Echo"/>
        </feature>
    </config-file>
    <source-file src="src/android/Echo.java" target-dir="src/org/apache/cordova/plugin" />
</platform>‎

ثم أضف ما يلي إلى ملف src/android/Echo.java:

package org.apache.cordova.plugin;
import org.apache.cordova.CordovaPlugin;
import org.apache.cordova.CallbackContext;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
/**
* This class echoes a string called from JavaScript.
*/
public class Echo extends CordovaPlugin {
@Override
public boolean execute(String action, JSONArray args, CallbackContext callbackContext) throws JSONException {
    if (action.equals("echo")) {
        String message = args.getString(0);
        this.echo(message, callbackContext);
        return true;
    }
    return false;
}
private void echo(String message, CallbackContext callbackContext) {
    if (message != null && message.length() > 0) {
        callbackContext.success(message);
    } else {
        callbackContext.error("Expected one non-empty string argument.");
    }
}
}‎

توسع عمليات الاستيراد (import) الموجودة في أعلى الملف الصنف من CordovaPlugin، والذي يُعاد تعريف تابعه execute() لتلقي الرسائل من exec(). يختبر التابع execute() في البداية قيمة action، والتي ليس لها في هذه الحالة سوى قيمة echo واحدة صالحة. أي إجراء (action) آخر سيُعيد false ويطلق خطأ INVALID_ACTION، والذي يُحوّل إلى دالة الخطأ المستدعاة من جانب JavaScript.

بعد ذلك، يسترد التابع سلسلة echo باستخدام التابع args الخاص بالكائن المعطى getString، لتحديد الوسيط الأول الممرر إلى التابع. بعد تمرير القيمة إلى التابع الخاص echo، يتم فحص الوسيط للتأكد من أنه لا يساوي null أو سلسلة نصية فارغة، وإلا سيستدعي التابع callbackContext.error() دالة الخطأ (error callback) في JavaScript. إذا مرت التحقيقات بنجاح، يمرر callbackContext.success() السلسلة النصية الأصلية message مرة أخرى إلى دالة النجاح (success callback) في JavaScript كوسيط.

تكامل أندرويد (Android Integration)

يوفر أندرويد نظام مقاصد (Intent) يسمح للعمليات (processes) بالتواصل مع بعضها البعض. يمكن للإضافات الوصول إلى كائنات CordovaInterface، والتي لديها القدرة على الوصول إلى نشاط (Activity ) أندرويد الذي بُشغّل التطبيق. هذا هو السياق (Context) المطلوب لإطلاق مقصد أندرويد جديد. يتيح Intent للإضافات بدء تشغيل نشاط (CordovaInterface) للحصول على نتيجة، وتعيين استدعاء الإضافة (callback plugin) لاستخدامه عند عودة Activity إلى التطبيق.

اعتبارًا من Cordova 2.0، لم يعد بإمكان الإضافات الوصول إلى Intent مباشرةً، وتم إيقاف العضو القديم Context. كل توابع ctx صارت موجودة الآن في السياق (ctx)، بحيث يمكن لكلا التابعين Context و getContext() إعادة الكائن المطلوب.

أذونات أندرويد

كانت أذونات أندرويد تُعالج حتى وقت قريب وقتَ التثبيت (install-time)، بدلاً من وقت التشغيل (runtime). من الضروري التصريح بهذه الأذونات في أي تطبيق يستخدم الأذونات، ويجب إضافة هذه الأذونات إلى بيان أندرويد (Android Manifest). يمكن تحقيق ذلك باستخدام getActivity() لإدارج تلك الأذونات في ملف config.xml. يستخدم المثال التالي إذن جهات الاتصال (Contacts permission). AndroidManifest.xml

أذونات وقت التشغيل (Cordova-Android 5.0.0 فما فوق)

قدم إصدار Android 6.0 "Marshmallow" نموذج أذونات جديد حيث يمكن للمستخدم تشغيل وإيقاف الأذونات حسب الضرورة. وهذا يعني أن التطبيقات يجب أن تتكيف مع هذه التغييرات، وهذا هو محور إصدار Cordova-Android 5.0.0.

يمكن العثور على الأذونات التي تحتاج إلى المعالجة وقت التشغيل في توثيق مطوري برامج أندرويد

<config-file target="AndroidManifest.xml" parent="/*">
    <uses-permission android:name="android.permission.READ_CONTACTS" />
</config-file>‎

.

بالنسبة للإضافات، يمكن طلب الإذن عن طريق استدعاء تابع الأذونات ذو الإمضاء التالي: here

من المعتاد تعيينه إلى متغير محلي ثابت.

cordova.requestPermission(CordovaPlugin plugin, int requestCode, String permission);‎

من المتعارف أيضًا تعريف كود الطلب requestCode على النحو التالي:

public static final String READ = Manifest.permission.READ_CONTACTS;‎

ثم ينبغي التحقق من الإذن في التابع exec، :

public static final int SEARCH_REQ_CODE = 0;‎

في هذه الحالة، سنستدعي requestPermission وحسب:

if(cordova.hasPermission(READ))
{
    search(executeArgs);
}
else
{
    getReadPermission(SEARCH_REQ_CODE);
}‎

سيؤدي هذا إلى استدعاء النشاط (activity) وظهور مِحَثّ (prompt) يطلب الإذن. بمجرد حصول المستخدم على الإذن، يجب معالجة النتيجة باستخدام التابع

protected void getReadPermission(int requestCode)
{
    cordova.requestPermission(this, requestCode, READ);
}‎

، والذي يجب أن يعاد تعريفه في كل إضافة. يمكن العثور على مثال على ذلك أدناه:

onRequestPermissionResult

ستعود التعليمة switch أعلاه من المحث وبناءًا على قيمة الوسيط المعطى requestCode قد تستدعي التابع المعني. تجدر الإشارة إلى أن محثات (switch) الأذونات قد تتعطل في حالة عدم معالجة التنفيذ بشكل صحيح، وأنه يجب تجنب ذلك.

بالإضافة إلى الاستئذان للحصول على إذن واحد، من الممكن أيضًا طلب الأذونات لمجموعة بأكملها، عن طريق تعريف مصفوفة الأذونات، كما هو الحال في الإضافة Geolocation:

public void onRequestPermissionResult(int requestCode, String[] permissions,
                                         int[] grantResults) throws JSONException
{
    for(int r:grantResults)
    {
        if(r == PackageManager.PERMISSION_DENIED)
        {
            this.callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.ERROR, PERMISSION_DENIED_ERROR));
            return;
        }
    }
    switch(requestCode)
    {
        case SEARCH_REQ_CODE:
            search(executeArgs);
            break;
        case SAVE_REQ_CODE:
            save(executeArgs);
            break;
        case REMOVE_REQ_CODE:
            remove(executeArgs);
            break;
    }
}‎

يعد ذلك، كل ما عليك القيام به لطلب الإذن، هو ما يلي:

String [] permissions = { Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION };‎

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

تصحيح إضافات أندرويد (Debugging Android Plugins)

يمكن تصحيح أخطاء أندرويد باستخدام Eclipse أو Android Studio، على الرغم من أنه يُوصى باستخدام Android Studio. نظرًا لاستخدام Cordova-Android حاليًا كمكتبة، وأن الإضافات تُدعم كشيفرة مصدرية، فمن الممكن تصحيح شيفرة جافا داخل تطبيقات Cordova تمامًا مثل تطبيقات أندرويد الأصلية.

إطلاق أنشطة أخرى

هناك بعض الإجراءات الخاصة التي يجب اتخاذها إن كانت الإضافة ستطلق نشاطًا (Activity) يدفع Cordova

cordova.requestPermissions(this, 0, permissions);‎

إلى الخلفية. سينهي نظام التشغيل أندرويد الأنشطة الموجودة في الخلفية عندما تنحسر الذاكرة المتاحة في الجهاز. في هذه الحالة، ستُنهى نسخة (instance‏) Activity كذلك. إن كانت الإضافةة تنتظر نتيجة من النشاط (CordovaPlugin) الذي أطلقته، فسيتم إنشاء نسخة جديدة من الإضافة عند إعادة Cordova Activity إلى الواجهة، وعند الحصول على النتيجة. لكن لن تُحفظ حالة الإضافة أو تستعاد تلقائيًا، وسيُفقد السياق Activity الخاص بالإضافة. هناك تابعان يمكن أن يعرفها CallbackContext للتعامل مع هذه المسألة:

CordovaPlugin

من المهم ملاحظة أنه يجب لا ينبغي استخدام التابعين المذكورين أعلاه إلا إن كانت الإضافة ستطلق نشاطًا (

/**
 * Called when the Activity is being destroyed (e.g. if a plugin calls out to an
 * external Activity and the OS kills the CordovaActivity in the background).
 * The plugin should save its state in this method only if it is awaiting the
 * result of an external Activity and needs to preserve some information so as
 * to handle that result; onRestoreStateForActivityResult() will only be called
 * if the plugin is the recipient of an Activity result
 *
 * @return  Bundle containing the state of the plugin or null if state does not
 *          need to be saved
 */
public Bundle onSaveInstanceState() {}
/**
 * Called when a plugin is the recipient of an Activity result after the
 * CordovaActivity has been destroyed. The Bundle will be the same as the one
 * the plugin returned in onSaveInstanceState()
 *
 * @param state             Bundle containing the state of the plugin
 * @param callbackContext   Replacement Context to return the plugin result to
 */
public void onRestoreStateForActivityResult(Bundle state, CallbackContext callbackContext) {}‎

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

كجزء من startActivityForResult()، سيُمرّر إلى الإضافة سياق بديل CallbackContext. من المهم أن تدرك أن السياق CallbackContext ليس هو نفسه الذي تم إنهاؤه مع النشاط. إذ يُفقد الاستدعاء الأصلي (original callback)، ولن يتم تشغيلها في تطبيق JavaScript. بدلاً من ذلك، سيُعيد onRestoreStateForActivityResult() النتيجة كجزء من الحدث CallbackContext الذي يتم تفعيله عند استئناف تشغيل التطبيق. الحدث [../../../cordova/events/events.html#resume resume] يتبغ البنية التالية: [../../../cordova/events/events.html#resume resume]

  • سيطابق
    {
        action: "resume",
        pendingResult: {
            pluginServiceName: string,
            pluginStatus: string,
            result: any
        }
    }‎
    العنصر pluginServiceName من ملف plugin.xml.
  • [../../../plugin_ref/spec.html#name name element] سيكون سلسلة نصية تصف حالة ناتج الإضافة (PluginResult) المٌمررة إلى CallbackContext. راجع صفحة PluginResult.java للتعرف على قيم السلسلة النصية الموافقة لحالات الإضافات
  • pluginStatus سيساوي النتيجة التي مررتها الإضافة إلى CallbackContext (سلسلة نصية، عدد، كائن JSON، إلخ.)

سيتم تمرير محتوى result إلى لك عمليات الاستدعاء (callbacks) التي سجلتها تطبيقات JavaScript مع الحدث [../../../cordova/events/events.html#resume resume]. هذا يعني أن النتيجة ستذهب مباشرة إلى تطبيق Cordova؛ لن يكون للإضافة خاصتك فرصة لمعالجة النتيجة بجافااسكريبت قبل أن يتسلمها التطبيق. وبالتالي، يجب أن تسعى إلى جعل النتيجة المعادة من الشيفرة الأصلية كاملة قدر الإمكان، وألا تعتمد على دوال جافااسكريبت عند إطلاق الأنشطة.

تأكد من توضيح كيفية تفسير تطبيق Cordova للنتيجة التي يتلقاها عند الحدث [../../../cordova/events/events.html#resume resume]. يُناط بتطبيقات Cordova حفظ حالاتها، وتذكر الطلبيات التي أرسلتها، والوسائط التي مررتها إذا لزم الأمر. ومع ذلك، يجب عليك توضيح معاني قيم [../../../cordova/events/events.html#resume resume] ونوع البيانات التي ستعاد في الحقل pluginStatus كجزء من الواجهة البرمجية للإضافة.

هذه سلسلة الأحداث الكاملة لبدء نشاط معين:

  • تطبيق Cordova يستدعي الإضافة خاصتك
  • تطلق الإضافة نشاطًا قصد الحصول على نتيجة
  • ينهي نظام التشغيل أندرويد كلًا من النشاط ونسخة (instance) الإضافة
  • يستدعى الحدث onSaveInstanceState()‎
  • يتفاعل المستخدم مع نشاطك، ثم ينتهي النشاط
  • يعاد إنشاء نشاط Cordova، ويتم تلقي نتيجة النشاط
  • يستدعى الحدث onRestoreStateForActivityResult()‎
  • يتم استدعاء [../../../cordova/events/events.html#resume resume] ثم تمرر الإضافة نتيجة إلى السياق CallbackContext الجديد
  • يُفعّل الحدث onActivityResult() ويُتلقى من قبل تطبيق Cordova

يوفر أندرويد إعدادات للمطوِّرين لتصحيح أخطاء إنهاء الأنشطة عندما يكون هناك انحسار في مساحة الذاكرة. قم بتمكين الإعداد "Don't keep activities" في قائمة خيارات المطور على جهازك أو المحاكي لمحاكاة سيناريوهات انحسار الذاكرة. إن كانت الإضافة تُطلق نشاطات خارجية، فيجب عليك إجراء بعض الاختبارات مع تمكين هذا الإعداد لضمان التعامل بشكل صحيح مع سيناريوهات انحسار الذاكرة.

مصادر