Giới thiệu
Bạn đang xây dựng ứng dụng và muốn có một kiến trúc mạnh mẽ và chất lượng cao? Hãy đọc hướng dẫn này để biết những nguyên tắc và kiến trúc được đề xuất để xây dựng ứng dụng ổn định và chất lượng cao.
Kinh nghiệm người dùng của ứng dụng di động
Một ứng dụng di động thông thường bao gồm nhiều thành phần, bao gồm hoạt động, các phân đoạn, dịch vụ, nhà cung cấp nội dung và bộ thu sóng. Các thành phần của ứng dụng này thường được khai báo trong tệp manifest của ứng dụng. Hệ điều hành Android sau đó sử dụng tệp này để quyết định cách tích hợp ứng dụng vào trải nghiệm người dùng chung của thiết bị. Vì một ứng dụng Android thông thường có thể chứa nhiều thành phần và người dùng thường tương tác với nhiều ứng dụng trong một khoảng thời gian ngắn, ứng dụng cần phải thích ứng với các quy trình làm việc và tác vụ do người dùng thực hiện.
Hãy nhớ rằng các thiết bị di động cũng có hạn chế về tài nguyên, vì vậy vào bất kỳ thời điểm nào, hệ điều hành có thể kết thúc một số quy trình ứng dụng để tạo chỗ cho những quy trình mới.
Với điều kiện của môi trường này, các thành phần của ứng dụng của bạn có thể được khởi chạy một cách riêng lẻ và không được theo đúng thứ tự, và hệ điều hành hoặc người dùng có thể phá hủy chúng vào bất kỳ thời điểm nào. Vì các sự kiện này không nằm trong tầm kiểm soát của bạn, bạn không nên lưu trữ hoặc giữ các dữ liệu hoặc trạng thái ứng dụng trong các thành phần của ứng dụng của bạn, và các thành phần của ứng dụng của bạn không nên phụ thuộc vào nhau.
Nguyên tắc kiến trúc phổ biến
Nếu bạn không nên sử dụng các thành phần ứng dụng để lưu trữ dữ liệu và trạng thái ứng dụng, bạn nên thiết kế kiến trúc của ứng dụng của mình như thế nào?
Khi ứng dụng Android của bạn ngày càng lớn, việc định nghĩa một kiến trúc cho phép ứng dụng mở rộng, tăng tính đồng nhất và dễ kiểm tra là điều quan trọng.
Một kiến trúc ứng dụng xác định rõ ranh giới giữa các phần của ứng dụng và trách nhiệm mà mỗi phần nên có. Để đáp ứng các yêu cầu được đề cập ở trên, bạn nên thiết kế kiến trúc ứng dụng của mình để tuân theo một số nguyên tắc cụ thể.
Phân tách các quan tâm
Nguyên tắc quan trọng nhất để tuân thủ là phân tách các quan tâm. Việc viết toàn bộ mã của bạn trong một hoạt động hoặc một đoạn là sai lầm thông thường. Các lớp dựa trên giao diện người dùng này chỉ nên chứa logic xử lý giao diện người dùng và tương tác với hệ điều hành. Bằng cách giữ cho các lớp này càng nhẹ càng tốt, bạn có thể tránh nhiều vấn đề liên quan đến vòng đời thành phần và cải thiện khả năng kiểm tra của các lớp này.
Hãy nhớ rằng bạn không sở hữu việc triển khai của Activity và Fragment; thay vào đó, đây chỉ là các lớp keo giữ đại diện cho hợp đồng giữa hệ điều hành Android và ứng dụng của bạn. Hệ điều hành có thể phá hủy chúng bất kỳ lúc nào dựa trên tương tác của người dùng hoặc vì các điều kiện hệ thống như bộ nhớ thấp. Để cung cấp trải nghiệm người dùng đáng hài lòng và trải nghiệm bảo trì ứng dụng dễ quản lý hơn, tốt nhất là giảm thiểu sự phụ thuộc của bạn vào chúng.
Điều hướng giao diện người dùng từ các mô hình dữ liệu
Nguyên tắc quan trọng khác là bạn nên điều hướng giao diện người dùng từ các mô hình dữ liệu, ưu tiên là các mô hình dữ liệu bền vững. Mô hình dữ liệu đại diện cho dữ liệu của ứng dụng. Chúng độc lập với các yếu tố giao diện người dùng và các thành phần khác trong ứng dụng của bạn. Điều này có nghĩa là chúng không bị ràng buộc với giao diện người dùng và vòng đời thành phần ứng dụng, nhưng sẽ bị phá hủy khi hệ điều hành quyết định loại bỏ quy trình của ứng dụng khỏi bộ nhớ.
Mô hình dữ liệu bền vững lý tưởng vì các lý do sau đây:
- Người dùng không mất dữ liệu nếu hệ điều hành Android phá hủy ứng dụng của bạn để giải phóng tài nguyên.
- Ứng dụng của bạn vẫn tiếp tục hoạt động trong trường hợp kết nối mạng không ổn định hoặc không khả dụng.
Nếu bạn xây dựng kiến trúc ứng dụng của mình dựa trên các lớp mô hình dữ liệu, bạn sẽ làm cho ứng dụng của bạn dễ kiểm tra và chắc chắn hơn.
Một nguồn dữ liệu chính
Khi định nghĩa một loại dữ liệu mới trong ứng dụng của bạn, bạn nên gán một nguồn Dữ liệu duy nhất (SSOT) cho nó. SSOT là người sở hữu của dữ liệu đó và chỉ có SSOT mới có thể sửa đổi hoặc biến đổi nó. Để đạt được điều này, SSOT chia sẻ dữ liệu bằng cách sử dụng một loại không thể thay đổi được và để sửa đổi dữ liệu, SSOT chia sẻ chức năng hoặc nhận sự kiện mà các loại khác có thể gọi.
Mẫu này mang lại nhiều lợi ích:
- Nó tập trung tất cả các thay đổi vào một loại dữ liệu cụ thể.
- Nó bảo vệ dữ liệu để các loại khác không thể xâm phạm nó.
- Nó làm cho các thay đổi trực quan hơn. Như vậy, các lỗi dễ nhìn thấy hơn.
Trong một ứng dụng tập trung vào việc ngoại tuyến, nguồn của sự thật cho dữ liệu ứng dụng thường là một cơ sở dữ liệu. Trong một số trường hợp khác, nguồn của sự thật có thể là ViewModel hoặc thậm chí là giao diện người dùng.
Luồng dữ liệu hướng một chiều
Nguyên tắc nguồn dữ liệu chính thường được sử dụng trong các hướng dẫn của chúng tôi với mô hình Luồng dữ liệu hướng một chiều (UDF). Trong UDF, trạng thái chảy theo một hướng duy nhất. Các sự kiện sửa đổi dữ liệu chảy theo hướng ngược lại.
Trong Android, trạng thái hoặc dữ liệu thường chảy từ các loại có phạm vi rộng hơn trong hệ thống cho đến các loại có phạm vi hạn chế hơn. Các sự kiện thường được kích hoạt từ các loại có phạm vi hạn chế hơn cho đến khi chúng đến SSOT cho loại dữ liệu tương ứng. Ví dụ, dữ liệu ứng dụng thường từ các nguồn dữ liệu đến giao diện người dùng. Sự kiện người dùng như nhấn nút chảy từ giao diện người dùng đến SSOT nơi dữ liệu ứng dụng được sửa đổi và tiếp tục được hiển thị dưới dạng một loại không thể thay đổi.
Mẫu này đảm bảo tính nhất quán của dữ liệu, ít dễ sai sót hơn, dễ gỡ lỗi hơn và mang lại tất cả các lợi ích của mẫu SSOT.
Kiến trúc ứng dụng được đề xuất
Phần này thể hiện cách cấu trúc ứng dụng theo các nguyên tắc và quy tắc tốt nhất được đề xuất.
Lưu ý: Các khuyến nghị và quy tắc tốt nhất hiện diện trong trang này có thể được áp dụng cho rất nhiều ứng dụng để cho phép chúng mở rộng, cải thiện chất lượng và tính đồng nhất và dễ kiểm tra. Tuy nhiên, bạn nên coi chúng như là hướng dẫn và thích nghi chúng với yêu cầu của mình theo cách cần thiết.
Xem xét các nguyên tắc kiến trúc thông thường được đề cập ở phần trước, mỗi ứng dụng nên có ít nhất hai lớp:
- Lớp giao diện người dùng hiển thị dữ liệu ứng dụng trên màn hình.
- Lớp dữ liệu chứa logic kinh doanh của ứng dụng và tiết lộ dữ liệu ứng dụng.
Bạn có thể thêm một lớp phụ gọi là lớp miền để đơn giản hóa và tái sử dụng các tương tác giữa lớp giao diện người dùng và lớp dữ liệu.
Để biết thêm chi tiết, xem các hình ảnh và nguyên tắc trong trang gốc.
Quản lý các phụ thuộc giữa các thành phần
Các lớp trong ứng dụng của bạn phụ thuộc vào các lớp khác để hoạt động đúng cách. Bạn có thể sử dụng mẫu thiết kế sau để thu thập các phụ thuộc của một lớp cụ thể:
- Dependency Injection (DI): Dependency injection cho phép các lớp xác định các phụ thuộc của chúng mà không cần tạo chúng. Tại thời điểm chạy, một lớp khác chịu trách nhiệm cung cấp các phụ thuộc này.
- Service locator: Mẫu tìm kiếm dịch vụ cung cấp một đăng ký nơi các lớp có thể lấy các phụ thuộc của mình thay vì tạo chúng.
Các mẫu này cho phép bạn mở rộng mã của bạn vì chúng cung cấp mẫu rõ ràng để quản lý các phụ thuộc mà không nhân đôi mã hoặc tăng tính phức tạp. Hơn nữa, các mẫu này cho phép bạn nhanh chóng chuyển đổi giữa các đơn vị kiểm tra và triển khai sản phẩm.
Chúng tôi khuyến nghị tuân thủ các mẫu thiết kế phụ thuộc và sử dụng thư viện Hilt trong ứng dụng Android của bạn. Hilt tự động xây dựng các đối tượng bằng cách đi qua cây phụ thuộc và cung cấp các cam kết biên dịch về phụ thuộc, và tạo các container phụ thuộc cho các lớp của Android framework.
Thực hành tốt nhất chung
lập trình là một lĩnh vực sáng tạo và xây dựng ứng dụng Android cũng không phải là ngoại lệ. Có nhiều cách để giải quyết một vấn đề; bạn có thể truyền dữ liệu giữa nhiều hoạt động hoặc đoạn, truy xuất dữ liệu từ xa và lưu trữ nó cục bộ để sử dụng ngoại tuyến hoặc xử lý bất kỳ số lượng kịch bản thông thường nào mà ứng dụng không dễ gặp phải.
Mặc dù các khuyến nghị sau không bắt buộc, nhưng trong hầu hết các trường hợp tuân thủ chúng sẽ làm cho mã cơ sở của bạn mạnh mẽ hơn, dễ kiểm tra hơn và bảo trì trong dài hạn:
- Không lưu trữ dữ liệu trong các thành phần ứng dụng.
- Giảm sự phụ thuộc vào các lớp Android.
- Tạo ranh giới trách nhiệm rõ ràng giữa các mô-đun khác nhau trong ứng dụng của bạn.
- Tiết lộ ít nhất có thể từ mỗi mô-đun.
- Tập trung vào lõi độc đáo của ứng dụng của bạn để nó nổi bật so với các ứng dụng khác.
- Xem xét cách làm cho mỗi phần của ứng dụng của bạn có thể kiểm tra độc lập.
- Các loại chịu trách nhiệm cho chính sách đồng thời của chúng.
- Lưu nhiều dữ liệu liên quan và mới nhất như có thể.
- Cải thiện hiệu suất ứng dụng của bạn để người dùng có thể tận hưởng tính năng ngay cả khi thiết bị của họ không kết nối mạng.
Lợi ích của kiến trúc
Việc triển khai một kiến trúc tốt trong ứng dụng của bạn mang lại nhiều lợi ích cho dự án và nhóm kỹ thuật:
- Nó cải thiện khả năng bảo trì, chất lượng và tính đồng nhất của ứng dụng tổng thể.
- Nó cho phép ứng dụng mở rộng. Có thể có nhiều người và nhiều nhóm tham gia đóng góp vào cùng một mã nguồn với ít xung đột mã.
- Nó giúp việc tiếp nhận nhân viên mới. Kiến trúc mang tính đồng nhất vào dự án của bạn, các thành viên mới trong nhóm có thể nhanh chóng nắm bắt và làm việc hiệu quả trong thời gian ngắn.
- Nó dễ kiểm tra hơn. Một kiến trúc tốt khuyến khích các loại đơn giản hơn, thường dễ kiểm tra hơn.
- Có thể điều tra lỗi một cách phương pháp với các quy trình xác định rõ ràng.
Đầu tư vào Kiến trúc cũng có ảnh hưởng trực tiếp đến người dùng của bạn. Họ được hưởng lợi từ một ứng dụng ổn định hơn và nhiều tính năng hơn do một nhóm kỹ thuật hiệu quả. Tuy nhiên, Kiến trúc cũng đòi hỏi việc đầu tư thời gian từ đầu. Để giúp bạn chứng minh thời gian này cho phần còn lại của công ty của bạn, hãy xem các nghiên cứu trường hợp này, nơi các công ty khác chia sẻ câu chuyện thành công khi có một kiến trúc tốt trong ứng dụng của họ.
Các ví dụ
Các ví dụ từ Google sau đây thể hiện kiến trúc ứng dụng tốt. Hãy khám phá chúng để thấy các hướng dẫn này được thực hiện trong thực tế:
- Note: Đây chỉ là mã nguồn dự án Example.
- Note: Layer UI.
- Note: Layer Data.
- Note: Layer Domain.