بهترین روشهای ذکر شده در اینجا بهعنوان راهنمایی برای توسعه رابطهای AIDL به طور مؤثر و با توجه به انعطافپذیری رابط عمل میکند، بهویژه زمانی که AIDL برای تعریف یک API یا تعامل با سطوح API استفاده میشود.
AIDL می تواند برای تعریف یک API استفاده شود، زمانی که برنامه ها نیاز به رابط با یکدیگر در یک فرآیند پس زمینه یا نیاز به رابط با سیستم دارند. برای اطلاعات بیشتر در مورد توسعه رابط های برنامه نویسی در برنامه های دارای AIDL، به زبان تعریف رابط اندروید (AIDL) مراجعه کنید. برای مثالهایی از AIDL در عمل، به AIDL برای HAL و AIDL پایدار مراجعه کنید.
نسخه سازی
هر عکس فوری سازگار با عقب از یک API AIDL مربوط به یک نسخه است. برای گرفتن عکس فوری، m <module-name>-freeze-api
اجرا کنید. هر زمان که یک کلاینت یا سرور API منتشر می شود (مثلاً در قطار اصلی)، باید یک عکس فوری بگیرید و یک نسخه جدید بسازید. برای APIهای سیستم به فروشنده، این باید با بازبینی سالانه پلتفرم اتفاق بیفتد.
برای جزئیات بیشتر و اطلاعات در مورد نوع تغییرات مجاز، به رابطهای نسخهسازی مراجعه کنید.
دستورالعمل های طراحی API
ژنرال
1. همه چیز را مستند کنید
- هر روشی را برای معناشناسی، آرگومان ها، استفاده از استثناهای داخلی، استثناهای خاص سرویس و مقدار بازگشتی آن مستند کنید.
- هر رابط را برای معناشناسی آن مستند کنید.
- معنای معنایی تعداد و ثابت ها را مستند کنید.
- هر چیزی که ممکن است برای یک مجری نامشخص باشد را مستند کنید.
- در صورت لزوم مثال هایی ارائه کنید.
2. پوشش
از پوشش شتر بالایی برای انواع و از پوشش شتر پایینی برای روش ها، زمینه ها و استدلال ها استفاده کنید. به عنوان مثال، MyParcelable
برای یک نوع parcelable و anArgument
برای یک آرگومان. برای کلمات اختصاری، مخفف را یک کلمه در نظر بگیرید ( NFC
-> Nfc
).
[-Wconst-name] مقادیر و ثابت های Enum باید ENUM_VALUE
و CONSTANT_NAME
باشند
رابط ها
1. نامگذاری
[-Winterface-name] نام رابط باید با I
like IFoo
شروع شود.
2. اجتناب از رابط بزرگ با "اشیاء" مبتنی بر شناسه
هنگامی که تماس های زیادی مربوط به یک API خاص وجود دارد، واسط های فرعی را ترجیح دهید. این مزایای زیر را فراهم می کند:
- درک کد مشتری یا سرور را آسان تر می کند
- چرخه زندگی اشیا را ساده تر می کند
- از غیرقابل جعل بودن کلاسورها استفاده می کند.
توصیه نمی شود: یک رابط واحد و بزرگ با اشیاء مبتنی بر ID
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] روشهای یکطرفه را با روشهای غیر یکطرفه مخلوط نکنید، زیرا درک مدل threading را برای کلاینتها و سرورها پیچیده میکند. به طور خاص، هنگام خواندن کد مشتری یک رابط خاص، باید به دنبال هر روشی باشید که آیا آن روش مسدود می شود یا خیر.
4. از بازگرداندن کدهای وضعیت خودداری کنید
روش ها باید از کدهای وضعیت به عنوان مقادیر بازگشتی اجتناب کنند، زیرا همه روش های AIDL دارای کد بازگشت وضعیت ضمنی هستند. ServiceSpecificException
یا EX_SERVICE_SPECIFIC
را ببینید. طبق قرارداد، این مقادیر به عنوان ثابت در رابط AIDL تعریف می شوند. اطلاعات دقیق تر در بخش رسیدگی به خطاهای باطن AIDL موجود است.
5. آرایه ها به عنوان پارامترهای خروجی مضر در نظر گرفته می شوند
[-Wout-array] روش هایی که دارای پارامترهای خروجی آرایه هستند، مانند void foo(out String[] ret)
معمولا بد هستند زیرا اندازه آرایه خروجی باید توسط مشتری در جاوا اعلام و تخصیص داده شود، و بنابراین اندازه خروجی آرایه نمی تواند توسط سرور انتخاب شود این رفتار نامطلوب به دلیل نحوه کار آرایه ها در جاوا اتفاق می افتد (آنها را نمی توان دوباره تخصیص داد). در عوض APIهایی مانند String[] foo()
را ترجیح دهید.
6. از پارامترهای inout اجتناب کنید
[-Winout-parameter] این می تواند کلاینت ها را گیج کند زیرا حتی in
پارامترها نیز مانند پارامترهای out
به نظر می رسند.
7. از خروج و خروج پارامترهای غیر آرایه nullable @ اجتناب کنید
[-Wout-nullable] از آنجایی که باطن جاوا حاشیهنویسی @nullable
را مدیریت نمیکند در حالی که سایر باطنها این کار را انجام میدهند، out/inout @nullable T
ممکن است منجر به رفتار ناسازگار در میان پشتیبانها شود. به عنوان مثال، پشتیبانهای غیر جاوا میتوانند یک پارامتر @nullable
را به null تنظیم کنند (در C++، آن را به عنوان std::nullopt
تنظیم میکند) اما مشتری جاوا نمیتواند آن را به عنوان null بخواند.
بسته بندی های ساخت یافته
1. زمان استفاده
از بستهبندیهای ساختاریافته استفاده کنید که در آن انواع دادههای متعددی برای ارسال دارید.
یا زمانی که یک نوع داده دارید اما انتظار دارید که در آینده باید آن را گسترش دهید. به عنوان مثال، String username
استفاده نکنید. از یک بسته قابل توسعه مانند موارد زیر استفاده کنید:
parcelable User {
String username;
}
به طوری که در آینده می توانید آن را به شرح زیر تمدید کنید:
parcelable User {
String username;
int id;
}
2. پیش فرض ها را به صراحت ارائه دهید
[-Wexplicit-default, -Wenum-explicit-default] پیش فرض های صریح را برای فیلدها ارائه دهید.
قطعات بدون ساختار
1. زمان استفاده
قطعات بدون ساختار در جاوا با @JavaOnlyStableParcelable
و در پشتیبان NDK با @NdkOnlyStableParcelable
موجود هستند. معمولاً اینها بستهبندیهای قدیمی و موجود هستند که نمیتوان آنها را ساختار داد.
ثابت ها و اعداد
1. Bitfield ها باید از فیلدهای ثابت استفاده کنند
فیلدهای بیتی باید از فیلدهای ثابت استفاده کنند (مثلاً const int FOO = 3;
در یک رابط).
2. Enum ها باید مجموعه های بسته باشند.
Enum ها باید مجموعه های بسته باشند. توجه: فقط مالک رابط می تواند عناصر enum را اضافه کند. اگر فروشندگان یا OEM ها نیاز به گسترش این زمینه ها داشته باشند، مکانیزم جایگزین مورد نیاز است. در صورت امکان، عملکرد فروشنده بالادستی باید ترجیح داده شود. با این حال، در برخی موارد، ممکن است مقادیر سفارشی فروشنده مجاز باشد (البته، فروشندگان باید مکانیزمی برای نسخهسازی این نسخه داشته باشند، شاید خود AIDL، آنها نباید بتوانند با یکدیگر تضاد داشته باشند، و این مقادیر نباید در معرض برنامه های شخص ثالث).
3. از مقادیری مانند "NUM_ELEMENTS" اجتناب کنید
از آنجایی که enum ها نسخه بندی شده اند، باید از مقادیری که تعداد مقادیر موجود را نشان می دهند اجتناب شود. در C++، می توان با enum_range<>
این کار را انجام داد. برای Rust از 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
enum MyStatus {
GOOD,
BAD
}
FileDescriptor
[-Wfile-descriptor] استفاده از FileDescriptor
به عنوان آرگومان یا مقدار بازگشتی یک روش رابط AIDL به شدت ممنوع است. به خصوص، زمانی که AIDL در جاوا پیادهسازی میشود، ممکن است باعث نشت توصیفگر فایل شود، مگر اینکه با دقت مدیریت شود. اساساً، اگر یک FileDescriptor
قبول کنید، باید آن را به صورت دستی ببندید، زمانی که دیگر استفاده نمی شود.
برای backend های بومی، شما ایمن هستید، زیرا 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;
بهترین روشهای ذکر شده در اینجا بهعنوان راهنمایی برای توسعه رابطهای AIDL به طور مؤثر و با توجه به انعطافپذیری رابط عمل میکند، بهویژه زمانی که AIDL برای تعریف یک API یا تعامل با سطوح API استفاده میشود.
AIDL می تواند برای تعریف یک API استفاده شود، زمانی که برنامه ها نیاز به رابط با یکدیگر در یک فرآیند پس زمینه یا نیاز به رابط با سیستم دارند. برای اطلاعات بیشتر در مورد توسعه رابط های برنامه نویسی در برنامه های دارای AIDL، به زبان تعریف رابط اندروید (AIDL) مراجعه کنید. برای مثالهایی از AIDL در عمل، به AIDL برای HAL و AIDL پایدار مراجعه کنید.
نسخه سازی
هر عکس فوری سازگار با عقب از یک API AIDL مربوط به یک نسخه است. برای گرفتن عکس فوری، m <module-name>-freeze-api
اجرا کنید. هر زمان که یک کلاینت یا سرور API منتشر می شود (مثلاً در قطار اصلی)، باید یک عکس فوری بگیرید و یک نسخه جدید بسازید. برای APIهای سیستم به فروشنده، این باید با بازبینی سالانه پلتفرم اتفاق بیفتد.
برای جزئیات بیشتر و اطلاعات در مورد نوع تغییرات مجاز، به رابطهای نسخهسازی مراجعه کنید.
دستورالعمل های طراحی API
ژنرال
1. همه چیز را مستند کنید
- هر روشی را برای معناشناسی، آرگومان ها، استفاده از استثناهای داخلی، استثناهای خاص سرویس و مقدار بازگشتی آن مستند کنید.
- هر رابط را برای معناشناسی آن مستند کنید.
- معنای معنایی تعداد و ثابت ها را مستند کنید.
- هر چیزی که ممکن است برای یک مجری نامشخص باشد را مستند کنید.
- در صورت لزوم مثال هایی ارائه کنید.
2. پوشش
از پوشش شتر بالایی برای انواع و از پوشش شتر پایینی برای روش ها، زمینه ها و استدلال ها استفاده کنید. به عنوان مثال، MyParcelable
برای یک نوع parcelable و anArgument
برای یک آرگومان. برای کلمات اختصاری، مخفف را یک کلمه در نظر بگیرید ( NFC
-> Nfc
).
[-Wconst-name] مقادیر و ثابت های Enum باید ENUM_VALUE
و CONSTANT_NAME
باشند
رابط ها
1. نامگذاری
[-Winterface-name] نام رابط باید با I
like IFoo
شروع شود.
2. اجتناب از رابط بزرگ با "اشیاء" مبتنی بر شناسه
هنگامی که تماس های زیادی مربوط به یک API خاص وجود دارد، واسط های فرعی را ترجیح دهید. این مزایای زیر را فراهم می کند:
- درک کد مشتری یا سرور را آسان تر می کند
- چرخه زندگی اشیا را ساده تر می کند
- از غیرقابل جعل بودن کلاسورها استفاده می کند.
توصیه نمی شود: یک رابط واحد و بزرگ با اشیاء مبتنی بر ID
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] روشهای یکطرفه را با روشهای غیر یکطرفه مخلوط نکنید، زیرا درک مدل threading را برای کلاینتها و سرورها پیچیده میکند. به طور خاص، هنگام خواندن کد مشتری یک رابط خاص، باید به دنبال هر روشی باشید که آیا آن روش مسدود می شود یا خیر.
4. از بازگرداندن کدهای وضعیت خودداری کنید
روش ها باید از کدهای وضعیت به عنوان مقادیر بازگشتی اجتناب کنند، زیرا همه روش های AIDL دارای کد بازگشت وضعیت ضمنی هستند. ServiceSpecificException
یا EX_SERVICE_SPECIFIC
را ببینید. طبق قرارداد، این مقادیر به عنوان ثابت در رابط AIDL تعریف می شوند. اطلاعات دقیق تر در بخش رسیدگی به خطاهای باطن AIDL موجود است.
5. آرایه ها به عنوان پارامترهای خروجی مضر در نظر گرفته می شوند
[-Wout-array] روش هایی که دارای پارامترهای خروجی آرایه هستند، مانند void foo(out String[] ret)
معمولا بد هستند زیرا اندازه آرایه خروجی باید توسط مشتری در جاوا اعلام و تخصیص داده شود، و بنابراین اندازه خروجی آرایه نمی تواند توسط سرور انتخاب شود این رفتار نامطلوب به دلیل نحوه کار آرایه ها در جاوا اتفاق می افتد (آنها را نمی توان دوباره تخصیص داد). در عوض APIهایی مانند String[] foo()
را ترجیح دهید.
6. از پارامترهای inout اجتناب کنید
[-Winout-parameter] این می تواند کلاینت ها را گیج کند زیرا حتی in
پارامترها نیز مانند پارامترهای out
به نظر می رسند.
7. از خروج و خروج پارامترهای غیر آرایه nullable @ اجتناب کنید
[-Wout-nullable] از آنجایی که باطن جاوا حاشیهنویسی @nullable
را مدیریت نمیکند در حالی که سایر باطنها این کار را انجام میدهند، out/inout @nullable T
ممکن است منجر به رفتار ناسازگار در میان پشتیبانها شود. به عنوان مثال، پشتیبانهای غیر جاوا میتوانند یک پارامتر @nullable
را به null تنظیم کنند (در C++، آن را به عنوان std::nullopt
تنظیم میکند) اما مشتری جاوا نمیتواند آن را به عنوان null بخواند.
بسته بندی های ساخت یافته
1. زمان استفاده
از بستهبندیهای ساختاریافته استفاده کنید که در آن انواع دادههای متعددی برای ارسال دارید.
یا زمانی که یک نوع داده دارید اما انتظار دارید که در آینده باید آن را گسترش دهید. به عنوان مثال، String username
استفاده نکنید. از یک بسته قابل توسعه مانند موارد زیر استفاده کنید:
parcelable User {
String username;
}
به طوری که در آینده می توانید آن را به شرح زیر تمدید کنید:
parcelable User {
String username;
int id;
}
2. پیش فرض ها را به صراحت ارائه دهید
[-Wexplicit-default, -Wenum-explicit-default] پیش فرض های صریح را برای فیلدها ارائه دهید.
قطعات بدون ساختار
1. زمان استفاده
قطعات بدون ساختار در جاوا با @JavaOnlyStableParcelable
و در پشتیبان NDK با @NdkOnlyStableParcelable
موجود هستند. معمولاً اینها بستهبندیهای قدیمی و موجود هستند که نمیتوان آنها را ساختار داد.
ثابت ها و اعداد
1. Bitfield ها باید از فیلدهای ثابت استفاده کنند
فیلدهای بیتی باید از فیلدهای ثابت استفاده کنند (مثلاً const int FOO = 3;
در یک رابط).
2. Enum ها باید مجموعه های بسته باشند.
Enum ها باید مجموعه های بسته باشند. توجه: فقط مالک رابط می تواند عناصر enum را اضافه کند. اگر فروشندگان یا OEM ها نیاز به گسترش این زمینه ها داشته باشند، مکانیزم جایگزین مورد نیاز است. در صورت امکان، عملکرد فروشنده بالادستی باید ترجیح داده شود. با این حال، در برخی موارد، ممکن است مقادیر سفارشی فروشنده مجاز باشد (البته، فروشندگان باید مکانیزمی برای نسخهسازی این نسخه داشته باشند، شاید خود AIDL، آنها نباید بتوانند با یکدیگر تضاد داشته باشند، و این مقادیر نباید در معرض برنامه های شخص ثالث).
3. از مقادیری مانند "NUM_ELEMENTS" اجتناب کنید
از آنجایی که enum ها نسخه بندی شده اند، باید از مقادیری که تعداد مقادیر موجود را نشان می دهند اجتناب شود. در C++، می توان با enum_range<>
این کار را انجام داد. برای Rust از 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
enum MyStatus {
GOOD,
BAD
}
FileDescriptor
[-Wfile-descriptor] استفاده از FileDescriptor
به عنوان آرگومان یا مقدار بازگشتی یک روش رابط AIDL به شدت ممنوع است. به خصوص، زمانی که AIDL در جاوا پیادهسازی میشود، ممکن است باعث نشت توصیفگر فایل شود، مگر اینکه با دقت مدیریت شود. اساساً، اگر یک FileDescriptor
قبول کنید، باید آن را به صورت دستی ببندید، زمانی که دیگر استفاده نمی شود.
برای backend های بومی، شما ایمن هستید، زیرا 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;