หน้านี้แสดงแนวทางปฏิบัติแนะนำและแนวทางปฏิบัติแนะนำหลายประการเกี่ยวกับสถาปัตยกรรม ใช้แนวทางเหล่านี้เพื่อปรับปรุงคุณภาพ ความเสถียร และความสามารถในการปรับขนาดของแอป และยังช่วยให้คุณดูแลรักษาและทดสอบแอปได้ง่ายขึ้นด้วย
แนวทางปฏิบัติแนะนำด้านล่างจะจัดกลุ่มตามหัวข้อ แต่ละรายการจะมีลําดับความสําคัญที่แสดงถึงระดับการแนะนําของทีม รายการลำดับความสำคัญมีดังต่อไปนี้
- แนะนำอย่างยิ่ง: คุณควรใช้แนวทางปฏิบัตินี้ เว้นแต่ว่าแนวทางปฏิบัติดังกล่าวจะขัดแย้งกับแนวทางของคุณโดยพื้นฐาน
- แนะนำ: แนวทางปฏิบัตินี้น่าจะช่วยปรับปรุงแอปของคุณได้
- ไม่บังคับ: วิธีนี้ช่วยปรับปรุงแอปได้ในบางสถานการณ์
สถาปัตยกรรมแบบเป็นชั้น
สถาปัตยกรรมแบบเป็นชั้นที่เราแนะนำจะแยกข้อกังวลออกจากกัน โดยขับเคลื่อน UI จากโมเดลข้อมูล เป็นไปตามหลักการแหล่งข้อมูลที่ถูกต้องแห่งเดียว และเป็นไปตามหลักการการไหลของข้อมูลแบบทิศทางเดียว แนวทางปฏิบัติแนะนำเกี่ยวกับ สถาปัตยกรรมแบบเลเยอร์มีดังนี้
คำแนะนำ | คำอธิบาย |
---|---|
ใช้ชั้นข้อมูลที่กําหนดไว้อย่างชัดเจน
แนะนำอย่างยิ่ง |
ชั้นข้อมูลจะเปิดเผยข้อมูลแอปพลิเคชันส่วนที่เหลือของแอปและประกอบด้วยตรรกะทางธุรกิจส่วนใหญ่ของแอป
|
ใช้เลเยอร์ UI ที่กําหนดไว้อย่างชัดเจน
แนะนำอย่างยิ่ง |
เลเยอร์ UI จะแสดงข้อมูลแอปพลิเคชันบนหน้าจอและใช้เป็นจุดหลักในการโต้ตอบกับผู้ใช้
|
ชั้นข้อมูลควรแสดงข้อมูลแอปพลิเคชันโดยใช้ที่เก็บข้อมูล
แนะนำอย่างยิ่ง |
คอมโพเนนต์ในเลเยอร์ UI เช่น Composable, กิจกรรม หรือ ViewModel ไม่ควรโต้ตอบกับแหล่งข้อมูลโดยตรง ตัวอย่างแหล่งข้อมูล ได้แก่
|
ใช้ Coroutine และ Flow
แนะนำอย่างยิ่ง |
ใช้โครูทีนและโฟลว์เพื่อสื่อสารระหว่างเลเยอร์ |
ใช้เลเยอร์โดเมน
แนะนำในแอปขนาดใหญ่ |
ใช้เลเยอร์โดเมน กรณีการใช้งาน หากคุณต้องการนําตรรกะทางธุรกิจที่โต้ตอบกับเลเยอร์ข้อมูลใน ViewModel หลายรายการมาใช้ซ้ำ หรือต้องการลดความซับซ้อนของตรรกะทางธุรกิจของ ViewModel บางรายการ |
เลเยอร์ UI
บทบาทของเลเยอร์ UI คือการแสดงข้อมูลแอปพลิเคชันบนหน้าจอ และใช้เป็นจุดหลักในการโต้ตอบกับผู้ใช้ แนวทางปฏิบัติแนะนำสำหรับเลเยอร์ UI มีดังนี้
คำแนะนำ | คำอธิบาย |
---|---|
ทำตามการไหลของข้อมูลแบบทิศทางเดียว (UDF)
แนะนำอย่างยิ่ง |
ทำตามหลักการ Unidirectional Data Flow (UDF) โดย ViewModels แสดงสถานะ UI โดยใช้รูปแบบผู้สังเกตการณ์และรับการดำเนินการจาก UI ผ่านการเรียกใช้เมธอด |
ใช้ ViewModel ของ AAC หากแอปของคุณได้รับประโยชน์จาก ViewModel ดังกล่าว
แนะนำอย่างยิ่ง |
ใช้ ViewModel ของ AAC เพื่อจัดการตรรกะทางธุรกิจ และดึงข้อมูลแอปพลิเคชันเพื่อแสดงสถานะ UI ต่อ UI (Compose หรือ Android View) |
ใช้การเก็บรวบรวมสถานะ UI ที่รับรู้วงจรของลูกค้า
แนะนำอย่างยิ่ง |
รวบรวมสถานะ UI จาก UI โดยใช้โปรแกรมสร้างโคโรทีนซึ่งคำนึงถึงวงจรชีวิตของแอปที่เหมาะสม ได้แก่ repeatOnLifecycle ในระบบ View และ collectAsStateWithLifecycle ใน Jetpack Compose
อ่านเพิ่มเติมเกี่ยวกับ อ่านข้อมูลเพิ่มเติมเกี่ยวกับ |
อย่าส่งเหตุการณ์จาก ViewModel ไปยัง UI
แนะนำอย่างยิ่ง |
ประมวลผลเหตุการณ์ทันทีใน ViewModel และสร้างการอัปเดตสถานะโดยเป็นผลมาจากการจัดการเหตุการณ์ ดูข้อมูลเพิ่มเติมเกี่ยวกับเหตุการณ์ UI ได้ที่นี่ |
ใช้แอปพลิเคชันกิจกรรมเดียว
แนะนำ |
ใช้ Navigation Fragments หรือ Navigation Compose เพื่อไปยังส่วนต่างๆ ระหว่างหน้าจอและ Deep Link ไปยังแอปหากแอปมีมากกว่า 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
ViewModel มีหน้าที่ระบุสถานะ UI และเข้าถึงเลเยอร์ข้อมูล แนวทางปฏิบัติแนะนำบางส่วนสำหรับ ViewModel มีดังนี้
คำแนะนำ | คำอธิบาย |
---|---|
ViewModel ไม่ควรเกี่ยวข้องกับวงจรชีวิตของ Android
แนะนำอย่างยิ่ง |
ViewModels ไม่ควรอ้างอิงถึงประเภทใดๆ ที่เกี่ยวข้องกับวงจร อย่าส่ง Activity, Fragment, 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
)
// ...
}
วงจร
แนวทางปฏิบัติแนะนำบางส่วนสำหรับการใช้ Android lifecycle มีดังนี้
คำแนะนำ | คำอธิบาย |
---|---|
อย่าลบล้างเมธอดวงจรของ Activity หรือ Fragment
แนะนำอย่างยิ่ง |
อย่าลบล้างเมธอดอายุการใช้งาน เช่น 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 Injection โดยหลักๆ แล้วจะใช้การแทรกเครื่องมือสร้าง เมื่อเป็นไปได้ |
กำหนดขอบเขตไปยังคอมโพเนนต์เมื่อจำเป็น
แนะนำอย่างยิ่ง |
กำหนดขอบเขตเป็นคอนเทนเนอร์ทรัพยากร Dependency เมื่อประเภทมีข้อมูลที่เปลี่ยนแปลงได้ซึ่งจำเป็นต้องแชร์ หรือประเภทการเริ่มต้นมีต้นทุนสูงและใช้กันอย่างแพร่หลายในแอป |
ใช้ Hilt
แนะนำ |
ใช้ Hilt หรือการฉีด Dependency ด้วยตนเองในแอปง่ายๆ ใช้ Hilt หากโปรเจ็กต์มีความซับซ้อนมาก เช่น หากคุณมีสิ่งต่อไปนี้
|
การทดสอบ
แนวทางปฏิบัติแนะนำบางส่วนสําหรับการทดสอบมีดังนี้
คำแนะนำ | คำอธิบาย |
---|---|
รู้สิ่งที่จะทดสอบ
แนะนำอย่างยิ่ง |
คุณควรทดสอบโปรเจ็กต์เป็นอย่างน้อยด้วยสิ่งต่อไปนี้ เว้นแต่โปรเจ็กต์จะเรียบง่ายพอๆ กับแอป Hello World
|
ชอบปลอมเพื่อล้อเลียน
แนะนำอย่างยิ่ง |
อ่านเพิ่มเติมได้ในใช้ Test Double ในเอกสารประกอบของ Android |
ทดสอบ StateFlow
แนะนำอย่างยิ่ง |
เมื่อทดสอบ StateFlow
|
สำหรับข้อมูลเพิ่มเติม โปรดดูสิ่งที่ต้องทดสอบในคู่มือ DAC ของ Android
รุ่น
คุณควรปฏิบัติตามแนวทางปฏิบัติแนะนำเหล่านี้เมื่อพัฒนาโมเดลในแอป
คำแนะนำ | คำอธิบาย |
---|---|
สร้างโมเดลต่อเลเยอร์ในแอปที่ซับซ้อน
แนะนำ |
ในแอปที่ซับซ้อน ให้สร้างโมเดลใหม่ในเลเยอร์หรือคอมโพเนนต์ต่างๆ ตามความเหมาะสม ลองดูตัวอย่างต่อไปนี้
|
รูปแบบการตั้งชื่อ
เมื่อตั้งชื่อโค้ดเบส คุณควรคำนึงถึงแนวทางปฏิบัติแนะนำต่อไปนี้
คำแนะนำ | คำอธิบาย |
---|---|
วิธีการตั้งชื่อ
ไม่บังคับ |
เมธอดควรเป็นวลีที่มีคำกริยา เช่น makePayment() |
การตั้งชื่อพร็อพเพอร์ตี้
ไม่บังคับ |
พร็อพเพอร์ตี้ควรเป็นวลีนาม เช่น inProgressTopicSelection |
ตั้งชื่อสตรีมข้อมูล
ไม่บังคับ |
เมื่อคลาสแสดงสตรีม Flow, LiveData หรือสตรีมอื่นๆ รูปแบบการตั้งชื่อจะเป็น get{model}Stream() เช่น getAuthorStream(): Flow<Author>
หากฟังก์ชันแสดงผลรายการโมเดล ชื่อโมเดลควรเป็นพหูพจน์ getAuthorsStream(): Flow<List<Author>> |
การตั้งชื่อการติดตั้งใช้งานอินเทอร์เฟซ
ไม่บังคับ |
ชื่อสำหรับการติดตั้งใช้งานอินเทอร์เฟซควรสื่อความหมาย ใช้ Default เป็นคำนำหน้าหากไม่พบชื่อที่ดีกว่า เช่น สําหรับอินเทอร์เฟซ NewsRepository คุณอาจมี OfflineFirstNewsRepository หรือ InMemoryNewsRepository หากไม่เห็นชื่อที่เป็นประโยชน์ ให้ใช้ DefaultNewsRepository
การใช้งานจำลองควรขึ้นต้นด้วย Fake เช่น FakeAuthorsRepository |