دليل نمط AIDL

تعمل أفضل الممارسات الموضحة هنا كدليل لتطوير واجهات AIDL بشكل فعال مع الاهتمام بمرونة الواجهة، خاصة عند استخدام AIDL لتعريف واجهة برمجة التطبيقات (API) أو التفاعل مع أسطح واجهة برمجة التطبيقات (API).

يمكن استخدام AIDL لتحديد واجهة برمجة التطبيقات (API) عندما تحتاج التطبيقات إلى التفاعل مع بعضها البعض في عملية الخلفية أو تحتاج إلى التفاعل مع النظام. لمزيد من المعلومات حول تطوير واجهات البرمجة في التطبيقات باستخدام AIDL، راجع لغة تعريف واجهة Android (AIDL) . للحصول على أمثلة على AIDL عمليًا، راجع AIDL لـ HALs و Stable AIDL .

الإصدار

تتوافق كل لقطة متوافقة مع الإصدارات السابقة من واجهة برمجة تطبيقات AIDL مع إصدار ما. لالتقاط لقطة، قم بتشغيل m <module-name>-freeze-api . عندما يتم إصدار عميل أو خادم لواجهة برمجة التطبيقات (على سبيل المثال، في قطار رئيسي)، فإنك تحتاج إلى التقاط لقطة وإنشاء إصدار جديد. بالنسبة لواجهات برمجة التطبيقات من النظام إلى البائع، يجب أن يحدث هذا مع مراجعة النظام الأساسي السنوية.

لمزيد من التفاصيل والمعلومات حول نوع التغييرات المسموح بها، راجع واجهات إصدار الإصدارات .

إرشادات تصميم واجهة برمجة التطبيقات

عام

1. توثيق كل شيء

  • قم بتوثيق كل طريقة من حيث دلالاتها ووسائطها واستخدام الاستثناءات المضمنة والاستثناءات الخاصة بالخدمة وقيمة الإرجاع.
  • توثيق كل واجهة لدلالاتها.
  • توثيق المعنى الدلالي للتعدادات والثوابت.
  • قم بتوثيق كل ما قد يكون غير واضح للمنفذ.
  • تقديم أمثلة حيثما كان ذلك مناسبا.

2. الغلاف

استخدم غلاف الجمل العلوي للأنواع، وغطاء الجمل السفلي للطرق والحقول والوسائط. على سبيل المثال، MyParcelable للنوع القابل للتوزيع anArgument للوسيطة. بالنسبة للاختصارات، اعتبر الاختصار كلمة ( NFC -> Nfc ).

[-Wconst-name] يجب أن تكون قيم التعداد والثوابت ENUM_VALUE و CONSTANT_NAME

واجهات

1. التسمية

[-Winterface-name] يجب أن يبدأ اسم الواجهة بـ I like 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. تجنب الخروج/الخروج @nullable المعلمات غير المصفوفة

[-Wout-nullable] نظرًا لأن الواجهة الخلفية لـ Java لا تتعامل مع التعليق التوضيحي @nullable بينما تفعل الواجهات الخلفية الأخرى، out/inout @nullable T قد يؤدي إلى سلوك غير متناسق عبر الواجهات الخلفية. على سبيل المثال، يمكن للواجهات الخلفية غير التابعة لـ Java تعيين معلمة @nullable إلى 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. يجب أن تستخدم حقول البت حقولاً ثابتة

يجب أن تستخدم حقول البت حقولاً ثابتة (على سبيل المثال const int FOO = 3; في الواجهة).

2. يجب أن تكون التعدادات مجموعات مغلقة.

يجب أن تكون التعدادات مجموعات مغلقة. ملاحظة: يمكن لمالك الواجهة فقط إضافة عناصر التعداد. إذا احتاج البائعون أو مصنعو المعدات الأصلية إلى توسيع هذه الحقول، فستكون هناك حاجة إلى آلية بديلة. كلما كان ذلك ممكنًا، يجب تفضيل وظيفة البائع المنبع. ومع ذلك، في بعض الحالات، قد يتم السماح بقيم البائع المخصصة من خلال (على الرغم من أنه يجب أن يكون لدى البائعين آلية لإصدار هذا، ربما AIDL نفسه، ولا ينبغي أن يكونوا قادرين على التعارض مع بعضهم البعض، ولا ينبغي أن تكون هذه القيم يتعرض لتطبيقات الطرف الثالث).

3. تجنب القيم مثل "NUM_ELEMENTS"

نظرًا لأنه تم إصدار التعدادات، فيجب تجنب القيم التي تشير إلى عدد القيم الموجودة. في لغة C++، يمكن حل هذه المشكلة باستخدام enum_range<> . بالنسبة إلى الصدأ، استخدم enum_values() . في جافا، لا يوجد حل حتى الآن.

غير مستحسن: استخدام القيم المرقمة

@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
}

واصف الملف

[-Wfile-descriptor] لا يُنصح بشدة باستخدام FileDescriptor كوسيطة أو قيمة الإرجاع لأسلوب واجهة AIDL. على وجه الخصوص، عند تنفيذ AIDL في 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;