Giới thiệu về Android Architecture Components (Phần I)
Có lẽ gần đây các Android dev chúng ta thường nghe nói (và/hoặc đã làm) về các mô hình kiến trúc (Architecture Pattern) của Android. Google cũng đã xây dựng hẳn 1 github repo dành để giới thiệu (và hướng dẫn) về các mô hình kiến trúc này. Tuy nhiên, việc xây dựng các kiến trúc đó mới chỉ dựa trên ...
Có lẽ gần đây các Android dev chúng ta thường nghe nói (và/hoặc đã làm) về các mô hình kiến trúc (Architecture Pattern) của Android. Google cũng đã xây dựng hẳn 1 github repo dành để giới thiệu (và hướng dẫn) về các mô hình kiến trúc này. Tuy nhiên, việc xây dựng các kiến trúc đó mới chỉ dựa trên nền tảng Java đơn thuần, trong khi đó với 1 app Android ta có nhiều việc cần phải làm hơn ví dụ như việc quản lý lifecycle của app, Activity hay Fragment. Hiểu được điều đó, Google đã xây dựng lên một tập hợp các lib dành riêng cho việc xây dựng kiến trúc này, với tên gọi Android Architecture Components. Hãy bắt đầu với lời giới thiệu của Google:
A new collection of libraries that help you design robust, testable, and maintainable apps. Start with classes for managing your UI component lifecycle and handling data persistence.
- Note: Đây mới chỉ là phiên bản preview, nghĩa là bộ lib này vẫn đang trong quá trình phát triển và chưa chính thức được release. Khuyến cáo chúng ta chỉ nên tìm hiểu trước chứ không nên áp dụng ngay vì có thể sẽ còn nhiều thay đổi trong tương lai.
Trong bài đầu tiên này ta sẽ tìm hiểu bắt đầu từ App Architecture.
Các vấn đề thường gặp của các developer
Không giống như các máy tính để bàn truyền thống, trong đa số trường hợp, có một điểm vào từ lối tắt của launcher và chạy như một quá trình đơn khối, các ứng dụng Android có cấu trúc phức tạp hơn nhiều. Một ứng dụng Android điển hình được xây dựng trên nhiều thành phần của ứng dụng , bao gồm các Activity, Fragment, Service, Content Provider và Broastcast Receiver.
Hầu hết các thành phần ứng dụng này được khai báo trong tệp Manifest của ứng dụng được sử dụng bởi Hệ điều hành Android để quyết định làm thế nào để tích hợp ứng dụng của bạn vào trải nghiệm người dùng tổng thể với các thiết bị của họ. Mặc dù, như đã đề cập trước đó, một ứng dụng dành cho máy tính để bàn thường chạy theo quy trình khối, ứng dụng Android được viết đúng cần phải linh hoạt hơn nhiều khi người dùng trải qua các ứng dụng khác nhau trên thiết bị của họ, liên tục chuyển đổi các luồng và tác vụ.
Ví dụ: hãy xem xét điều gì sẽ xảy ra khi bạn chia sẻ ảnh trong ứng dụng mạng xã hội ưa thích của bạn. Ứng dụng này kích hoạt Camera Intent mà hệ điều hành Android khởi chạy ứng dụng máy ảnh để xử lý yêu cầu. Tại thời điểm này, người dùng rời ứng dụng mạng xã hội nhưng trải nghiệm của họ liền mạch. Mặt khác, ứng dụng camera có thể kích hoạt các Intent khác, chẳng hạn như khởi chạy trình chọn tệp, có thể khởi chạy một ứng dụng khác. Cuối cùng, người dùng quay lại ứng dụng mạng xã hội và chia sẻ ảnh. Ngoài ra, người dùng có thể bị gián đoạn bằng một cuộc điện thoại tại bất kỳ điểm nào trong quá trình này và quay lại để chia sẻ ảnh sau khi kết thúc cuộc gọi điện thoại.
Trong Android, hành vi ứng dụng kiểu này rất phổ biến, vì vậy ứng dụng của bạn phải xử lý đúng các luồng này. Hãy nhớ rằng các thiết bị di động là tài nguyên bị ràng buộc, do đó bất cứ lúc nào hệ điều hành có thể cần phải kill một số ứng dụng để nhường chỗ cho các ứng dụng mới.
Tâm điểm của tất cả các điều này là các thành phần ứng dụng của bạn có thể được khởi chạy riêng lẻ và không theo thứ tự và có thể bị destroy bất cứ lúc nào bởi người dùng hoặc hệ thống. Vì các thành phần của ứng dụng là không phù hợp và vòng đời của chúng (khi chúng được tạo và hủy) không thuộc sự kiểm soát của bạn, bạn không nên lưu trữ dữ liệu ứng dụng hoặc trạng thái trong các thành phần ứng dụng của bạn và các thành phần ứng dụng của bạn không nên phụ thuộc lẫn nhau.
Nguyên tắc kiến trúc chung
Nếu bạn không thể sử dụng các thành phần ứng dụng để lưu trữ dữ liệu ứng dụng và trạng thái, làm thế nào để áp dụng được cấu trúc?
Điều quan trọng nhất bạn nên tập trung vào là sự tách biệt các mối quan tâm trong ứng dụng của bạn. Một sai lầm phổ biến là bạn thường viết tất cả code của bạn trong một Activity hoặc một Fragment . Bất kỳ code nào không xử lý giao diện người dùng hoặc tương tác hệ điều hành không được để trong các lớp này. Giữ cho chúng càng gọn gàng càng tốt, nó sẽ cho phép bạn tránh được nhiều vấn đề liên quan đến vòng đời. Đừng quên rằng bạn không sở hữu những class này, chúng chỉ là những lớp gắn kết để thể hiện contract giữa hệ điều hành và ứng dụng của bạn. Hệ điều hành Android có thể destroy chúng bất cứ lúc nào dựa trên tương tác của người dùng hoặc các yếu tố khác như bộ nhớ thấp. Tốt nhất là giảm thiểu sự phụ thuộc vào chúng để cung cấp trải nghiệm người dùng vững chắc.
Nguyên tắc quan trọng thứ hai là bạn nên điều khiển giao diện người dùng của bạn từ một Model, tốt hơn là một Model bền bỉ. Tính bền bỉ là lý tưởng vì hai lý do: người dùng của bạn sẽ không bị mất dữ liệu nếu hệ điều hành hủy ứng dụng của bạn để giải phóng tài nguyên và ứng dụng của bạn sẽ tiếp tục hoạt động ngay cả khi kết nối mạng yếu hoặc không có kết nối. Các Model là các thành phần chịu trách nhiệm xử lý dữ liệu cho ứng dụng. Chúng độc lập với các View và các app component trong ứng dụng của bạn, do đó chúng bị cô lập khỏi các vấn đề về vòng đời của các thành phần đó. Giữ code giao diện đơn giản và không có logic của ứng dụng làm cho việc quản lý trở nên dễ dàng hơn. Ứng dụng của bạn dựa trên các lớp Model với trách nhiệm quản lý dữ liệu được xác định rõ ràng sẽ làm cho chúng có thể test được và ứng dụng của bạn nhất quán.
Nguyên tắc hướng dẫn
Lập trình là một lĩnh vực sáng tạo, và xây dựng ứng dụng Android không phải là ngoại lệ. Có nhiều cách để giải quyết vấn đề, có thể là liên lạc dữ liệu giữa các Activity hoặc các Fragment, lấy dữ liệu từ xa và duy trì nó ở chế độ ngoại tuyến hoặc bất kỳ một số kịch bản phổ biến khác mà các ứng dụng thường gặp phải.
Mặc dù các khuyến nghị dưới đây là không bắt buộc, nhưng theo kinh nghiệm của Google, việc làm theo chúng sẽ làm cho code của bạn mạnh mẽ hơn, có thể test và maintain trong thời gian dài.
- Các entry point bạn xác định trong Manifest của bạn - các Activity, Service, Broadcast Receiver, v.v ... không phải là nguồn dữ liệu. Thay vào đó, họ chỉ nên phối hợp các tập con của dữ liệu có liên quan đến entry point đó. Vì mỗi thành phần ứng dụng khá ngắn, tùy thuộc vào sự tương tác của người dùng với thiết bị của họ và tình trạng tổng thể hiện tại của hệ điều hành, bạn không muốn bất kỳ entry point nào là nguồn dữ liệu.
- Đừng chần chừ trong việc tạo ra ranh giới trách nhiệm rõ ràng giữa các mô đun khác nhau của ứng dụng. Ví dụ: không lây lan code tải dữ liệu từ mạng qua nhiều lớp hoặc gói trong cơ sở mã của bạn. Tương tự, đừng làm những thứ không liên quan đến trách nhiệm - chẳng hạn như lưu trữ dữ liệu và ràng buộc dữ liệu - vào cùng một lớp.
- Hãy phơi bày càng ít càng tốt từ mỗi mô-đun. Không bị cám dỗ để tạo ra "chỉ một" lối tắt mà phơi bày chi tiết thực hiện nội bộ từ một mô-đun. Bạn có thể đạt được một chút thời gian trong ngắn hạn, nhưng bạn sẽ phải trả nợ kỹ thuật nhiều lần khi codebase của bạn tiến hóa. (Đoạn này khó dịch quá, đại khái ý nó là nên dùng interface thay vì dùng class)
- Khi bạn xác định sự tương tác giữa các mô-đun, hãy suy nghĩ làm thế nào để làm cho mỗi một module có thể test được một cách độc lập. Ví dụ, việc có một API được định nghĩa rõ ràng để lấy dữ liệu từ mạng sẽ làm cho việc kiểm tra mô-đun vẫn tồn tại dữ liệu trong cơ sở dữ liệu cục bộ dễ dàng hơn. Nếu thay vào đó, bạn trộn logic từ hai mô-đun này ở một nơi, hoặc rải rác mã lấy dữ liệu từ mạng của bạn trên toàn bộ cơ sở mã của bạn, sẽ khó khăn hơn - nếu không thể - để kiểm tra.
- Cốt lõi của ứng dụng là cái làm cho nó nổi bật so với phần còn lại. Đừng tốn thời gian của bạn để phát minh lại cái bánh xe hoặc viết lại cùng một mã lệnh. Thay vào đó, hãy tập trung năng lượng tinh thần của bạn vào những gì làm cho ứng dụng của bạn trở nên độc đáo, còn lại hãy để Android Architecture Components và các thư viện được đề xuất khác giải quyết các vấn đề kỹ thuật.
- Lưu giữ càng nhiều dữ liệu liên quan và mới nhất để có thể sử dụng ứng dụng của bạn khi thiết bị đang ở chế độ ngoại tuyến. Mặc dù bạn có thể kết nối liên tục và tốc độ cao nhưng người dùng của bạn có thể không.
- Repository của bạn nên chỉ định một nguồn dữ liệu làm nguồn tin cậy duy nhất. Bất cứ khi nào ứng dụng của bạn cần truy cập vào phần dữ liệu này, nó luôn phải xuất phát từ nguồn đó. Để biết thêm thông tin, xem Single source of truth.
(Còn tiếp) Nguồn: https://developer.android.com/topic/libraries/architecture/index.html