Trang này trình bày một số nội dung đề xuất và phương pháp hay nhất về Cấu trúc. Hãy áp dụng các nội dung đề xuất đó để cải thiện chất lượng, độ mạnh và khả năng mở rộng của ứng dụng. Các nội dung đề xuất này cũng giúp bạn bảo trì và kiểm thử ứng dụng dễ dàng hơn.
Các phương pháp hay nhất dưới đây được nhóm theo chủ đề. Mỗi nhóm sẽ có một mức ưu tiên phản ánh mức độ đề xuất riêng. Dưới đây là danh sách mức ưu tiên:
- Strongly recommended (Rất nên dùng): Bạn nên triển khai phương pháp này, trừ phi phương pháp đó xung đột với cách làm của bạn.
- Recommended (Nên dùng): Phương pháp này có thể giúp cải thiện ứng dụng của bạn.
- Optional (Không bắt buộc): Phương pháp này có thể cải thiện ứng dụng của bạn trong một số trường hợp nhất định.
Cấu trúc phân lớp
Cấu trúc phân lớp được đề xuất của chúng tôi ưu tiên tách biệt các mối quan ngại. Cấu trúc này điều khiển giao diện người dùng qua mô hình dữ liệu, tuân thủ nguyên tắc một nguồn đáng tin cậy và tuân theo nguyên tắc luồng dữ liệu một chiều. Dưới đây là một số phương pháp hay nhất về cấu trúc phân lớp:
Nội dung đề xuất | Nội dung mô tả |
---|---|
Dùng lớp dữ liệu được xác định rõ ràng.
Strongly recommended (Rất nên dùng) |
Lớp dữ liệu hiển thị dữ liệu ứng dụng với phần còn lại của ứng dụng và chứa phần lớn logic nghiệp vụ của ứng dụng.
|
Dùng lớp giao diện người dùng được xác định rõ ràng.
Strongly recommended (Rất nên dùng) |
Lớp giao diện người dùng hiển thị dữ liệu ứng dụng trên màn hình và đóng vai trò là điểm chính trong quá trình tương tác của người dùng.
|
Lớp dữ liệu sẽ hiển thị dữ liệu ứng dụng bằng cách dùng kho lưu trữ.
Strongly recommended (Rất nên dùng) |
Các thành phần trong lớp giao diện người dùng, chẳng hạn như thành phần kết hợp, hoạt động hoặc ViewModel không được tương tác trực tiếp với nguồn dữ liệu. Ví dụ về nguồn dữ liệu:
|
Dùng coroutine và luồng.
Strongly recommended (Rất nên dùng) |
Dùng coroutine và luồng để giao tiếp giữa các lớp. |
Dùng lớp miền.
Recommended in big apps (Nên dùng trong các ứng dụng lớn) |
Dùng lớp miền, các trường hợp sử dụng nếu bạn cần dùng lại logic nghiệp vụ để tương tác với lớp dữ liệu trên nhiều ViewModel hoặc bạn muốn đơn giản hoá logic nghiệp vụ của một ViewModel cụ thể |
Lớp giao diện người dùng
Vai trò của lớp giao diện người dùng là hiển thị dữ liệu ứng dụng trên màn hình và đóng vai trò là điểm chính trong quá trình tương tác của người dùng. Dưới đây là một số phương pháp hay nhất cho lớp giao diện người dùng:
Nội dung đề xuất | Nội dung mô tả |
---|---|
Tuân theo nguyên tắc Luồng dữ liệu một chiều (UDF).
Strongly recommended (Rất nên dùng) |
Tuân theo nguyên tắc Luồng dữ liệu một chiều (UDF), trong đó ViewModel hiển thị trạng thái giao diện người dùng thông qua mẫu trình quan sát và nhận các thao tác từ giao diện người dùng thông qua lệnh gọi phương thức. |
Dùng ViewModel AAC nếu có lợi cho ứng dụng của bạn.
Strongly recommended (Rất nên dùng) |
Dùng AAC ViewModel để xử lý logic kinh doanh, cũng như tìm nạp dữ liệu ứng dụng để hiển thị trạng thái giao diện người dùng cho giao diện người dùng (Khung hiển thị Compose hoặc Android). |
Dùng bộ sưu tập trạng thái giao diện người dùng có nhận biết vòng đời.
Strongly recommended (Rất nên dùng) |
Thu thập trạng thái giao diện người dùng từ giao diện người dùng bằng trình tạo coroutine có nhận biết vòng đời thích hợp: repeatOnLifecycle trong hệ thống View (Khung hiển thị) và collectAsStateWithLifecycle trong Jetpack Compose.Đọc thêm về Đọc thêm về |
Không gửi các sự kiện từ ViewModel đến giao diện người dùng.
Strongly recommended (Rất nên dùng) |
Xử lý sự kiện ngay lập tức trong ViewModel và cập nhật trạng thái bằng kết quả xử lý sự kiện. Tìm hiểu thêm về sự kiện giao diện người dùng tại đây. |
Dùng ứng dụng hoạt động đơn.
Recommended (Nên dùng) |
Dùng Mảnh điều hướng hoặc thành phần Điều hướng trong Compose để di chuyển giữa các màn hình và đường liên kết sâu đến ứng dụng của bạn nếu ứng dụng có nhiều màn hình. |
Dùng Jetpack Compose.
Recommended (Nên dùng) |
Dùng Jetpack Compose để tạo ứng dụng mới cho điện thoại, máy tính bảng, thiết bị có thể gập lại và Wear OS. |
Đoạn mã sau đây chỉ ra cách thu thập trạng thái giao diện người dùng theo cách có nhận biết vòng đời:
Khung hiển thị
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
}
}
}
}
}
Compose
@Composable
fun MyScreen(
viewModel: MyViewModel = viewModel()
) {
val uiState by viewModel.uiState.collectAsStateWithLifecycle()
}
ViewModel
ViewModel chịu trách nhiệm cung cấp trạng thái giao diện người dùng và quyền truy cập vào lớp dữ liệu. Dưới đây là một số phương pháp hay nhất về ViewModel:
Nội dung đề xuất | Nội dung mô tả |
---|---|
ViewModel phải độc lập với vòng đời của Android.
Strongly recommended (Rất nên dùng) |
ViewModel không được chứa tệp tham chiếu đến bất kỳ kiểu nào liên quan đến Vòng đời. Không truyền Activity, Fragment, Context hoặc Resources làm phần phụ thuộc.
Nếu cần Context trong ViewModel, bạn nên đánh giá xem liệu ViewModel có thuộc đúng lớp (layer) hay không. |
Dùng coroutine và luồng.
Strongly recommended (Rất nên dùng) |
ViewModel tương tác với các lớp dữ liệu hoặc miền thông qua:
|
Dùng ViewModel ở cấp màn hình.
Strongly recommended (Rất nên dùng) |
Không sử dụng ViewModel trong các phần giao diện người dùng có thể tái sử dụng. Bạn nên sử dụng ViewModel trong:
|
Dùng các lớp của phần tử giữ trạng thái thuần tuý trong các thành phần giao diện người dùng có thể tái sử dụng.
Strongly recommended (Rất nên dùng) |
Dùng các lớp của phần tử giữ trạng thái thuần tuý để xử lý độ phức tạp của các thành phần trên giao diện người dùng có thể tái sử dụng. Bằng cách này, trạng thái có thể được chuyển lên trên và kiểm soát bên ngoài. |
Không sử dụng AndroidViewModel .Recommended (Nên dùng) |
Dùng lớp ViewModel , chứ không phải AndroidViewModel . Không nên dùng lớp Application trong ViewModel. Thay vào đó, hãy di chuyển phần phụ thuộc sang giao diện người dùng hoặc lớp dữ liệu. |
Hiển thị trạng thái giao diện người dùng.
Recommended (Nên dùng) |
ViewModel phải hiển thị dữ liệu cho giao diện người dùng thông qua một thuộc tính có tên là uiState . Nếu giao diện người dùng hiển thị nhiều phần dữ liệu không liên quan, máy ảo có thể hiển thị nhiều thuộc tính trạng thái giao diện người dùng.
|
Đoạn mã sau đây trình bày cách hiển thị trạng thái giao diện người dùng từ 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
)
// ...
}
Vòng đời
Sau đây là một số phương pháp hay nhất để xử lý vòng đời của Android:
Nội dung đề xuất | Nội dung mô tả |
---|---|
Không ghi đè các phương thức vòng đời trong Activity (Hoạt động) hoặc Fragment (Mảnh).
Strongly recommended (Rất nên dùng) |
Không ghi đè các phương thức vòng đời, chẳng hạn như onResume trong Activity (Hoạt động) hoặc Fragment (Mảnh). Thay vào đó, hãy sử dụng LifecycleObserver . Nếu ứng dụng cần thực hiện công việc khi vòng đời đạt đến một Lifecycle.State nhất định, hãy sử dụng API repeatOnLifecycle . |
Đoạn mã sau đây trình bày cách thực hiện các thao tác dựa trên một trạng thái Vòng đời nhất định:
Khung hiển thị
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) {
// ...
}
}
}
}
Compose
@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)
}
}
}
Xử lý các phần phụ thuộc
Bạn nên áp dụng một số phương pháp hay nhất khi quản lý các phần phụ thuộc giữa các thành phần:
Nội dung đề xuất | Nội dung mô tả |
---|---|
Dùng tính năng chèn phần phụ thuộc.
Strongly recommended (Rất nên dùng) |
Áp dụng các phương pháp hay nhất về kỹ thuật chèn phần phụ thuộc, chủ yếu là chèn hàm khởi tạo khi có thể. |
Xác định phạm vi ở một thành phần khi cần.
Strongly recommended (Rất nên dùng) |
Xác định phạm vi ở một vùng chứa phần phụ thuộc khi loại đó chứa dữ liệu có thể thay đổi cần được chia sẻ hoặc loại cần khởi chạy gây tốn kém và được sử dụng rộng rãi trong ứng dụng. |
Dùng Hilt.
Recommended (Nên dùng) |
Dùng Hilt hoặc kỹ thuật chèn phần phụ thuộc theo cách thủ công trong các ứng dụng đơn giản. Dùng Hilt nếu dự án của bạn khá phức tạp. Ví dụ: nếu bạn:
|
Kiểm thử
Sau đây là một số phương pháp hay nhất để kiểm thử:
Nội dung đề xuất | Nội dung mô tả |
---|---|
Nắm rõ nội dung cần kiểm thử.
Strongly recommended (Rất nên dùng) |
Trừ phi dự án này đơn giản như ứng dụng Hello World, ít nhất, bạn nên kiểm thử ứng dụng bằng:
|
Ưu tiên loại kiểm thử fake hoặc mock (giả hoặc mô phỏng).
Strongly recommended (Rất nên dùng) |
Vui lòng đọc thêm trong tài liệu Dùng kỹ thuật nhân đôi kiểm thử (test double) trong Android. |
Kiểm thử StateFlow.
Strongly recommended (Rất nên dùng) |
Khi kiểm thử StateFlow :
|
Để biết thêm thông tin, hãy xem hướng dẫn Nội dung cần kiểm thử trong Android DAC.
Mô hình
Bạn nên áp dụng các phương pháp hay nhất sau đây khi phát triển mô hình trong ứng dụng của mình:
Nội dung đề xuất | Nội dung mô tả |
---|---|
Tạo một mô hình trên mỗi lớp trong các ứng dụng phức tạp.
Recommended (Nên dùng) |
Trong các ứng dụng phức tạp, hãy tạo mô hình mới ở các lớp hoặc thành phần khác nhau khi thích hợp. Hãy xem các ví dụ sau đây:
|
Quy ước đặt tên
Khi đặt tên cho bộ mã, bạn nên nắm được các phương pháp hay nhất sau đây:
Nội dung đề xuất | Nội dung mô tả |
---|---|
Đặt tên cho phương thức.
Optional (Không bắt buộc) |
Phương thức phải là một cụm động từ. Ví dụ: makePayment() . |
Đặt tên cho thuộc tính.
Optional (Không bắt buộc) |
Thuộc tính phải là một cụm danh từ. Ví dụ: inProgressTopicSelection . |
Đặt tên cho luồng dữ liệu.
Optional (Không bắt buộc) |
Khi một lớp hiển thị luồng Quy trình, LiveData hoặc bất kỳ luồng nào khác, quy ước đặt tên là get{model}Stream() . Ví dụ: getAuthorStream(): Flow<Author> Nếu hàm trả về danh sách mô hình, tên mô hình phải ở dạng số nhiều: getAuthorsStream(): Flow<List<Author>> |
Đặt tên cho việc triển khai giao diện.
Optional (Không bắt buộc) |
Tên cho việc triển khai giao diện phải có ý nghĩa. Đặt Default làm tiền tố nếu không tìm thấy tên phù hợp hơn. Ví dụ: đối với giao diện NewsRepository , bạn có thể dùng tên OfflineFirstNewsRepository hoặc InMemoryNewsRepository . Nếu bạn không tìm thấy tên phù hợp, hãy dùng tên DefaultNewsRepository .
Việc triển khai giả phải có tiền tố là Fake , như trong FakeAuthorsRepository . |