Quy trình và chu kỳ ứng dụng

Trong hầu hết các trường hợp, mọi ứng dụng Android đều chạy trong quy trình Linux riêng. Quy trình này được tạo cho ứng dụng khi một số mã của ứng dụng cần chạy và tiếp tục chạy cho đến khi hệ thống cần thu hồi bộ nhớ để các ứng dụng khác dùng và không cần nữa.

Một tính năng bất thường và cơ bản của Android là vòng đời của một quy trình ứng dụng không do chính ứng dụng đó điều khiển trực tiếp. Thay vào đó, nó được hệ thống xác định thông qua sự kết hợp các phần của ứng dụng mà hệ thống biết là đang chạy, tầm quan trọng của những điều này đối với người dùng và dung lượng bộ nhớ tổng thể còn trống trong hệ thống.

Điều quan trọng là nhà phát triển ứng dụng phải hiểu được các thành phần của ứng dụng (cụ thể là Activity, ServiceBroadcastReceiver) ảnh hưởng như thế nào đến vòng đời của quy trình ứng dụng. Việc không sử dụng các thành phần này đúng cách có thể dẫn đến việc hệ thống buộc tắt quy trình của ứng dụng trong khi đang thực hiện công việc quan trọng.

Một ví dụ phổ biến về lỗi vòng đời quy trình là BroadcastReceiver bắt đầu một luồng khi nhận được Intent trong phương thức BroadcastReceiver.onReceive() rồi trở về từ hàm đó. Sau khi trả về, hệ thống sẽ coi BroadcastReceiver không còn hoạt động và quy trình lưu trữ của nó là không cần thiết nữa, trừ khi các thành phần ứng dụng khác đang hoạt động trong đó.

Vì vậy, hệ thống có thể loại bỏ quy trình này bất cứ lúc nào để lấy lại bộ nhớ và khi làm như vậy, hệ thống sẽ chấm dứt luồng được tạo đang chạy trong quá trình đó. Giải pháp cho vấn đề này thường là lên lịch JobService từ BroadcastReceiver để hệ thống biết rằng có một công việc đang diễn ra trong quá trình này.

