Bài học đầu về vòng đời mobile cho lập trình viên web
Theo bản nháp qua lúc ứng dụng ra nền, mất tiến trình và callback lưu đến muộn; phân biệt trạng thái giao diện với nội dung đã commit.

Cho bản nháp trên ứng dụng di động một vòng đời dài hơn màn hình đang chỉnh sửa nó. Với lập trình viên web chuyển sang làm mobile, câu hỏi đầu tiên hữu ích là phần nào còn tồn tại khi ứng dụng không được cấp thời gian thực thi hoặc tiến trình biến mất. Một ô nhập vẫn hiển thị đúng sau khi chuyển qua ứng dụng khác mới chỉ vượt qua một trường hợp: tiến trình ban đầu có thể vẫn còn sống.
Trong quá trình chuyển từ frontend sang công việc full stack rộng hơn, tôi cũng mở rộng sang mobile. Tôi đề xuất làm bài tập này sớm vì nó nối trạng thái giao diện quen thuộc với một trách nhiệm khác: dựng lại công việc của người dùng sau khi tiến trình từng hiển thị nó không còn.
Bài này dùng trình soạn ghi chú hiện trường giả định. Người dùng mong bản nháp đã được lưu rõ ràng sẽ trở lại sau khi tiến trình được tạo lại theo cách thông thường. Bài tập xét phục hồi cục bộ, không xét đồng bộ máy chủ, mất thiết bị hoặc gỡ ứng dụng. Chỉ cần hiểu cơ bản về trạng thái component và công việc bất đồng bộ để theo dõi ví dụ.
Tách màn hình, tiến trình và bản nháp đã lưu
Màn hình giữ chi tiết trình bày như vị trí focus, con trỏ và menu đang mở. Tiến trình giữ cây component đang chạy, đối tượng trong bộ nhớ và callback đang chờ. Bản nháp có thể nằm trong kho lưu trữ cục bộ bền vững, kèm định danh và số phiên bản. Các vòng đời này chồng lấp khi ứng dụng chạy nhưng không thay thế được cho nhau.
Tài liệu vòng đời tiến trình của Android nêu rằng hệ thống có thể kết thúc tiến trình được giữ trong bộ nhớ đệm khi cần tài nguyên, và không bảo đảm gọi onDestroy() trong trường hợp đó. Vì thế, hàm dọn dẹp không phải nơi đáng tin cậy để lần đầu lưu bền vững bản duy nhất của công việc người dùng. Việc thực thi nền cũng bị giới hạn; đặt một timer thông thường không giữ sẵn quyền được chạy trong tương lai.
Thiết kế thực tế là lưu những thay đổi có ý nghĩa của bản nháp khi ứng dụng đang hoạt động, theo chính sách lưu đã xác định. Chuyển sang nền có thể là thêm một dịp yêu cầu lưu. Sự kiện ấy không chứng minh thao tác đã chạy đến khi hoàn tất. Khác biệt này vẫn quan trọng dù chuyển đi rồi quay lại luôn có vẻ hoạt động trên điện thoại dùng để phát triển.
Mũi tên quay về trình soạn là bước đọc và dựng lại trạng thái. Giữ định danh bản nháp chỉ hữu ích nếu vẫn có thể khôi phục nội dung tương ứng theo chính sách lưu trữ của sản phẩm.
Dùng AppState để điều phối hành vi, không để xác nhận đã lưu
React Native AppState cho biết ứng dụng đang active hay ở nền; iOS còn có trạng thái inactive trong lúc chuyển trạng thái và bị gián đoạn. Trên Android, chuyển sang activity khác có thể được tính là background, kể cả activity hệ thống tạm thời như bộ chọn thông tin đăng nhập. Các tín hiệu này giúp quyết định có nên tạm dừng cập nhật hình ảnh hoặc làm mới dữ liệu khi người dùng quay lại.
API cũng phân biệt sự kiện focus và blur trên Android. Kéo bảng thông báo xuống có thể phát blur mà không đổi AppState. Coi mọi lần mất tương tác là chuyển sang nền sẽ trộn những sự kiện khác nhau. Tương tự, kiến trúc legacy được tài liệu mô tả có thể ban đầu trả currentState bằng null trong lúc lấy giá trị từ phía native. “Chưa biết” cần khác với “đang ở nền”.
Chọn sự kiện phù hợp với hành vi cần xử lý. Tạm dừng hoạt ảnh, làm mới màn hình có dữ liệu cũ và commit bản nháp có yêu cầu khác nhau. Thông báo đổi trạng thái cho biết vòng đời ứng dụng; nó không báo giao dịch cơ sở dữ liệu đã commit hay dịch vụ từ xa đã nhận tài liệu. Đăng ký và gỡ listener theo vòng đời component sở hữu nó, đồng thời giữ trách nhiệm quản lý bản nháp ở tầng ứng dụng phù hợp.
Theo lần sửa cuối qua một lần khởi động lại
Giả sử bản nháp note-17 đã commit phiên bản 7 với nội dung “Call supplier”. Trình soạn đổi nó thành phiên bản 8, “Call supplier on Friday”. Các trường hợp sau giả định thao tác commit nguyên tử toàn bộ bản ghi nháp cục bộ, và commit hoàn tất tồn tại qua việc kết thúc tiến trình thông thường. Lần ghi chỉ để lộ bản ghi cũ đầy đủ hoặc bản ghi mới đầy đủ, không có bản ghi dở dang. Lỗi lưu trữ, hỏng dữ liệu và bảo đảm khi thiết bị mất điện cần được kiểm tra riêng với cách lưu trữ đã chọn.
| Sự kiện trước khi tiến trình kết thúc | Tiến trình mới có thể khôi phục gì? |
|---|---|
| Phiên bản 8 chỉ có trong bộ nhớ của trình soạn | Phiên bản 7 |
| Đã yêu cầu ghi phiên bản 8 nhưng chưa biết kết quả | Phiên bản 7 hoặc 8; phải đọc để xác định |
| Phiên bản 8 đã commit và giao diện nhận được xác nhận | Phiên bản 8 |
| Phiên bản 8 đã commit nhưng tiến trình kết thúc trước callback của giao diện | Phiên bản 8 |
Hàng cuối dễ bị bỏ sót. Callback là cách giao diện biết kết quả ghi; nó không phải thứ làm lần ghi trở nên bền vững. Ngược lại, yêu cầu ghi không đồng nghĩa với ghi đã commit. Khi khởi động lại, đọc bản ghi đã lưu thay vì suy ra sự chắc chắn từ vòng quay chờ hoặc nhãn trạng thái xuất hiện lần cuối.
Bây giờ cho người dùng nhập phiên bản 9 trong lúc phiên bản 8 còn đang lưu. Thông báo hoàn tất của phiên bản 8 không được làm phiên bản 9 mang nhãn đã lưu. Gắn kết quả hoàn tất với đúng phiên bản mà nó bao phủ rồi so với phiên bản hiện tại của trình soạn. Đường ghi dữ liệu cũng cần quy tắc thứ tự: thực hiện các lần ghi tuần tự hoặc áp dụng điều kiện phiên bản phù hợp để lần ghi cũ không thay thế lần mới. Chỉ sửa nhãn giao diện không khắc phục được kho dữ liệu bị ghi sai thứ tự.
Chọn chính sách lưu mà sản phẩm giải thích được
Ghi sau mỗi phím bấm có thể tạo công việc lưu trữ không cần thiết. Debounce, tức chờ một khoảng ngắn sau khi người dùng ngừng sửa rồi mới ghi, giảm công việc đó nhưng cố ý tạo khoảng thời gian lần sửa mới chỉ có trong bộ nhớ. Nếu tiến trình kết thúc trong khoảng ấy, bản nháp được lưu xong gần nhất có thể trở lại mà thiếu chữ mới nhập. Nêu rõ đánh đổi này thay vì mô tả một khoảng debounce tùy chọn là cơ chế tự lưu có bảo đảm.
Trình soạn nhỏ có thể hiển thị “Đang lưu trên thiết bị này” cho tới khi lần ghi cục bộ hoàn tất, rồi đổi thành “Đã lưu trên thiết bị này”. Một hành động riêng có thể gửi ghi chú lên máy chủ. Nếu kho cục bộ từ chối lần ghi, giữ nội dung hiện tại trên màn hình và cho người dùng cách thử lại hoặc sao chép. Từ “đã lưu” cần mô tả thao tác hoàn tất mà ứng dụng thực sự biết kết quả.
Tệp đính kèm lớn cần trạng thái hoàn tất riêng. Bản ghi văn bản đã lưu nhưng trỏ tới tệp còn đang sao chép chưa chứng minh tệp sẽ có sau khi khởi động lại. Với bài tập đầu, giới hạn bản nháp ở văn bản. Khi sản phẩm cần tệp đính kèm, thêm việc lưu tệp thành trường hợp nghiệm thu riêng.
Phân biệt trạng thái giao diện được giữ lại với nội dung bền vững
Hướng dẫn lưu trạng thái giao diện của Android phân biệt trạng thái ViewModel, saved state và kho lưu trữ bền vững. ViewModel có thể tồn tại qua thay đổi cấu hình nhưng tự nó không tồn tại qua việc hệ thống kết thúc tiến trình. Cơ chế saved state có thể giúp dựng lại phần trạng thái giao diện nhỏ sau khi mất tiến trình; nó không thay thế lưu trữ dài hạn cho dữ liệu ứng dụng.
Với trình soạn Android native, định danh bản nháp nhỏ hoặc lựa chọn hiện tại có thể nằm trong cơ chế saved state phù hợp, còn nội dung bản nháp được đọc lại từ kho cục bộ. Hướng dẫn cũng nêu điều kiện vòng đời để dữ liệu saved state được ghi nhận. Đừng coi việc gán saved state là lần ghi xuống đĩa ngay lập tức và vô điều kiện. Sự kiện AppState của React Native là API khác và không tự triển khai những cơ chế lưu trữ Android đó.
Kho bền vững của ứng dụng vẫn có giới hạn. Xóa dữ liệu ứng dụng hoặc gỡ ứng dụng có thể làm mất bản nháp cục bộ. Nếu sản phẩm hứa khôi phục trên thiết bị khác, nó cần thiết kế chuyển hoặc đồng bộ riêng. Bài tập khởi động lại cục bộ không chứng minh được lời hứa ấy.
Kiểm tra riêng từng loại sự kiện
Bắt đầu từ bản nháp 7, sửa thành 8 rồi quan sát phiên bản được lưu sau khi thao tác lưu cục bộ hoàn tất. Sau đó kiểm tra riêng việc quay lại từ nền khi tiến trình còn sống, tạo lại màn hình và khởi động lại sau khi tiến trình bị kết thúc. Dùng công cụ được nền tảng đã chọn mô tả để phân biệt các trường hợp. Xóa ứng dụng khỏi danh sách tác vụ gần đây, xoay thiết bị và kết thúc tiến trình không phải các phép thử thay thế được cho nhau.
Với trường hợp bất đồng bộ, dùng bộ chuyển tiếp lưu trữ dành cho kiểm thử có thể tạm dừng trước commit và sau commit nhưng trước khi kết quả hoàn tất đến giao diện. Kết thúc hoặc tạo lại trình soạn tại mỗi ranh giới, rồi đọc qua luồng khởi động thông thường. Đây là kế hoạch kiểm thử đề xuất; bài này không chạy môi trường mobile. Ghi nhận cả nội dung được khôi phục lẫn nhãn trạng thái, gồm trường hợp phiên bản 9, thay vì coi màn hình render thành công là bằng chứng đủ.
Kết quả học là một cam kết phục hồi nhỏ: lần sửa nào đã commit, nó nằm ở đâu, tiến trình mới tìm nó thế nào và người dùng thấy gì khi kết quả còn chưa rõ. Khi trả lời được những câu hỏi đó, sự kiện foreground và background trở thành tín hiệu phối hợp hữu ích thay vì thứ duy nhất bảo vệ bản nháp.