แนวทางปฏิบัติแนะนำที่ระบุไว้ที่นี่เป็นแนวทางในการพัฒนาอินเทอร์เฟซ AIDL อย่างมีประสิทธิภาพและคำนึงถึงความยืดหยุ่นของอินเทอร์เฟซ โดยเฉพาะอย่างยิ่งเมื่อใช้ AIDL เพื่อกำหนด API ที่เสถียรและเข้ากันได้แบบย้อนหลัง
คุณใช้ AIDL เพื่อกำหนด API ได้เมื่อแอปต้องเชื่อมต่อกันในกระบวนการเบื้องหลังหรือต้องเชื่อมต่อกับระบบ
AIDL ที่เสถียรพร้อม @VintfStability ใช้สำหรับอินเทอร์เฟซ HAL และช่วยให้ไคลเอ็นต์และเซิร์ฟเวอร์อัปเดตแยกกันได้ ซึ่งต้องใช้ความเข้ากันได้แบบย้อนหลังและข้อมูลที่มีโครงสร้าง
ดูข้อมูลเพิ่มเติมเกี่ยวกับการพัฒนาอินเทอร์เฟซการเขียนโปรแกรมในแอปด้วย AIDL ได้ที่ภาษาที่ใช้สื่อสารข้อมูลระหว่างคอมโพเนนต์ของ Android (AIDL) ดูตัวอย่างการใช้งาน AIDL ได้ที่ AIDL สำหรับ HAL และ AIDL ที่เสถียร
การกำหนดเวอร์ชัน
สแนปชอตที่เข้ากันได้แบบย้อนหลังทุกรายการของ AIDL API จะสอดคล้องกับเวอร์ชันหนึ่งๆ
หากต้องการถ่ายสแนปชอต ให้เรียกใช้ m <module-name>-freeze-api เมื่อใดก็ตามที่มีการเผยแพร่ไคลเอ็นต์หรือเซิร์ฟเวอร์ของ API (เช่น ใน Mainline Train) คุณจะต้องถ่ายสแนปชอตและสร้างเวอร์ชันใหม่ สำหรับ API จากระบบไปยังผู้ให้บริการ การดำเนินการนี้ควรเกิดขึ้นพร้อมกับการแก้ไขแพลตฟอร์มประจำปี
เมื่ออินเทอร์เฟซถูกระงับ (บันทึกไว้ในไดเรกทอรี aidl_api ที่กำหนดเวอร์ชัน) จะต้องไม่มีการแก้ไขอินเทอร์เฟซดังกล่าว คุณแก้ไขได้เฉพาะไดเรกทอรี current เท่านั้น คุณสามารถเพิ่มเมธอดที่ส่วนท้ายของอินเทอร์เฟซ ฟิลด์ที่ส่วนท้ายของ Parcelable ตัวแจงนับไปยัง Enum และสมาชิกไปยัง Union ได้อย่างปลอดภัย
ไคลเอ็นต์ที่เรียกใช้เมธอดใหม่ในเซิร์ฟเวอร์รุ่นเก่าจะได้รับข้อผิดพลาด UNKNOWN_TRANSACTION ซึ่งไคลเอ็นต์ควรจัดการอย่างเหมาะสม
ดูรายละเอียดและข้อมูลเพิ่มเติมเกี่ยวกับประเภทการเปลี่ยนแปลงที่อนุญาตได้ที่อินเทอร์เฟซการกำหนดเวอร์ชัน
ทรัพยากร Dependency ของบิลด์
โมดูล Android ไม่สามารถใช้ไลบรารีที่สร้างขึ้นหลายเวอร์ชันที่แตกต่างกันจาก aidl_interface ไลบรารีเวอร์ชันต่างๆ จะกำหนดประเภทเดียวกันในเนมสเปซเดียวกัน ระบบบิลด์ aidl ของ Android จะระบุปัญหานี้และแสดงข้อผิดพลาดพร้อมกับกราฟทรัพยากร Dependency แต่ละรายการที่ลงท้ายด้วยไลบรารีเวอร์ชันที่ไม่ตรงกัน
ซึ่งอาจทำให้การอัปเดตอินเทอร์เฟซทั่วไปเวอร์ชันหนึ่งเป็นเรื่องยากเมื่อโมดูลมีการขึ้นต่อกันหลายรายการซึ่งมีการขึ้นต่อกันของตัวเอง
นักพัฒนาแอปสามารถใช้ aidl_interface_defaults เพื่อประกาศทรัพยากร Dependency ของอินเทอร์เฟซที่ใช้ร่วมกันในอินเทอร์เฟซอื่นๆ เพื่อไม่ให้ต้องอัปเดตทั้งหมดแยกกัน
เราขอแนะนำให้ใช้โมดูล *_defaults (เช่น rust_defaults, cc_defaults, java_defaults) เพื่อจัดระเบียบทรัพยากร Dependency ในไลบรารีที่สร้างขึ้น โดยปกติแล้วจะมีค่าเริ่มต้นสำหรับอินเทอร์เฟซเวอร์ชัน latest รวมถึงค่าเริ่มต้นสำหรับเวอร์ชันก่อนหน้าหากยังมีการใช้งานอยู่
นักพัฒนาแอปสามารถใช้ aidl_interface_defaults เพื่อประกาศทรัพยากร Dependency ของอินเทอร์เฟซที่ใช้ร่วมกันในอินเทอร์เฟซอื่นๆ เพื่อไม่ให้ต้องอัปเดตทั้งหมดแยกกัน
หลักเกณฑ์การออกแบบ API
ทั่วไป
1. จัดทำเอกสารสำหรับทุกอย่าง
- จัดทำเอกสารทุกเมธอดสำหรับความหมาย อาร์กิวเมนต์ การใช้ข้อยกเว้นในตัว ข้อยกเว้นเฉพาะบริการ และค่าที่แสดงผล
- บันทึกอินเทอร์เฟซทุกรายการเพื่อดูความหมาย
- บันทึกความหมายเชิงความหมายของ enum และค่าคงที่
- บันทึกข้อมูลที่อาจไม่ชัดเจนสำหรับผู้ติดตั้งใช้งาน
- ยกตัวอย่างในส่วนที่เกี่ยวข้อง
2. ขอบหน้าต่าง
ใช้การเขียนแบบ Camel Case ตัวพิมพ์ใหญ่สำหรับประเภท และการเขียนแบบ Camel Case ตัวพิมพ์เล็กสำหรับเมธอด ฟิลด์ และอาร์กิวเมนต์ เช่น MyParcelable สำหรับประเภทที่แยกวิเคราะห์ได้ และ anArgument สำหรับอาร์กิวเมนต์ สำหรับคำย่อ ให้ถือว่าคำย่อเป็นคำ (NFC -> Nfc)
[-Wconst-name] ค่า Enum และค่าคงที่ควรเป็น ENUM_VALUE และ CONSTANT_NAME
3. หลีกเลี่ยงการกำหนดให้ต้องมีความรู้ทั่วไป
API ไม่ควรคาดเดาว่านักพัฒนาซอฟต์แวร์มีความรู้โดยรวมเกี่ยวกับฐานของโค้ดทั้งหมดหรือความเชี่ยวชาญเฉพาะด้าน เมื่อจัดการกับตัวระบุเฉพาะโดเมน (เช่น ชื่ออุปกรณ์ รหัส หรือแฮนเดิล) ให้ทำดังนี้
- ระบุอย่างชัดเจนและบันทึกว่าตัวระบุเหล่านี้มาจากที่ใดและมีรูปแบบอย่างไร หากทั้ง 2 ฝั่งของอินเทอร์เฟซจำเป็นต้องทราบข้อมูลดังกล่าว
- หรือใช้ตัวระบุเฉพาะอินเทอร์เฟซ (เช่น ออบเจ็กต์ Binder หรือโทเค็นที่กำหนดเอง) และให้ฝ่ายหนึ่งจัดการการแมปกับค่าที่เกี่ยวข้อง ซึ่งจะช่วยลดการชนกันและทำให้ผู้ใช้ไม่ต้องทำความเข้าใจรายละเอียดการติดตั้งใช้งานนอกพื้นที่ของตน
4. ข้อมูลทั้งหมดมีโครงสร้างและเข้ากันได้แบบย้อนหลัง
ข้อมูลที่ไม่มีโครงสร้าง เช่น string, byte[] และหน่วยความจำที่ใช้ร่วมกันต้องมีรูปแบบที่เสถียรสำหรับเนื้อหา หรือไม่เปิดเผยต่ออินเทอร์เฟซด้านใดด้านหนึ่ง
ตัวอย่างเช่น อาร์กิวเมนต์สตริงที่ใช้เป็นข้อความแสดงข้อผิดพลาดสำหรับผลลัพธ์สามารถรับและบันทึกเพื่อแก้ไขข้อบกพร่องได้ แต่ต้องไม่แยกวิเคราะห์และตีความเนื่องจากรูปแบบและเนื้อหาอาจไม่เข้ากันได้แบบย้อนหลัง หากอีกฝั่งของอินเทอร์เฟซจำเป็นต้องทราบว่าข้อผิดพลาดคืออะไรในรันไทม์ ให้ใช้ enum, ค่าคงที่ หรือ ServiceSpecificException
ในทำนองเดียวกัน อย่าแปลงออบเจ็กต์เป็นอนุกรมใน byte[] หรือหน่วยความจำที่ใช้ร่วมกัน เว้นแต่ว่าออบเจ็กต์เหล่านั้นจะเสถียรและเข้ากันได้แบบย้อนหลัง ในบางกรณี คุณสามารถใช้คำอธิบายประกอบ @FixedSize สำหรับการแชร์ Parcelable และ Union ในหน่วยความจำที่ใช้ร่วมกันและคิวข้อความด่วน
อินเทอร์เฟซ
1. การตั้งชื่อ
[-Winterface-name] ชื่ออินเทอร์เฟซควรขึ้นต้นด้วย I เช่น IFoo
2. หลีกเลี่ยงอินเทอร์เฟซขนาดใหญ่ที่มี "ออบเจ็กต์" ที่อิงตามรหัส
แนะนำให้ใช้อินเทอร์เฟซย่อยเมื่อมีการเรียกใช้จำนวนมากที่เกี่ยวข้องกับ API ที่เฉพาะเจาะจง ซึ่งมีประโยชน์ดังนี้
- ทำให้โค้ดฝั่งไคลเอ็นต์หรือเซิร์ฟเวอร์เข้าใจง่ายขึ้น
- ทำให้วงจรชีวิตของออบเจ็กต์ง่ายขึ้น
- ใช้ประโยชน์จาก Binder ที่ปลอมแปลงไม่ได้
ไม่แนะนำ: อินเทอร์เฟซขนาดใหญ่รายการเดียวที่มีออบเจ็กต์ตามรหัส
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. อย่าผสมผสานวิธีการแบบทางเดียวกับแบบ 2 ทาง
[-Wmixed-oneway] อย่าผสมเมธอดแบบทางเดียวกับแบบไม่ใช่ทางเดียว เนื่องจากจะทำให้การทำความเข้าใจโมเดลการแยกชุดข้อความมีความซับซ้อนสำหรับไคลเอ็นต์และเซิร์ฟเวอร์ โดยเฉพาะอย่างยิ่ง เมื่ออ่านโค้ดไคลเอ็นต์ของอินเทอร์เฟซหนึ่งๆ คุณต้องค้นหาแต่ละเมธอดว่าเมธอดนั้นจะบล็อกหรือไม่
4. หลีกเลี่ยงการแสดงรหัสสถานะ
เมธอดควรหลีกเลี่ยงการใช้รหัสสถานะเป็นค่าที่แสดงผล เนื่องจากเมธอด AIDL ทั้งหมดมีรหัสแสดงผลสถานะโดยนัย ดูServiceSpecificExceptionหรือEX_SERVICE_SPECIFIC ตามแบบแผน ค่าเหล่านี้จะกำหนดเป็นค่าคงที่ในอินเทอร์เฟซ AIDL หากจำเป็นต้องใช้การหน่วงเวลาที่กำหนดเองหรือข้อมูลข้อผิดพลาดที่ไม่ซ้ำกันควบคู่ไปกับข้อผิดพลาด นั่นเป็นเพียงครั้งเดียวที่ออบเจ็กต์การตอบกลับที่กำหนดเองควรแสดงข้อผิดพลาด ดูข้อมูลโดยละเอียดเพิ่มเติมได้ที่การจัดการข้อผิดพลาด
5. อาร์เรย์เป็นพารามิเตอร์เอาต์พุตที่ถือว่าเป็นอันตราย
[-Wout-array] เมธอดที่มีพารามิเตอร์เอาต์พุตอาร์เรย์ เช่น
void foo(out String[] ret) มักจะไม่ดีเนื่องจากต้องประกาศและจัดสรรขนาดอาร์เรย์เอาต์พุตโดยไคลเอ็นต์ใน Java ดังนั้นเซิร์ฟเวอร์จึงเลือกขนาดของเอาต์พุตอาร์เรย์ไม่ได้ ลักษณะการทำงานที่ไม่พึงประสงค์นี้เกิดขึ้นเนื่องจากวิธีการทำงานของอาร์เรย์ใน Java (ไม่สามารถจัดสรรใหม่ได้) แต่ควรใช้ API เช่น String[] foo() แทน
6. หลีกเลี่ยงพารามิเตอร์ inout
[-Winout-parameter] This can confuse clients because even in parameters look
like out parameters.
7. หลีกเลี่ยงพารามิเตอร์ที่ไม่ใช่อาร์เรย์ @nullable แบบ out และ inout
[-Wout-nullable] เนื่องจากแบ็กเอนด์ Java ไม่จัดการคำอธิบายประกอบ @nullable ขณะที่แบ็กเอนด์อื่นๆ จัดการ out/inout @nullable T อาจทำให้เกิดลักษณะการทำงานที่ไม่สอดคล้องกันในแบ็กเอนด์ต่างๆ เช่น แบ็กเอนด์ที่ไม่ใช่ Java สามารถตั้งค่าพารามิเตอร์ @nullable เป็น null ได้ (ใน C++ ให้ตั้งค่าเป็น std::nullopt) แต่ไคลเอ็นต์ Java จะอ่านค่าเป็น null ไม่ได้
8. ใช้คำขอและการตอบกลับที่ไม่ซ้ำกัน
จัดกลุ่มพารามิเตอร์ที่จำเป็นทั้งหมดไว้ในอินพุต parcelable เดียว
สร้าง Parcelable สำหรับคำขอและการตอบกลับโดยเฉพาะสำหรับเมธอดอินเทอร์เฟซทุกเมธอดแทนการส่งผ่านค่าพื้นฐาน (เช่น ใช้ ComputeResponse compute(in ComputeRequest request) แทนการส่งผ่านตัวแปรแยกต่างหาก) ซึ่งจะช่วยให้เพิ่มอาร์กิวเมนต์ใหม่ในภายหลังได้โดยไม่ต้องเปลี่ยนซิกเนเจอร์ของฟังก์ชัน เราขอแนะนำให้ใช้รูปแบบนี้อย่างยิ่งเมื่อคาดว่าอาจมีการเพิ่มพารามิเตอร์อื่นๆ ในอนาคต หรือหากเมธอดมีพารามิเตอร์มากกว่า 4 รายการอยู่แล้ว
เมธอดที่ไม่ต้องใช้อินพุตหรือเอาต์พุตเพิ่มเติมจะไม่ได้รับประโยชน์จากคำแนะนำนี้ การพิจารณาแต่ละกรณีอย่างชัดเจนและยืดหยุ่นต่อการเปลี่ยนแปลงในอนาคตจะช่วยลดจำนวนเมธอดที่เลิกใช้งานและความซับซ้อนของโค้ดที่เข้ากันได้แบบย้อนหลัง
หากไม่ได้สร้างเมธอดโดยใช้รูปแบบนี้ คุณสามารถเปลี่ยนไปใช้รูปแบบนี้ได้โดยสร้างเมธอดใหม่ที่มี Parcelable ของคำขอและการตอบกลับ และเลิกใช้งานเมธอดเก่า เช่น
void foo(int a, int b, int c); // original version, but deprecated in favor of the next version
void fooV2(in MyArg arg); // new version having int a, b, c, and d.
Parcelable ที่มีโครงสร้าง
1. กรณีที่ควรใช้
ใช้ Parcelable ที่มีโครงสร้างเมื่อคุณมีข้อมูลหลายประเภทที่ต้องการส่ง
หรือเมื่อคุณมีข้อมูลประเภทเดียว แต่คาดว่าจะต้องขยายข้อมูลในอนาคต เช่น อย่าใช้ String username ใช้ Parcelable ที่ขยายได้ เช่น ตัวอย่างต่อไปนี้
parcelable User {
String username;
}
เพื่อให้คุณขยายเวลาได้ในอนาคต ดังนี้
parcelable User {
String username;
int id;
}
2. ระบุค่าเริ่มต้นอย่างชัดเจน
[-Wexplicit-default, -Wenum-explicit-default] ระบุค่าเริ่มต้นที่ชัดเจนสำหรับฟิลด์ เมื่อมีการเพิ่มช่องใหม่ลงใน Parcelable ไคลเอ็นต์และเซิร์ฟเวอร์เก่าจะทิ้งช่องเหล่านั้น แต่ค่าเริ่มต้นจะป้อนลงในไคลเอ็นต์และเซิร์ฟเวอร์ใหม่โดยอัตโนมัติ
3. ใช้ ParcelableHolder สำหรับส่วนขยายของผู้ให้บริการ
หากคุณกำหนด parcelable ของ AOSP ที่ผู้ติดตั้งอุปกรณ์ต้องขยาย ให้ฝังอินสแตนซ์ของ ParcelableHolder ในออบเจ็กต์ ซึ่งจะทำหน้าที่เป็นจุดขยายโดยไม่สร้างความขัดแย้งในการผสานรวม ซึ่งคล้ายกับส่วนขยายอินเทอร์เฟซที่แนบมา แต่ช่วยให้ผู้ติดตั้งใช้งานสามารถรวม parcelable ที่เป็นกรรมสิทธิ์ของตนไว้ข้างๆ parcelable ที่มีอยู่ได้โดยไม่ต้องสร้างอินเทอร์เฟซและประเภทของตนเอง
4. โครงสร้างข้อมูล
- ใช้อาร์เรย์หรือ
Listของ Parcelable เพื่อแสดงแผนที่ เนื่องจาก AIDL ไม่รองรับ ประเภทMapโดยค่าเริ่มต้นที่แปลอย่างปลอดภัยในแบ็กเอนด์แบบเนทีฟทั้งหมด (เช่นFeatureToScoreEntry[]) - ใช้อาร์เรย์ของออบเจ็กต์
parcelableสำหรับฟิลด์ที่ซ้ำได้แทนอาร์เรย์ของค่าพื้นฐาน เพื่อป้องกันไม่ให้ต้องใช้อาร์เรย์แบบขนานในอนาคต - ใช้ออบเจ็กต์
parcelableที่มีการระบุประเภทอย่างชัดเจนแทนสตริงที่เรียงลำดับหรือ JSON ผ่าน IPC - ใช้ enum แทนบูลีนสำหรับสถานะเพื่อให้ขยายได้ในอนาคต สำหรับบิตมาสก์ ให้ใช้ประเภท
const intแทนenumเพื่อหลีกเลี่ยงการแคสต์ที่ยุ่งยากในแบ็กเอนด์บางรายการ
Parcelable ที่ไม่มีโครงสร้าง
1. กรณีที่ควรใช้
Parcelable ที่ไม่มีโครงสร้างพร้อมใช้งานใน Java ด้วย @JavaOnlyStableParcelable และในแบ็กเอนด์ NDK ด้วย @NdkOnlyStableParcelable โดยปกติแล้ว ข้อมูลเหล่านี้จะเป็นข้อมูลเก่าและมีอยู่แล้วซึ่งไม่สามารถจัดโครงสร้างได้
ค่าคงที่และ Enum
1. Bitfield ควรใช้ช่องค่าคงที่
บิตฟิลด์ควรใช้ฟิลด์ค่าคงที่ (เช่น const int FOO = 3; ในอินเทอร์เฟซ)
2. Enum ควรเป็นชุดปิด
Enum ควรเป็นชุดปิด หมายเหตุ: มีเพียงเจ้าของอินเทอร์เฟซเท่านั้นที่เพิ่มองค์ประกอบ enum ได้ หากผู้ให้บริการหรือ OEM ต้องการขยายช่องเหล่านี้ ก็จำเป็นต้องใช้กลไกอื่น หากเป็นไปได้ ควรเลือกใช้ฟังก์ชันการทำงานของผู้ให้บริการต้นทาง อย่างไรก็ตาม ในบางกรณี ระบบอาจอนุญาตให้ใช้ค่าของผู้ให้บริการที่กำหนดเอง (แม้ว่าผู้ให้บริการควรมีกลไกในการกำหนดเวอร์ชัน เช่น AIDL เอง แต่ก็ไม่ควรขัดแย้งกัน และไม่ควรเปิดเผยค่าเหล่านี้แก่แอปของบุคคลที่สาม)
3. หลีกเลี่ยงค่าอย่าง "NUM_ELEMENTS"
เนื่องจาก enum มีการกำหนดเวอร์ชัน จึงควรหลีกเลี่ยงค่าที่ระบุจำนวนค่าที่มีอยู่ ใน 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 โดยตรง
enum MyStatus {
GOOD,
BAD
}
FileDescriptor
[-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;
การทำงานพร้อมกันและการดำเนินการแบบอะซิงโครนัส
จัดการการดำเนินการที่ใช้เวลานานด้วยอินเทอร์เฟซแบบอะซิงโครนัส (oneway) เพื่อหลีกเลี่ยงการบล็อก
หากบริการไม่เชื่อถือไคลเอ็นต์ Callback ใดๆ ที่ได้รับจากไคลเอ็นต์ควรเป็นอินเทอร์เฟซ oneway ซึ่งจะป้องกันไม่ให้ไคลเอ็นต์บล็อกบริการอย่างไม่มีกำหนด
จัดโครงสร้าง API แบบอะซิงโครนัสซึ่งประกอบด้วยการเรียกใช้แบบส่งต่อ อาร์กิวเมนต์อินพุต และอินเทอร์เฟซ Callback เพื่อรับผลลัพธ์ ดูใช้คำขอและการตอบกลับที่ไม่ซ้ำกันเพื่อดูคำแนะนำสำหรับอาร์กิวเมนต์