تُعدّ أفضل الممارسات الموضّحة هنا دليلاً لتطوير واجهات AIDL بفعالية مع الانتباه إلى مرونة الواجهة، لا سيّما عند استخدام AIDL لتحديد واجهة برمجة تطبيقات أو التفاعل مع مساحات عرض واجهات برمجة التطبيقات.
يمكن استخدام AIDL لتحديد واجهة برمجة تطبيقات عندما تحتاج التطبيقات إلى التفاعل مع بعضها في عملية في الخلفية أو تحتاج إلى التفاعل مع النظام. لمزيد من المعلومات حول تطوير واجهات برمجة التطبيقات في التطبيقات التي تستخدم لغة تعريف واجهة Android (AIDL)، يُرجى الاطّلاع على لغة تعريف واجهة Android (AIDL). للحصول على أمثلة على استخدام AIDL، يُرجى الاطّلاع على AIDL لأجل HALs وAIDL الثابت.
تحديد الإصدار
تتطابق كل لقطة متوافقة مع الإصدارات القديمة لواجهة برمجة التطبيقات AIDL مع إصدار.
لالتقاط لقطة، شغِّل m <module-name>-freeze-api
. عند إصدار إصدار جديد من العميل أو
الخادم لواجهة برمجة التطبيقات (على سبيل المثال، في سلسلة Mainline)، عليك
التقاط لقطة شاشة وإنشاء إصدار جديد. بالنسبة إلى واجهات برمجة التطبيقات التي تربط النظام بالمورّد، من المفترض أن يتم تعديلها عند إجراء مراجعة النظام السنوية.
لمزيد من التفاصيل والمعلومات حول نوع التغييرات المسموح بها، يُرجى الاطّلاع على واجهات تحديد الإصدار.
إرشادات تصميم واجهة برمجة التطبيقات
بنود عامة
1. توثيق كل شيء
- سجِّل كل طريقة لدلالتها ووسيطاتها واستخدامها للاستثناءات المضمّنة والاستثناءات الخاصة بالخدمة والقيمة المعروضة.
- سجِّل كل واجهة لدلالتها.
- توثيق المعنى الدلالي لقوائم التعداد والثوابت
- سجِّل أي معلومات قد تكون غير واضحة لأحد مُنفّذِي الحملة.
- يُرجى تقديم أمثلة عند الضرورة.
2- الغلاف
استخدِم الأحرف اللاتينية الكبيرة لكتابة الأنواع والأحرف اللاتينية الصغيرة لكتابة الطرق والحقول
والوسيطات. على سبيل المثال، MyParcelable
لنوع قابل للتقسيم وanArgument
لوسيطة. بالنسبة إلى الاختصارات، يمكنك اعتبار الاختصار كلمة (NFC
-> Nfc
).
[-Wconst-name] يجب أن تكون قيم Enum والثوابت ENUM_VALUE
و
CONSTANT_NAME
واجهات
1. التسمية
[-Winterface-name] يجب أن يبدأ اسم الواجهة بـ I
مثل IFoo
.
2- تجنب الواجهة الكبيرة باستخدام "الكائنات" المستندة إلى المعرف
تفضيل الواجهات الفرعية عندما يكون هناك العديد من الطلبات ذات الصلة بواجهة برمجة تطبيقات معينة. ويمنحك ذلك المزايا التالية:
- تسهيل فهم رمز العميل أو الخادم
- تجعل دورة حياة الكائنات أكثر بساطة
- الاستفادة من عدم إمكانية تزوير المجلدات
إجراء غير مقترَح: واجهة واحدة كبيرة تتضمّن عناصر مستندة إلى المعرّفات
interface IManager {
int getFooId();
void beginFoo(int id); // clients in other processes can guess an ID
void opFoo(int id);
void recycleFoo(int id); // ownership not handled by type
}
موصى بها: واجهات فردية
interface IManager {
IFoo getFoo();
}
interface IFoo {
void begin(); // clients in other processes can't guess a binder
void op();
}
3- عدم الخلط بين طريق أحادي الاتجاه وطرق ثنائية الاتجاه
[-Wmixed-oneway] لا تَمزِج بين الطرق ذات الاتجاه الواحد والطرق غير ذات الاتجاه الواحد، لأنّ ذلك يجعل فهم نموذج معالجة المهام المتعدّدة أمرًا معقّدًا على العملاء والخوادم. على وجه التحديد، عند قراءة رمز العميل لواجهة معيّنة، تحتاج إلى البحث عن كل طريقة إذا كانت هذه الطريقة ستحظرها أم لا.
4. تجنُّب عرض رموز الحالة
يجب أن تتجنّب الطرق رموز الحالة كقيم عرض، نظرًا لأن جميع طرق AIDL لها
رمز عرض حالة ضمني. يمكنك الاطّلاع على ServiceSpecificException
أو EX_SERVICE_SPECIFIC
. وفقًا للعرف، يتم تعريف هذه القيم على أنّها ثابتة في
واجهة AIDL. تتوفّر معلومات أكثر تفصيلاً في
قسم معالجة الأخطاء في AIDL
backends.
5. المصفوفات كمعلَمات إخراج تُعتبر ضارة
[-Wout-array] إنّ الطرق التي تحتوي على مَعلمات إخراج صفيف، مثل
void foo(out String[] ret)
، تكون عادةً سيئة لأنّ حجم صفيف الإخراج يجب
أن يُعلن عنه ويخصّصه العميل في Java، وبالتالي لا يمكن للخادم اختيار حجم صفيف
الإخراج. يحدث هذا السلوك غير المرغوب فيه بسبب
طريقة عمل الصفائف في Java (لا يمكن إعادة تخصيصها). بدلاً من ذلك، يفضلون واجهات برمجة التطبيقات
مثل String[] foo()
.
6. تجنُّب مَعلمات الإدخال والإخراج
[-Winout-parameter] قد يؤدي ذلك إلى إرباك العملاء لأنّ مَعلمات in
تبدو
مثل مَعلمات out
.
7. تجنَّب استخدام مَعلمات out وinout غير المصفوفة التي تحمل العلامة @nullable.
[-Wout-nullable] بما أنّ الخلفية في Java لا تتعامل مع التعليق التوضيحي @nullable
بينما تتعامل الخلفيات الأخرى معه، قد يؤدي out/inout @nullable T
إلى سلوك غير متّسق
في جميع الخلفيات. على سبيل المثال، يمكن لأنظمة التشغيل غير المستندة إلى Java ضبط مَعلمة @nullable
out على القيمة null (في C++، يتم ضبطها على std::nullopt
)، ولكن لا يمكن لبرنامج Java العميل قراءة القيمة null.
العناصر القابلة للتقسيم
1. حالات الاستخدام
استخدِم العناصر القابلة للتقسيم المنظَّمة عندما يكون لديك أنواع بيانات متعددة لإرسالها.
أو عندما يكون لديك نوع بيانات واحد ولكنك تتوقّع أن تحتاج
إلى توسيعه في المستقبل. على سبيل المثال، لا تستخدِم String username
. استخدِم ملفًا قابلاً للتوسيع، مثل ما يلي:
parcelable User {
String username;
}
وبالتالي، يمكنك تمديد هذه الفترة في المستقبل، على النحو التالي:
parcelable User {
String username;
int id;
}
2- تقديم الإعدادات التلقائية بشكل صريح
[-Wexplicit-default, -Wenum-explicit-default] توفير إعدادات تلقائية صريحة للحقول
العناصر غير القابلة للتقسيم
1. حالات الاستخدام
تتوفّر العناصر غير المنظَّمة في Java باستخدام @JavaOnlyStableParcelable
وفي الخلفية في NDK باستخدام @NdkOnlyStableParcelable
. عادةً ما تكون هذه قطعًا قديمة وحالية
لا يمكن هيكلتها.
الثوابت والتعدادات
1. يجب أن تستخدم حقول Bitfields حقولاً ثابتة
يجب أن تستخدم حقول Bitfields حقولاً ثابتة (على سبيل المثال، const int FOO = 3;
في واجهة).
2- يجب أن تكون النماذج المحدَّدة مسبقًا مجموعات مغلقة.
يجب أن تكون النماذج المحدَّدة مسبقًا مجموعات مغلقة. ملاحظة: يمكن لصاحب الواجهة فقط إضافة عناصر enum. إذا احتاج المورّدون أو المصنّعون الأصليون إلى توسيع هذه الحقول، يجب استخدام بديل للآلية. يجب تفضيل معالجة وظائف المورّد في الوقت الفعلي كلما أمكن. ومع ذلك، في بعض الحالات، قد يُسمح باستخدام قيم المورّدين المخصّصة (على الرغم من أنّه يجب أن يكون لدى المورّدين آلية متوفّرة لإصدار هذه القيم، ربما AIDL نفسها، ويجب ألا يكون بإمكانها الاصطدام ببعضها، ويجب عدم عرض هذه القيم للتطبيقات التابعة لجهات خارجية).
3- تجنَّب استخدام قيم مثل "NUM_ELEMENTS".
نظرًا لأن التعدادات لها نُسخ، يجب تجنب القيم التي تشير إلى عدد القيم الموجودة. في C++، يمكن حلّ هذه المشكلة باستخدام enum_range<>
. لاستخدام Rust، استخدِم enum_values()
. في Java، ما مِن حلّ حتى الآن.
إجراء غير مقترَح: استخدام قيم مرقّمة
@Backing(type="int")
enum FruitType {
APPLE = 0,
BANANA = 1,
MANGO = 2,
NUM_TYPES, // BAD
}
4. تجنَّب استخدام البادئات واللاحقات المكرّرة.
[-Wredundant-name] تجنَّب استخدام البادئات واللاحقات المكرّرة أو المتكرّرة في الثوابت والمعرّفات.
غير مستحسن: استخدام بادئة متكرّرة
enum MyStatus {
STATUS_GOOD,
STATUS_BAD // BAD
}
إجراء مقترَح: تسمية القائمة المحدودة القيم مباشرةً
enum MyStatus {
GOOD,
BAD
}
FileDescriptor
[-Wfile-descriptor] يُنصح بشدة بعدم استخدام FileDescriptor
كوسيطة أو قيمة القيمة المعروضة لطريقة واجهة AIDL. على وجه الخصوص، عند تنفيذ IDE في Java، قد يؤدي ذلك إلى تسرُّب ملف الوصف ما لم تتم معالجته بعناية. بشكل أساسي، إذا وافقت على FileDescriptor
، عليك
إغلاقه يدويًا عندما لا يعود قيد الاستخدام.
بالنسبة إلى الخلفيات الأصلية، لن تواجه أي مشكلة لأنّ FileDescriptor
يتم ربطه بـ unique_fd
الذي يمكن إغلاقه تلقائيًا. وبغض النظر عن لغة الخلفية التي تريد استخدامها،
ننصحك بعدم استخدام FileDescriptor
على الإطلاق لأنّ ذلك سيحدّ من
حريتك في تغيير لغة الخلفية في المستقبل.
بدلاً من ذلك، استخدِم ParcelFileDescriptor
الذي يمكن إغلاقه تلقائيًا.
الوحدات المتغيّرة
تأكَّد من تضمين الوحدات المتغيّرة في الاسم حتى تكون وحداتها محدّدة بشكل جيد ومفهومة بدون الحاجة إلى الرجوع إلى المستندات.
أمثلة
long duration; // Bad
long durationNsec; // Good
long durationNanos; // Also good
double energy; // Bad
double energyMilliJoules; // Good
int frequency; // Bad
int frequencyHz; // Good
يجب أن تشير الطوابع الزمنية إلى مرجعها.
يجب أن تشير الطوابع الزمنية (أو جميع الوحدات) بوضوح إلى وحداتها ونقاط مرجعها.
أمثلة
/**
* Time since device boot in milliseconds
*/
long timestampMs;
/**
* UTC time received from the NTP server in units of milliseconds
* since January 1, 1970
*/
long utcTimeMs;