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 lấy lại bộ nhớ để các ứng dụng khác sử dụng và không cần đến nữa.

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

Điều quan trọng là nhà phát triển ứng dụng phải hiểu cách các thành phần ứng dụng khác nhau (đặc biệt là Activity, ServiceBroadcastReceiver) tác động đến thời gian hoạt động của quy trình ứng dụng. Việc không sử dụng đúng các thành phần này có thể dẫn đến việc hệ thống chấm dứt quy trình của ứng dụng trong khi ứng dụng đ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 BroadcastReceiver không còn cần thiết nữa, trừ phi các thành phần ứng dụng khác đang hoạt động trong đó.

Vì vậy, hệ thống có thể chấm dứt quy trình bất cứ lúc nào để lấy lại bộ nhớ. Khi đó, hệ thống sẽ chấm dứt luồng được tạo đang chạy trong quy 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ó công việc đang diễn ra trong quy trình.

Để xác định quy trình nào cần loại bỏ khi bộ nhớ sắp hết, Android đặ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 đang chạy trong quy trình đó và trạng thái của các thành phần đó. Theo thứ tự quan trọng, các loại quy trình này là:

  1. Quy trình trên nền trước là quy trình bắt buộc đối với những việc người dùng đang làm. Nhiều thành phần ứng dụng có thể khiến quy trình chứa ứng dụng được coi là nền trước theo nhiều cách. Một quy trình được coi là ở nền trước nếu đáp ứng bất kỳ điều kiện nào sau đây:
  2. Hệ thống chỉ có một vài quy trình như vậy và các quy trình này chỉ bị loại bỏ như biện pháp cuối cùng nếu bộ nhớ quá thấp đến mức ngay cả các quy trình này cũng không 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, thao tác này là bắt buộc để duy trì khả năng phản hồi của giao diện người dùng.

  3. Quy trình hiển thị đang thực hiện công việc mà người dùng hiện đang biết, vì vậy, việc loại bỏ quy trình này sẽ ảnh hưởng tiêu cực đáng kể đế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 có thể nhìn thấy trên màn hình nhưng không phải ở nền trước (phương thức onPause() của ứng dụng đã được gọi). Điều này có thể xảy ra, ví dụ: nếu Activity ở nền trước hiển thị dưới dạng hộp thoại cho phép Activity trước đó xuất hiện phía sau.
    • Ứng dụng này có một Service đang chạy dưới dạng 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 dịch vụ mà người dùng biết hoặc về cơ bản là như thể dịch vụ này hiển thị).
    • Ứng dụng này đang 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 các quy trình này chạy trong hệ thống ít bị ràng buộc hơn so với các 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ị loại bỏ trừ phi cần thiết để duy trì tất cả các quy trình trên nền trước.

  4. Quy trình dịch vụ là quy trình chứa một Service đã được bắt đầu bằng phương thức startService(). Mặc dù người dùng không thể 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 xuống dữ liệu mạng ở chế độ nền), vì vậy, hệ thống luôn duy trì các quy trình như vậy đang chạy trừ khi không có đủ bộ nhớ để giữ lại tất cả các quy trình hiển thị và trên nền trước.

    Các dịch vụ đã chạy trong thời gian dài (chẳng hạn như 30 phút trở lên) có thể bị giảm mức độ quan trọng để quá trình của chúng chuyển sang danh sách được 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 đó 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 đó thông qua AlarmManager. Để biết thêm thông tin, hãy tham khảo phần 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 tình huống 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ư rò rỉ bộ nhớ, khiến hệ thống không thể mang lại 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, vì vậy, hệ thống có thể xoá quy trình đó khi cần khi các tài nguyên như bộ nhớ cần được sử dụng ở 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 hoạt động tốt luôn có 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 đóng các ứng dụng đã lưu vào bộ nhớ đệm khi cần. Chỉ trong những trường hợp rất nghiêm trọng, hệ thống mới đến một điểm mà tất cả các quy trình lưu vào bộ nhớ đệm đều bị tắt và hệ thống phải bắt đầu tắt các quy trình dịch vụ.

    Vì hệ thống có thể huỷ 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 mọi hoạt động khi ở 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 một trong các API trên để chạy công việc từ trạng thái quy trình đang hoạt động.

    Các quy trình lưu vào bộ nhớ đệm thường giữ một hoặc nhiều thực thể Activity mà người dùng hiện không nhìn thấy (phương thức onStop() của các thực thể này đã được gọi và trả về). Miễn là họ triển khai vòng đời Activity đúng cách khi hệ thống huỷ các quy trình như vậy, 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 đó. Vòng đời có thể khôi phục trạng thái đã lưu trước đó khi hoạt động liên kết được tạo lại trong một quy trình mới. Xin lưu ý rằng onDestroy() không được đảm bảo sẽ được gọi trong trường hợp hệ thống chấm dứt 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 ứng dụng có thể nhận được thời gian thực thi có giới hạn hoặc không có thời gian thực thi cho đến khi quy trình đó chuyển sang một trong các trạng thái vòng đời đang hoạt động ở trên.

    Các quy trình được lưu vào bộ nhớ đệm được lưu giữ trong một danh sách. Chính sách sắp xếp chính xác cho danh sách này là thông tin triển khai chi tiết của nền tảng. Nhìn chung, trình quản lý này 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 màn hình chính của người dùng hoặc hoạt động gần đây nhất mà người dùng đã xem, trước các loại quy trình khác. Bạn cũng có thể áp dụng các chính sách khác để loại bỏ các quy trình, chẳng hạn như đặt giới hạn cứng về số lượng quy trình được phép hoặc giới hạn thời lượng một quy trình có thể liên tục lưu vào bộ nhớ đệm.

Khi quyết định cách phân loại một quy trình, hệ thống sẽ dựa vào cấp độ quan trọng nhất trong số tất cả các thành phần hiện đang hoạt động trong quy trình đó. Hãy xem tài liệu về Activity, ServiceBroadcastReceiver để biết thêm thông tin chi tiết về cách mỗi thành phần này đóng góp vào vòng đời tổng thể 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à quy trình đó có. 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 quy trình B ít nhất cũng quan trọng như quy trình A.