หน้านี้นำเสนอแนวทางปฏิบัติที่ดีที่สุดและคำแนะนำมากมายเกี่ยวกับสถาปัตยกรรม นําไปใช้เพื่อปรับปรุงคุณภาพ ความทนทาน และความสามารถในการปรับขนาดของแอป และ ช่วยให้ดูแลและทดสอบแอปได้ง่ายขึ้น
แนวทางปฏิบัติแนะนำด้านล่างจัดกลุ่มตามหัวข้อ แต่ละแบบมีลำดับความสำคัญที่ กับทีมนั้นแนะนำอย่างจริงจังเพียงใด รายการลำดับความสำคัญมีดังต่อไปนี้
- แนะนําอย่างยิ่ง: คุณควรทําตามแนวทางปฏิบัตินี้ เว้นแต่จะเกิดการชนกัน กับแนวทางของคุณเป็นหลัก
- แนะนำ: วิธีนี้น่าจะช่วยปรับปรุงแอปได้
- ไม่บังคับ: วิธีนี้ช่วยปรับปรุงแอปได้ในบางสถานการณ์
สถาปัตยกรรมแบบเลเยอร์
สถาปัตยกรรมแบบเลเยอร์ที่แนะนำของเราเพื่อแยกข้อกังวลได้ ทั้งนี้ UI จากโมเดลข้อมูลเป็นไปตาม แหล่งที่มาที่ถูกต้องแห่งเดียว และทำตามหลักการการถ่ายโอนข้อมูลที่ไปในทิศทางเดียวกัน นี่เป็นตัวอย่างที่ดีที่สุด แนวทางปฏิบัติสำหรับสถาปัตยกรรมเลเยอร์
คำแนะนำ | คำอธิบาย |
---|---|
ใช้ชั้นข้อมูลที่กำหนดไว้อย่างชัดเจน
แนะนำอย่างยิ่ง |
ชั้นข้อมูลจะเปิดเผยข้อมูลแอปพลิเคชันส่วนที่เหลือของแอปและประกอบด้วยตรรกะทางธุรกิจส่วนใหญ่ของแอป
|
ใช้เลเยอร์ UI ที่กำหนดไว้อย่างชัดเจน
แนะนำอย่างยิ่ง |
เลเยอร์ UI จะแสดงข้อมูลแอปพลิเคชันบนหน้าจอและทำหน้าที่เป็นจุดหลักของการโต้ตอบของผู้ใช้
|
ชั้นข้อมูลควรเปิดเผยข้อมูลแอปพลิเคชันโดยใช้ที่เก็บ
แนะนำอย่างยิ่ง |
คอมโพเนนต์ในเลเยอร์ UI เช่น Composables, กิจกรรม หรือ ViewModels ไม่ควรโต้ตอบกับแหล่งข้อมูลโดยตรง ตัวอย่างแหล่งข้อมูลมีดังนี้
|
ใช้โครูทีนและโฟลว์
แนะนำอย่างยิ่ง |
ใช้โครูทีนและโฟลว์เพื่อสื่อสารระหว่างเลเยอร์ |
ให้ใช้เลเยอร์โดเมน
แนะนำในแอปขนาดใหญ่ |
ใช้เลเยอร์โดเมนกรณีการใช้งาน หากคุณต้องการนำตรรกะทางธุรกิจที่โต้ตอบกับชั้นข้อมูลใน ViewModel หลายรายการมาใช้ซ้ำ หรือหากต้องการลดความซับซ้อนของตรรกะทางธุรกิจของ ViewModel บางรายการ |
เลเยอร์ UI
บทบาทของเลเยอร์ UI คือการแสดงข้อมูลแอปพลิเคชันบนหน้าจอ และทำหน้าที่เป็นจุดหลักของการโต้ตอบของผู้ใช้ ตัวอย่างแนวทางปฏิบัติแนะนำมีดังนี้ สำหรับเลเยอร์ UI ดังนี้
คำแนะนำ | คำอธิบาย |
---|---|
โปรดทำตาม Unidirectional Data Flow (UDF)
แนะนำอย่างยิ่ง |
ทำตามหลักการ Unidirectional Data Flow (UDF) โดย ViewModels แสดงสถานะ UI โดยใช้รูปแบบผู้สังเกตการณ์และรับการดำเนินการจาก UI ผ่านการเรียกใช้เมธอด |
ใช้ AAC ViewModels หากแอปของคุณมีประโยชน์ต่อแอปของคุณ
แนะนำอย่างยิ่ง |
ใช้ AAC ViewModels เพื่อจัดการตรรกะทางธุรกิจและดึงข้อมูลแอปพลิเคชันเพื่อแสดงสถานะ UI ใน UI (เขียนหรือมุมมอง Android) |
ใช้การรวบรวมสถานะ UI ที่คำนึงถึงอายุการใช้งาน
แนะนำอย่างยิ่ง |
รวบรวมสถานะ UI จาก UI โดยใช้เครื่องมือสร้างโครูทีนที่รับรู้ตลอดอายุการใช้งานที่เหมาะสม ซึ่งได้แก่ repeatOnLifecycle ในระบบมุมมองและ collectAsStateWithLifecycle ใน Jetpack Compose
อ่านเพิ่มเติมเกี่ยวกับ อ่านเพิ่มเติมเกี่ยวกับ |
อย่าส่งเหตุการณ์จาก ViewModel ไปยัง UI
แนะนำอย่างยิ่ง |
ประมวลผลเหตุการณ์ทันทีใน ViewModel และสร้างการอัปเดตสถานะโดยเป็นผลมาจากการจัดการเหตุการณ์ ดูข้อมูลเพิ่มเติมเกี่ยวกับเหตุการณ์ UI ที่นี่ |
ใช้แอปพลิเคชันกิจกรรมเดียว
แนะนำ |
ใช้ส่วนย่อยการนำทางหรือการเขียนการนำทางเพื่อนำทางระหว่างหน้าจอและลิงก์ในรายละเอียดไปยังแอปของคุณ หากแอปมีมากกว่า 1 หน้าจอ |
ใช้ Jetpack Compose
แนะนำ |
ใช้ Jetpack Compose เพื่อสร้างแอปใหม่ๆ สำหรับโทรศัพท์ แท็บเล็ต อุปกรณ์แบบพับได้ รวมถึง Wear OS |
ข้อมูลโค้ดต่อไปนี้สรุปวิธีรวบรวมสถานะ UI ตามวงจร ลักษณะ:
ยอดดู
class MyFragment : Fragment() {
private val viewModel: MyViewModel by viewModel()
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
viewLifecycleOwner.lifecycleScope.launch {
viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
viewModel.uiState.collect {
// Process item
}
}
}
}
}
เขียน
@Composable
fun MyScreen(
viewModel: MyViewModel = viewModel()
) {
val uiState by viewModel.uiState.collectAsStateWithLifecycle()
}
ViewModel
ViewModels รับผิดชอบในการระบุสถานะ UI และการเข้าถึง ของ Google Analytics แนวทางปฏิบัติแนะนำสำหรับ ViewModels มีดังนี้
คำแนะนำ | คำอธิบาย |
---|---|
ViewModels ไม่เกี่ยวข้องกับวงจรชีวิตของ Android
แนะนำอย่างยิ่ง |
ViewModels ไม่ควรอ้างอิงถึงประเภทใดๆ ที่เกี่ยวข้องกับวงจร อย่าส่ง Activity, Fragment, Context หรือ Resources เป็นทรัพยากร Dependency
หากมีบางอย่างต้องมี Context ใน ViewModel คุณควรประเมินอย่างเคร่งครัดว่าข้อมูลนั้นอยู่ในเลเยอร์ที่ถูกต้องหรือไม่ |
ใช้โครูทีนและโฟลว์
แนะนำอย่างยิ่ง |
ViewModel โต้ตอบกับเลเยอร์ข้อมูลหรือโดเมนโดยใช้สิ่งต่อไปนี้
|
ใช้ ViewModels ที่ระดับหน้าจอ
แนะนำอย่างยิ่ง |
อย่าใช้ ViewModels ใน UI ที่ใช้ซ้ำได้ คุณควรใช้ ViewModels ใน
|
ใช้คลาสผู้ถือสถานะปกติในคอมโพเนนต์ UI ที่นำมาใช้ใหม่ได้
แนะนำอย่างยิ่ง |
ใช้คลาสผู้ถือสถานะปกติเพื่อจัดการความซับซ้อนในคอมโพเนนต์ UI ที่นำมาใช้ใหม่ได้ การทำเช่นนี้จะทำให้สามารถเคลื่อนย้ายและควบคุมสถานะได้จากภายนอก |
อย่าใช้ AndroidViewModel
แนะนำ |
ใช้ชั้นเรียน ViewModel ไม่ใช่ AndroidViewModel ไม่ควรใช้คลาส Application ใน ViewModel แต่ให้ย้ายทรัพยากร Dependency ไปยัง UI หรือชั้นข้อมูลแทน |
แสดงสถานะ UI
แนะนำ |
ViewModels ควรแสดงข้อมูลไปยัง 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
)
// ...
}
วงจร
ต่อไปนี้เป็นแนวทางปฏิบัติที่ดีที่สุดบางส่วนในการใช้งานระบบ Android วงจร:
คำแนะนำ | คำอธิบาย |
---|---|
อย่าลบล้างเมธอดอายุการใช้งานใน "กิจกรรม" หรือ "ส่วนย่อย"
แนะนำอย่างยิ่ง |
อย่าลบล้างวิธีใช้อายุการใช้งาน เช่น onResume ในกิจกรรมหรือ Fragment โปรดใช้ LifecycleObserver แทน หากแอปต้องทำงานเมื่อวงจรถึง Lifecycle.State ที่กำหนด ให้ใช้ repeatOnLifecycle API |
ข้อมูลโค้ดต่อไปนี้สรุปวิธีดำเนินการ สถานะของวงจร:
ยอดดู
class MyFragment: Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
viewLifecycleOwner.lifecycle.addObserver(object : DefaultLifecycleObserver {
override fun onResume(owner: LifecycleOwner) {
// ...
}
override fun onPause(owner: LifecycleOwner) {
// ...
}
}
}
}
เขียน
@Composable
fun MyApp() {
val lifecycleOwner = LocalLifecycleOwner.current
DisposableEffect(lifecycleOwner, ...) {
val lifecycleObserver = object : DefaultLifecycleObserver {
override fun onStop(owner: LifecycleOwner) {
// ...
}
}
lifecycleOwner.lifecycle.addObserver(lifecycleObserver)
onDispose {
lifecycleOwner.lifecycle.removeObserver(lifecycleObserver)
}
}
}
จัดการทรัพยากร Dependency
มีแนวทางปฏิบัติแนะนำมากมายที่คุณควรปฏิบัติตามเมื่อจัดการทรัพยากร Dependency ระหว่างคอมโพเนนต์
คำแนะนำ | คำอธิบาย |
---|---|
ใช้การแทรก Dependency
แนะนำอย่างยิ่ง |
ใช้แนวทางปฏิบัติแนะนำเกี่ยวกับ Dependency Injection โดยหลักๆ แล้วจะใช้การแทรกเครื่องมือสร้าง เมื่อเป็นไปได้ |
กำหนดขอบเขตไปยังคอมโพเนนต์เมื่อจำเป็น
แนะนำอย่างยิ่ง |
กำหนดขอบเขตเป็นคอนเทนเนอร์ทรัพยากร Dependency เมื่อประเภทมีข้อมูลที่เปลี่ยนแปลงได้ซึ่งจำเป็นต้องแชร์ หรือประเภทการเริ่มต้นมีต้นทุนสูงและใช้กันอย่างแพร่หลายในแอป |
ใช้ Hilt
แนะนำ |
ใช้ Hilt หรือการแทรกทรัพยากร Dependency ด้วยตนเองในแอปแบบง่ายๆ ใช้ Hilt หากโครงการของคุณซับซ้อนพอ ตัวอย่างเช่น หากคุณมี
|
การทดสอบ
แนวทางปฏิบัติแนะนำสำหรับการทดสอบมีดังนี้
คำแนะนำ | คำอธิบาย |
---|---|
รู้ว่าต้องทดสอบอะไร
แนะนำอย่างยิ่ง |
คุณควรทดสอบโปรเจ็กต์ด้วยรายการต่อไปนี้เป็นอย่างน้อย เว้นแต่โปรเจ็กต์จะเรียบง่ายเหมือนแอป Hello World แล้ว
|
ชอบปลอมเพื่อล้อเลียน
แนะนำอย่างยิ่ง |
อ่านเพิ่มเติมได้ในใช้การทดสอบแบบทวีคูณในเอกสาร Android |
ทดสอบ StateFlows
แนะนำอย่างยิ่ง |
เมื่อทดสอบ StateFlow :
|
สำหรับข้อมูลเพิ่มเติม โปรดดูสิ่งที่ต้องทดสอบในคู่มือ DAC ของ Android
รุ่น
คุณควรสังเกตแนวทางปฏิบัติแนะนำต่อไปนี้เมื่อพัฒนาโมเดลในแอป
คำแนะนำ | คำอธิบาย |
---|---|
สร้างโมเดลต่อเลเยอร์ในแอปที่ซับซ้อน
แนะนำ |
ในแอปที่ซับซ้อน ให้สร้างโมเดลใหม่ในเลเยอร์หรือคอมโพเนนต์ต่างๆ ตามความเหมาะสม ลองดูตัวอย่างต่อไปนี้
|
รูปแบบการตั้งชื่อ
เมื่อตั้งชื่อฐานของโค้ด คุณควรทำตามแนวทางปฏิบัติแนะนำต่อไปนี้
คำแนะนำ | คำอธิบาย |
---|---|
วิธีตั้งชื่อ
ไม่บังคับ |
วิธีการควรเป็นวลีกริยา เช่น makePayment() |
การตั้งชื่อพร็อพเพอร์ตี้
ไม่บังคับ |
คุณสมบัติควรเป็นคำนาม เช่น inProgressTopicSelection |
การตั้งชื่อสตรีมข้อมูล
ไม่บังคับ |
เมื่อคลาสแสดงสตรีมโฟลว์, LiveData หรือสตรีมอื่นๆ รูปแบบการตั้งชื่อคือ get{model}Stream() เช่น getAuthorStream(): Flow<Author>
หากฟังก์ชันแสดงผลรายการโมเดล ชื่อโมเดลควรอยู่ในรูปพหูพจน์: getAuthorsStream(): Flow<List<Author>> |
การใช้งานอินเทอร์เฟซการตั้งชื่อ
ไม่บังคับ |
ชื่อสำหรับการใช้อินเทอร์เฟซควรมีความหมาย มี Default เป็นคำนำหน้าหากไม่พบชื่อที่ดีกว่า ตัวอย่างเช่น สำหรับอินเทอร์เฟซ NewsRepository คุณอาจมี OfflineFirstNewsRepository หรือ InMemoryNewsRepository หากไม่เห็นชื่อที่เป็นประโยชน์ ให้ใช้ DefaultNewsRepository
การติดตั้งใช้งานปลอมควรขึ้นต้นด้วย Fake เช่น FakeAuthorsRepository |