Để xác định quy trình nào cần loại bỏ khi sắp hết bộ nhớ, Android sẽ đặt từng quy trình vào một hệ phân cấp mức độ quan trọng dựa trên các thành phần chạy trong đó và trạng thái của các thành phần đó. Sau đây là các loại quy trình sau đây theo mức độ quan trọng:

  1. Quy trình trên nền trước là cần thiết cho công việc mà người dùng hiện đang thực hiện. Nhiều thành phần của ứng dụng có thể khiến quá trình chứa thành phần đó được coi là nền trước theo nhiều cách. Một quy trình được coi là chạy ở nền trước nếu có bất kỳ điều kiện nào sau đây:
  2. Chỉ có một vài quy trình như vậy trong hệ thống và các quy trình này chỉ bị tắt khi không còn cách nào khác nếu bộ nhớ thấp đến mức không thể các quy trình này có thể tiếp tục chạy. Nhìn chung, nếu điều này xảy ra, thiết bị đã đạt đến trạng thái phân trang bộ nhớ, vì vậy, đây là hành động bắt buộc để đảm bảo giao diện người dùng thích ứng.

  3. Một quy trình có thể nhìn thấy đang thực hiện công việc mà người dùng hiện đã biết, vì vậy, việc loại bỏ quy trình này có tác động tiêu cực đáng chú ý đến trải nghiệm người dùng. Một quy trình được coi là hiển thị trong các điều kiện sau:
    • Ứng dụng này đang chạy một Activity mà người dùng nhìn thấy trên màn hình nhưng không hiển thị ở nền trước (phương thức onPause() của ứng dụng này đã được gọi). Điều này có thể xảy ra, chẳng hạn như khi Activity trên nền trước hiển thị dưới dạng một hộp thoại cho phép nhìn thấy Activity trước đó phía sau.
    • Dịch vụ này có một Service đang chạy dưới dạng một dịch vụ trên nền trước thông qua Service.startForeground() (yêu cầu hệ thống coi dịch vụ này là một thứ mà người dùng biết, hoặc về cơ bản là hiển thị).
    • Kho lưu trữ này lưu trữ một dịch vụ mà hệ thống đang sử dụng cho một tính năng cụ thể mà người dùng biết, chẳng hạn như hình nền động hoặc dịch vụ phương thức nhập.

    Số lượng quy trình này chạy trong hệ thống ít bị ràng buộc hơn so với quy trình trên nền trước, nhưng vẫn được kiểm soát tương đối. Các quy trình này được coi là cực kỳ quan trọng và không bị tắt trừ phi cần phải thực hiện việc này để đảm bảo tất cả các quy trình trên nền trước luôn chạy.

  4. Quy trình dịch vụ là một quy trình có Service đã bắt đầu bằng phương thức startService(). Mặc dù người dùng không trực tiếp nhìn thấy các quy trình này, nhưng chúng thường thực hiện những việc mà người dùng quan tâm (chẳng hạn như tải lên hoặc tải dữ liệu mạng ở chế độ nền). Vì vậy, hệ thống luôn duy trì chạy các quy trình đó trừ phi không có đủ bộ nhớ để giữ lại tất cả quy trình đang hiển thị trên nền trước và quy trình hiển thị.

    Các dịch vụ đã chạy trong một thời gian dài (chẳng hạn như 30 phút trở lên) có thể bị giảm hạng mức độ quan trọng để cho phép quy trình của các dịch vụ đó giảm xuống danh sách đã lưu vào bộ nhớ đệm.

    Bạn có thể tạo các quy trình cần chạy trong một thời gian dài bằng setForeground. Nếu đây là một quy trình định kỳ yêu cầu thời gian thực thi nghiêm ngặt, thì bạn có thể lên lịch cho quy trình này thông qua AlarmManager. Để biết thêm thông tin, hãy xem bài viết Hỗ trợ trình thực thi chạy trong thời gian dài. Điều này giúp tránh các trường hợp mà các dịch vụ chạy trong thời gian dài sử dụng quá nhiều tài nguyên, chẳng hạn như bằng cách rò rỉ bộ nhớ, ngăn hệ thống cung cấp trải nghiệm tốt cho người dùng.

  5. Quy trình lưu vào bộ nhớ đệm là quy trình hiện không cần thiết, do đó, hệ thống có thể tuỳ ý loại bỏ quy trình đó khi cần khi cần các tài nguyên như bộ nhớ ở nơi khác. Trong một hệ thống hoạt động bình thường, đây là những quy trình duy nhất liên quan đến việc quản lý tài nguyên.

    Một hệ thống chạy tốt luôn có sẵn nhiều quy trình được lưu vào bộ nhớ đệm, để chuyển đổi hiệu quả giữa các ứng dụng và thường xuyên tắt các ứng dụng đã lưu vào bộ nhớ đệm khi cần. Chỉ trong những tình huống rất nghiêm trọng, hệ thống mới đạt đến điểm mà tất cả các quy trình đã lưu vào bộ nhớ đệm bị tắt và phải bắt đầu loại bỏ các quy trình dịch vụ.

    Vì hệ thống có thể loại bỏ các quy trình đã lưu vào bộ nhớ đệm bất cứ lúc nào, nên các ứng dụng phải ngừng hoạt động khi đang ở trạng thái đã lưu vào bộ nhớ đệm. Nếu ứng dụng phải thực hiện công việc quan trọng đối với người dùng, thì ứng dụng phải sử dụng các API ở trên để chạy công việc ở trạng thái quy trình đang hoạt động.

    Các quy trình trong bộ nhớ đệm thường lưu giữ một hoặc nhiều thực thể Activity hiện không hiển thị cho người dùng (phương thức onStop() đã được gọi và trả về). Miễn là họ triển khai vòng đời Activity đúng cách khi hệ thống loại bỏ các quy trình đó, thì điều này sẽ không ảnh hưởng đến trải nghiệm của người dùng khi quay lại ứng dụng đó. Điều này có thể khôi phục trạng thái đã lưu trước đó khi hoạt động liên quan tạo lại trong một quy trình mới. Xin lưu ý rằng onDestroy() không đảm bảo sẽ được gọi trong trường hợp hệ thống dừng một quy trình. Để biết thêm thông tin, hãy xem Activity.

    Kể từ Android 13, một quy trình của ứng dụng có thể bị giới hạn hoặc không có thời gian thực thi cho đến khi chuyển sang một trong các trạng thái vòng đời hoạt động nêu trên.

    Các quá trình được lưu vào bộ nhớ đệm được lưu giữ trong danh sách. Chính sách thứ tự chính xác cho danh sách này là chi tiết triển khai của nền tảng. Nhìn chung, hệ thống sẽ cố gắng giữ lại các quy trình hữu ích hơn, chẳng hạn như các quy trình lưu trữ ứng dụng trên trang chủ của người dùng hoặc hoạt động gần đây nhất mà người dùng nhìn thấy, trước các loại quy trình khác. Các chính sách khác về việc loại bỏ các quy trình cũng có thể được áp dụng, chẳng hạn như đặt giới hạn cố định về số lượng quy trình được phép hoặc giới hạn khoảng thời gian mà một quy trình có thể lưu liên tục vào bộ nhớ đệm.

Khi quyết định cách phân loại một quy trình, hệ thống sẽ đưa ra quyết định dựa trên cấp quan trọng nhất có trong số tất cả thành phần hiện đang hoạt động trong quy trình đó. Hãy xem tài liệu Activity, ServiceBroadcastReceiver để biết thêm thông tin chi tiết về cách từng thành phần này đóng góp vào vòng đời chung của một quy trình và của ứng dụng.

Mức độ ưu tiên của một quy trình cũng có thể tăng lên dựa trên các phần phụ thuộc khác mà một quy trình có đối với nó. Ví dụ: nếu quy trình A đã liên kết với Service bằng cờ Context.BIND_AUTO_CREATE hoặc đang sử dụng ContentProvider trong quy trình B, thì việc phân loại của quy trình B luôn quan trọng bằng quy trình A.