Chào mọi người, hôm nay mình xin chia sẻ về một chủ đề khá thông dụng trong Android, đó là Context. Mục đích của bài này đâu đấy các bạn mới tiếp cận với Android có thể hiểu đúng bản chất của Context, các loại Context và quan trọng hơn là cách cân nhắc sử dụng chúng trong từng trường hợp một cách hợp lý và chính xác nhất, tránh gây ra những lỗi không mong muốn.
1. Khái niệm
Mình xin trích dẫn một đoạn trong Official Document Android về Context
Mình xin trích dẫn một đoạn trong Official Document Android về Context
Interface to global information about an application environment. This is an abstract class whose implementation is provided by the Android system. It allows access to application-specific resources and classes, as well as up-calls for application-level operations such as launching activities, broadcasting and receiving intents, etc.
Các bạn cũng có thể hiểu cơ bản thế này
Context là trạng thái của ứng dụng tại một thời điểm nhất định.
Context là 1 lớp cơ bản chứa hầu hết các thông tin về môi trường ứng dụng của android, tức là mọi thao tác, tương tác với hệ điều hành đều phải thông qua lớp này.
Context là 1 abstract class, nó cung cấp cho các lớp triển khai các phương thức truy cập vào tài nguyên của ứng dụng và hệ thống. Ví dụ như nó có thể khởi tạo và chạy các activities, broadcast, các intents....
Nào giờ chúng ta xem xét 3 functions hay được sử dụng nhiều nhất để truy xuất Context:
- getContext() — trả về Context được liên kết với Activity được gọi.
- getApplicationContext() — trả về Context được liên kết với Application chứa tất cả các Activity đang chạy bên trong nó.
- getBaseContext() — có liên quan đến ContextWrapper, được tạo xung quanh Context hiện có và cho phép thay đổi hành vi của nó. Với getBaseContext() chúng ta có thể lấy Context hiện có bên trong lớp ContextWrapper.
Trong số này nổi bật hơn cả là getContext() và getApplicationContext(). Liên quan đến getBaseContext(), có một lưu ý nhỏ là tránh sử dụng loại Context này - lớp này được triển khai khi một class extends từ ContextWrapper. Mà lớp này lại có khoảng 40 lớp con trực tiếp và không trực tiếp. Vì vậy, nên gọi trực tiếp đến getContext, Activity, Fragment... để tránh gây ra memory leak.
2. Các loại truy cập Context
2.1. getContext()
Trong getContext(), Context được gắn với một Activity và vòng đời của nó. Chúng ta có thể hình dung Context là layer đứng sau Activity và nó sẽ tồn tại chừng nào Activity còn tồn tại. Thời điểm Activity chết, Context cũng vậy.
Dưới đây là danh sách các chức năng mà Activity’s Context cung cấp cho chúng ta:
Load Resource Values,
Layout Inflation,
Start an Activity,
Show a Dialog,
Start a Service,
Bind to a Service,
Send a Broadcast,
Register BroadcastReceiver.
2.2. getApplicationContext()
Trong getApplicationContext(), Context của chúng ta được gắn với ứng dụng và vòng đời của nó. Chúng ta có thể coi nó như một lớp đằng sau toàn bộ ứng dụng. Miễn là người dùng không tắt ứng dụng, thì nó vẫn tồn tại.
Bây giờ bạn có thể tự hỏi, đâu là sự khác biệt giữa getContext() và getApplicationContext(). Sự khác biệt là Context của ứng dụng không liên quan đến giao diện người dùng. Điều đó có nghĩa là, chúng ta không nên sử dụng nó để Inflate một Layout, start một Activity cũng như Dialog. Về phần còn lại của các chức năng từ Context của Activity, chúng cũng có sẵn trong Application Context. Vì vậy, danh sách các chức năng cho Application Context bao gồm như sau:
Load Resource Values,
Start a Service,
Bind to a Service,
Send a Broadcast,
Register BroadcastReceiver.
3. Cách sử dụng hợp lý Context trong Android
Việc sử dụng Context phù hợp trong Android là rất quan trọng để đảm bảo ứng dụng của bạn hoạt động bình thường và tránh các sự cố như memoryleak hoặc sự cố tài nguyên. Dưới đây là một số nguyên tắc giúp bạn sử dụng ngữ cảnh phù hợp:
- Chọn Context cụ thể theo ngữ cảnh: Chọn Context cung cấp phạm vi cần thiết cho thao tác bạn đang thực hiện. Ví dụ: nếu bạn đang tạo thành phần giao diện người dùng trong một Activity, hãy sử dụng Activity Context thay vì Application Context.
- Lưu ý đến Lifespan của Context: Xem xét vòng đời của thành phần yêu cầu Context. Đối với các hoạt động tồn tại trong thời gian ngắn, hãy sử dụng Context phù hợp với Lifespan của Component. Ví dụ: nếu bạn cần Context trong phương thức onReceive() của BroadcastReceiver, hãy sử dụng tham số Context được cung cấp thay vì Context tồn tại lâu dài như Application Context.
- Tránh sử dụng Context hoạt động ngoài vòng đời của nó: Hãy thận trọng khi sử dụng Context hoạt động bên ngoài vòng đời của Activity, vì nó có thể dẫn đến memoryleak. Chẳng hạn, nếu bạn lưu trữ tham chiếu đến ngữ Activity Context và sử dụng nó sau khi Activity đã bị hủy, thì điều đó có thể gây ra sự cố. Trong những trường hợp như vậy, hãy chuyển sang sử dụng Application Context để thay thế.
- Lưu ý về bộ nhớ: Tránh lưu trữ các tham chiếu lâu dài đến các Context một cách không cần thiết, vì chúng có thể giữ một tham chiếu đến toàn bộ Activity và Application và ngăn các tài nguyên bị thu gom rác. Nếu bạn cần một Context trong một Component tồn tại lâu dài, chẳng hạn như một singleton, hãy ưu tiên sử dụng Application Context.
- Cân nhắc sử dụng Dependency Injection: Việc sử dụng các Dependency Injection như Dagger, Hilt hoặc Koin có thể đơn giản hóa việc quản lý Context bằng cách cung cấp ngữ cảnh phù hợp khi cần. Nó giúp tách rời các thành phần và đảm bảo Context chính xác được đưa vào dựa trên phạm vi và yêu cầu của hoạt động.
4. Kết luận
Việc lựa chọn Context phụ thuộc vào các yêu cầu cụ thể của mã của bạn và thành phần bạn đang xử lý. Điều quan trọng là chọn Context phù hợp để tránh memoryleak, sự cố tài nguyên hoặc sự không nhất quán trong ứng dụng của bạn. Theo nguyên tắc chung, hãy cố gắng sử dụng bối cảnh hạn chế nhất có thể để hoàn thành nhiệm vụ hiện tại, đảm bảo rằng nó có phạm vi và Lifespan cần thiết cho hoạt động.
Mong bài viết này sẽ giúp ích ít nhiều cho các bạn. Hẹn gặp lại mọi người trong bài viết tiếp theo.