หน้านี้แสดงแนวทางปฏิบัติแนะนำและคำแนะนำเกี่ยวกับสถาปัตยกรรมหลายข้อ นำไปใช้เพื่อปรับปรุงคุณภาพ ความแข็งแกร่ง และความสามารถในการปรับขนาดของแอป นอกจากนี้ ยังช่วยให้คุณดูแลรักษาและทดสอบแอปได้ง่ายขึ้นด้วย
แนวทางปฏิบัติแนะนำด้านล่างจัดกลุ่มตามหัวข้อ แต่ละรายการมีลำดับความสำคัญที่แสดง ความสำคัญของคำแนะนำ โดยลำดับความสำคัญมีดังนี้
- ขอแนะนำอย่างยิ่ง: ใช้แนวทางปฏิบัตินี้ เว้นแต่จะขัดแย้งกับแนวทางของคุณโดยสิ้นเชิง
- แนะนำ: แนวทางปฏิบัตินี้มีแนวโน้มที่จะปรับปรุงแอป
- ไม่บังคับ: แนวทางปฏิบัตินี้จะช่วยปรับปรุงแอปของคุณได้ในบางกรณี
สถาปัตยกรรมแบบเลเยอร์
สถาปัตยกรรมแบบเลเยอร์ที่เราแนะนำจะเน้นการแยกความกังวล ซึ่งขับเคลื่อน UI จากโมเดลข้อมูล ปฏิบัติตามหลักการแหล่งข้อมูลที่ถูกต้องแห่งเดียว และปฏิบัติตามหลักการการไหลของข้อมูลแบบทิศทางเดียว แนวทางปฏิบัติแนะนำสำหรับสถาปัตยกรรมแบบเลเยอร์มีดังนี้
| คำแนะนำ | คำอธิบาย |
|---|---|
| ใช้ชั้นข้อมูลที่กำหนดไว้อย่างชัดเจน
ขอแนะนำ |
เลเยอร์ข้อมูลจะแสดงข้อมูลแอปพลิเคชันต่อส่วนอื่นๆ ของแอป และมีตรรกะทางธุรกิจส่วนใหญ่ของแอป
|
| ใช้เลเยอร์ UI ที่กำหนดไว้อย่างชัดเจน
ขอแนะนำ |
เลเยอร์ UI จะแสดงข้อมูลแอปพลิเคชันบนหน้าจอและเป็นจุดหลักที่ผู้ใช้โต้ตอบ Jetpack Compose เป็นชุดเครื่องมือสมัยใหม่ที่แนะนำสำหรับการสร้าง UI ของแอป
|
| แสดงข้อมูลแอปพลิเคชันจากชั้นข้อมูลโดยใช้ที่เก็บ
ขอแนะนำ |
ตรวจสอบว่าคอมโพเนนต์ในเลเยอร์ UI เช่น Composable หรือ ViewModel ไม่โต้ตอบกับแหล่งข้อมูลโดยตรง ตัวอย่างแหล่งข้อมูล ได้แก่
|
| ใช้โครูทีนและโฟลว์
ขอแนะนำ |
ใช้โครูทีนและโฟลว์เพื่อสื่อสารระหว่างเลเยอร์
ดูข้อมูลเพิ่มเติมเกี่ยวกับแนวทางปฏิบัติแนะนำสำหรับโครูทีนได้ที่แนวทางปฏิบัติแนะนำสำหรับโครูทีนใน Android |
| ใช้เลเยอร์โดเมน
แนะนำในแอปขนาดใหญ่ |
ใช้เลเยอร์โดเมนกับกรณีการใช้งานหากต้องการนำตรรกะทางธุรกิจที่โต้ตอบกับเลเยอร์ข้อมูลใน ViewModel หลายรายการกลับมาใช้ซ้ำ หรือต้องการลดความซับซ้อนของตรรกะทางธุรกิจของ ViewModel ที่เฉพาะเจาะจง |
เลเยอร์ UI
บทบาทของเลเยอร์ UI คือการแสดงข้อมูลแอปพลิเคชันบนหน้าจอ และเป็นจุดหลักของการโต้ตอบของผู้ใช้ แนวทางปฏิบัติแนะนำสำหรับเลเยอร์ UI มีดังนี้
| คำแนะนำ | คำอธิบาย |
|---|---|
| ทำตามการไหลของข้อมูลแบบทางเดียว (UDF)
ขอแนะนำ |
ปฏิบัติตามหลักการการไหลของข้อมูลแบบทิศทางเดียว (UDF) ซึ่ง ViewModel จะแสดงสถานะ UI โดยใช้รูปแบบ Observer และรับการดำเนินการจาก UI ผ่านการเรียกใช้เมธอด |
| ใช้ AAC ViewModels หากประโยชน์ของ ViewModel นั้นๆ เหมาะกับแอปของคุณ
ขอแนะนำ |
ใช้ AAC ViewModels เพื่อจัดการตรรกะทางธุรกิจ และดึงข้อมูลแอปพลิเคชันเพื่อแสดงสถานะ UI ต่อ UI
ดูข้อมูลเพิ่มเติมเกี่ยวกับแนวทางปฏิบัติแนะนำของ ViewModel ได้ที่คำแนะนำด้านสถาปัตยกรรม ดูข้อมูลเพิ่มเติมเกี่ยวกับประโยชน์ของ ViewModel ได้ที่ViewModel ในฐานะที่เก็บสถานะของตรรกะทางธุรกิจ |
| ใช้การรวบรวมสถานะ UI ที่รับรู้ถึงวงจร
ขอแนะนำ |
รวบรวมสถานะ UI จาก UI โดยใช้ตัวสร้างโครูทีนที่รับรู้ถึงวงจรที่เหมาะสม collectAsStateWithLifecycle
ดูข้อมูลเพิ่มเติมเกี่ยวกับ |
| อย่าส่งเหตุการณ์จาก ViewModel ไปยัง UI
ขอแนะนำ |
ประมวลผลเหตุการณ์ใน ViewModel ทันทีและทําให้เกิดการอัปเดตสถานะด้วยผลลัพธ์ของการจัดการเหตุการณ์ ดูข้อมูลเพิ่มเติมเกี่ยวกับเหตุการณ์ UI ได้ที่จัดการเหตุการณ์ ViewModel |
| ใช้แอปพลิเคชันแบบกิจกรรมเดียว
ขอแนะนำ |
ใช้การนำทาง 3 เพื่อไปยังหน้าจอต่างๆ และ Deep Link ไปยังแอปหากแอปมีมากกว่า 1 หน้าจอ |
| ใช้ Jetpack Compose
ขอแนะนำ |
ใช้ Jetpack Compose เพื่อสร้างแอปใหม่สำหรับโทรศัพท์ แท็บเล็ต อุปกรณ์พับได้ และ Wear OS |
ข้อมูลโค้ดต่อไปนี้แสดงวิธีรวบรวมสถานะ UI ในลักษณะที่รับรู้ถึงวงจร
@Composable
fun MyScreen(
viewModel: MyViewModel = viewModel()
) {
val uiState by viewModel.uiState.collectAsStateWithLifecycle()
}
ViewModel
ViewModel มีหน้าที่จัดเตรียมสถานะ UI และเข้าถึงเลเยอร์ข้อมูล แนวทางปฏิบัติแนะนำบางส่วนสำหรับ ViewModel มีดังนี้
| คำแนะนำ | คำอธิบาย |
|---|---|
| ทำให้ ViewModel ไม่ขึ้นอยู่กับวงจรของ Android
ขอแนะนำ |
ใน ViewModel อย่าเก็บการอ้างอิงถึงประเภทใดๆ ที่เกี่ยวข้องกับวงจร อย่าส่ง Activity, Context หรือ Resources เป็นทรัพยากร Dependency
หากมีบางอย่างที่ต้องใช้ Context ใน ViewModel ให้ประเมินอย่างรอบคอบว่าอยู่ในเลเยอร์ที่ถูกต้องหรือไม่ |
| ใช้โครูทีนและโฟลว์
ขอแนะนำ |
ViewModel โต้ตอบกับเลเยอร์ข้อมูลหรือโดเมนโดยใช้สิ่งต่อไปนี้
|
| ใช้ ViewModel ที่ระดับหน้าจอ
ขอแนะนำ |
อย่าใช้ ViewModel ในชิ้นส่วน UI ที่นำกลับมาใช้ใหม่ได้ คุณควรใช้ ViewModel ในส่วนต่อไปนี้
|
| ใช้คลาสตัวเก็บสถานะธรรมดาในคอมโพเนนต์ UI ที่นำกลับมาใช้ใหม่ได้
ขอแนะนำ |
ใช้คลาสตัวเก็บสถานะธรรมดาเพื่อจัดการความซับซ้อนในคอมโพเนนต์ UI ที่นำกลับมาใช้ใหม่ได้ เมื่อทำเช่นนี้ คุณจะยกสถานะและควบคุมภายนอกได้ |
อย่าใช้ AndroidViewModel
แนะนำ |
ใช้คลาส ViewModel ไม่ใช่ AndroidViewModel อย่าใช้คลาส Application ใน ViewModel แต่ให้ย้ายการอ้างอิงไปยัง UI หรือชั้นข้อมูลแทน |
| แสดงสถานะ UI
แนะนำ |
ทำให้ ViewModel แสดงข้อมูลต่อ UI ผ่านพร็อพเพอร์ตี้เดียวที่ชื่อ uiState หาก UI แสดงข้อมูลหลายส่วนที่ไม่เกี่ยวข้อง VM จะแสดงพร็อพเพอร์ตี้สถานะ UI หลายรายการได้
|
ข้อมูลโค้ดต่อไปนี้แสดงวิธีเปิดเผยสถานะ UI จาก ViewModel
@HiltViewModel
class BookmarksViewModel @Inject constructor(
newsRepository: NewsRepository
) : ViewModel() {
val feedState: StateFlow<NewsFeedUiState> =
newsRepository
.getNewsResourcesStream()
.mapToFeedState(savedNewsResourcesState)
.stateIn(
scope = viewModelScope,
started = SharingStarted.WhileSubscribed(5_000),
initialValue = NewsFeedUiState.Loading
)
// ...
}
วงจร
ทำตามแนวทางปฏิบัติแนะนำสำหรับการทำงานกับวงจร กิจกรรม
| คำแนะนำ | คำอธิบาย |
|---|---|
ใช้เอฟเฟกต์ที่รับรู้ถึงวงจรใน Composable แทนการลบล้างโค้ดเรียกกลับของวงจร Activity
ขอแนะนำ |
อย่าลบล้าง
|
ข้อมูลโค้ดต่อไปนี้แสดงวิธีดำเนินการเมื่อมีสถานะ Lifecycle ที่เฉพาะเจาะจง
@Composable
fun LocationChangedEffect(
locationManager: LocationManager,
onLocationChanged: (Location) -> Unit
) {
val currentOnLocationChanged by rememberUpdatedState(onLocationChanged)
LifecycleStartEffect(locationManager) {
val listener = LocationListener { newLocation ->
currentOnLocationChanged(newLocation)
}
try {
locationManager.requestLocationUpdates(
LocationManager.GPS_PROVIDER,
1000L,
1f,
listener,
)
} catch (e: SecurityException) {
// TODO: Handle missing permissions
}
onStopOrDispose {
locationManager.removeUpdates(listener)
}
}
}
จัดการทรัพยากร Dependency
ทำตามแนวทางปฏิบัติแนะนำเมื่อจัดการการขึ้นต่อกัน ระหว่างคอมโพเนนต์
| คำแนะนำ | คำอธิบาย |
|---|---|
| ใช้การแทรกการอ้างอิง
ขอแนะนำ |
ใช้แนวทางปฏิบัติแนะนำเกี่ยวกับการแทรกทรัพยากร Dependency โดยหลักๆ คือการแทรกเครื่องมือสร้างเมื่อเป็นไปได้ |
| กำหนดขอบเขตไปยังคอมโพเนนต์เมื่อจำเป็น
ขอแนะนำ |
กำหนดขอบเขตเป็นคอนเทนเนอร์การอ้างอิงเมื่อประเภทมีข้อมูลที่เปลี่ยนแปลงได้ซึ่งต้องแชร์ หรือประเภทมีค่าใช้จ่ายสูงในการเริ่มต้นและใช้กันอย่างแพร่หลายในแอป |
| ใช้ Hilt
แนะนำ |
ใช้ Hilt หรือการแทรกทรัพยากร Dependency ด้วยตนเองในแอปที่เรียบง่าย ใช้ Hilt หากโปรเจ็กต์มีความซับซ้อนมากพอ เช่น หากมีสิ่งต่อไปนี้
|
การทดสอบ
ต่อไปนี้คือแนวทางปฏิบัติแนะนำบางส่วนสำหรับการทดสอบ
| คำแนะนำ | คำอธิบาย |
|---|---|
| รู้ว่าควรทดสอบอะไร
ขอแนะนำ |
ทดสอบโปรเจ็กต์ เว้นแต่ว่าโปรเจ็กต์จะเป็นแอปง่ายๆ อย่าง "hello world" โดยควรมีข้อมูลต่อไปนี้เป็นอย่างน้อย
|
| ใช้ Fake แทน Mock
ขอแนะนำ |
ดูข้อมูลเพิ่มเติมเกี่ยวกับการใช้ข้อมูลจำลองได้ที่ใช้การทดสอบแบบคู่ใน Android |
| ทดสอบ StateFlow
ขอแนะนำ |
เมื่อทดสอบ StateFlow ให้ทำดังนี้
|
ดูข้อมูลเพิ่มเติมได้ที่สิ่งที่ควรทดสอบใน Android และทดสอบเลย์เอาต์ Compose
โมเดล
โปรดปฏิบัติตามแนวทางปฏิบัติแนะนำต่อไปนี้เมื่อพัฒนาโมเดลในแอป
| คำแนะนำ | คำอธิบาย |
|---|---|
| สร้างโมเดลต่อเลเยอร์ในแอปที่ซับซ้อน
แนะนำ |
ในแอปที่ซับซ้อน ให้สร้างโมเดลใหม่ในเลเยอร์หรือคอมโพเนนต์ต่างๆ เมื่อเหมาะสม ลองดูตัวอย่างต่อไปนี้
|
รูปแบบการตั้งชื่อ
เมื่อตั้งชื่อฐานของโค้ด คุณควรทราบแนวทางปฏิบัติแนะนำต่อไปนี้
| คำแนะนำ | คำอธิบาย |
|---|---|
| วิธีการตั้งชื่อ
ไม่บังคับ |
ใช้วลีที่มีคำกริยาเพื่อตั้งชื่อเมธอด เช่น makePayment() |
| การตั้งชื่อพร็อพเพอร์ตี้
ไม่บังคับ |
ใช้กลุ่มคำนามเพื่อตั้งชื่อพร็อพเพอร์ตี้ เช่น inProgressTopicSelection |
| การตั้งชื่อสตรีมข้อมูล
ไม่บังคับ |
เมื่อคลาสแสดงสตรีมโฟลว์หรือสตรีมอื่นๆ รูปแบบการตั้งชื่อจะเป็น get{model}Stream เช่น getAuthorStream(): Flow<Author>
หากฟังก์ชันแสดงผลรายการโมเดล ให้ใช้ชื่อโมเดลแบบพหูพจน์: getAuthorsStream(): Flow<List<Author>> |
| การตั้งชื่อการติดตั้งใช้งานอินเทอร์เฟซ
ไม่บังคับ |
ใช้ชื่อที่มีความหมายสำหรับการติดตั้งใช้งานอินเทอร์เฟซ ใช้ Default เป็นคำนำหน้าหากไม่พบชื่อที่ดีกว่า เช่น สำหรับอินเทอร์เฟซ NewsRepository คุณอาจมี OfflineFirstNewsRepository หรือ InMemoryNewsRepository หากไม่พบชื่อที่เหมาะสม ให้ใช้ DefaultNewsRepository
นำหน้าการใช้งานปลอมด้วย Fake เช่น FakeAuthorsRepository |
แหล่งข้อมูลเพิ่มเติม
ดูข้อมูลเพิ่มเติมเกี่ยวกับสถาปัตยกรรมของ Android ได้ที่แหล่งข้อมูลเพิ่มเติมต่อไปนี้