Khi nào gọi bối cảnh hoạt động HOẶC bối cảnh ứng dụng?


265

Đã có rất nhiều bài đăng về hai bối cảnh này là gì .. Nhưng tôi vẫn không hoàn toàn đúng

Theo tôi hiểu cho đến nay: Mỗi trường hợp là một thể hiện của lớp, điều đó có nghĩa là một số lập trình viên khuyên bạn nên sử dụng this.getApplicationContext()thường xuyên nhất có thể để không "rò rỉ" bất kỳ bộ nhớ nào. Điều này là do cái khác this(lấy Activitybối cảnh thể hiện) chỉ ra một Activitythứ đang bị phá hủy mỗi khi người dùng nghiêng điện thoại hoặc rời khỏi ứng dụng, v.v. Rõ ràng là Garbage Collector (GC) không bắt được và do đó sử dụng quá nhiều bộ nhớ ..

Nhưng bất cứ ai cũng có thể đưa ra một số ví dụ mã hóa thực sự tốt trong đó nó sẽ là thứ phù hợp để sử dụng this(lấy bối cảnh của Activityví dụ hiện tại ) và bối cảnh ứng dụng sẽ vô dụng / sai?

Câu trả lời:


408

getApplicationContext()hầu như luôn luôn sai. Cô Hackborn (trong số những người khác) đã rất rõ ràng rằng bạn chỉ sử dụng getApplicationContext()khi bạn biết lý do tại sao bạn đang sử dụng getApplicationContext()và chỉ khi bạn cần sử dụng getApplicationContext().

Nói thẳng ra, "một số lập trình viên" sử dụng getApplicationContext()(hoặc getBaseContext(), ở mức độ thấp hơn) vì kinh nghiệm Java của họ bị hạn chế. Họ thực hiện một lớp bên trong (ví dụ: một OnClickListenercho một Buttontrong một Activity) và cần một Context. Thay vì sử dụng MyActivity.thisđể có được ở lớp bên ngoài ' this, họ sử dụng getApplicationContext()hoặc getBaseContext()để lấy một Contextđối tượng.

Bạn chỉ sử dụng getApplicationContext()khi bạn biết bạn cần một Contextthứ gì đó có thể sống lâu hơn bất kỳ khả năng nào khác Contextmà bạn có theo ý của bạn. Kịch bản bao gồm:

  • Sử dụng getApplicationContext()nếu bạn cần một cái gì đó gắn liền với Contextchính nó sẽ có phạm vi toàn cầu. Tôi sử dụng getApplicationContext(), ví dụ, trong WakefulIntentService, cho tĩnh WakeLockđược sử dụng cho dịch vụ. Vì đó WakeLocklà tĩnh và tôi cần phải ContextPowerManagerđể tạo ra nó, nên an toàn nhất để sử dụng getApplicationContext().

  • Sử dụng getApplicationContext()khi bạn liên kết với một Servicetừ Activity, nếu bạn muốn chuyển ServiceConnection(nghĩa là xử lý cho ràng buộc) giữa các Activityphiên bản thông qua onRetainNonConfigurationInstance(). Android nội bộ theo dõi các ràng buộc thông qua các liên kết này ServiceConnectionsvà giữ các tham chiếu đến Contextscác liên kết tạo ra các liên kết. Nếu bạn liên kết từ Activity, thì thể hiện mới Activitysẽ có một tham chiếu đến ServiceConnectioncái có tham chiếu ngầm đến cái cũ Activityvà cái cũ Activitykhông thể được thu gom rác.

Một số nhà phát triển sử dụng các lớp con tùy chỉnh Applicationcho dữ liệu toàn cầu của riêng họ mà họ truy xuất thông qua getApplicationContext(). Điều đó chắc chắn là có thể. Tôi thích các thành viên dữ liệu tĩnh, nếu không vì lý do nào khác ngoài bạn chỉ có thể có mộtApplication đối tượng tùy chỉnh . Tôi đã xây dựng một ứng dụng bằng cách sử dụng một Applicationđối tượng tùy chỉnh và thấy nó thật đau đớn. Bà Hackborn cũng đồng ý với vị trí này .

Dưới đây là những lý do tại sao không sử dụng getApplicationContext()bất cứ nơi nào bạn đi:

  • Nó không phải là một hoàn chỉnh Context, hỗ trợ tất cả mọi thứ mà Activitylàm. Nhiều thứ bạn sẽ cố gắng thực hiện với điều này Contextsẽ thất bại, chủ yếu liên quan đến GUI .

  • Nó có thể tạo ra rò rỉ bộ nhớ, nếu Contexttừ getApplicationContext()giữ một thứ gì đó được tạo bởi các cuộc gọi của bạn trên đó mà bạn không dọn dẹp. Với một Activity, nếu nó giữ một cái gì đó, một khi Activityrác được thu gom, mọi thứ khác cũng sẽ tuôn ra. Các Applicationđối tượng vẫn còn trong suốt quá trình của bạn.


1
Cảm ơn bạn rất nhiều vì câu trả lời này. Một liên kết khác tôi tìm thấy trước khi tôi đọc câu trả lời này cũng có thể giúp một số người. stackoverflow.com/questions/7298731/ - liên kết này giải thích mối quan tâm của tôi về việc rò rỉ bộ nhớ.
Norfeldt

27
@Norfeldt: FYI, liên kết trong bình luận của bạn liên kết lại với câu trả lời này.
CommonsWare

2
cảm ơn bạn .. đây là liên kết: stackoverflow.com/questions/5796611/ Khăn nó mô tả rò rỉ bộ nhớ mà tôi sợ nhận được bằng cách sử dụng này
Norfeldt

6
@djaqeel: Phần sau của trích dẫn của bạn gần như đúng. Nó được diễn đạt tốt hơn là "không cung cấp bối cảnh Hoạt động cho một cái gì đó sẽ tồn tại lâu hơn Hoạt động, chẳng hạn như một thành viên dữ liệu tĩnh". Tuy nhiên, bạn vẫn chỉ sử dụng getApplicationContext()khi bạn biết chính xác lý do tại sao bạn cần nó trong một tình huống nhất định. Thổi phồng một bố cục? Sử dụng các hoạt động. Liên kết với một dịch vụ, nơi bạn cần sự ràng buộc đó để tồn tại thay đổi cấu hình? Sử dụng getApplicationContext(), vì vậy các ràng buộc không được gắn với Activitythể hiện.
CommonsWare

7
@Sever: Tôi bao gồm điều này trong câu trả lời của tôi. Dave Smith cũng có một bài đăng blog tuyệt vời bao gồm các bối cảnh: doubleencore.com/2013/06/context Đoạn tóm tắt của anh ấy: "Trong hầu hết các trường hợp, hãy sử dụng Ngữ cảnh có sẵn trực tiếp cho bạn từ thành phần kèm theo mà bạn đang làm việc. Bạn có thể giữ an toàn một tham chiếu đến nó miễn là tham chiếu đó không vượt quá vòng đời của thành phần đó. Ngay khi bạn cần lưu tham chiếu đến Ngữ cảnh từ một đối tượng nằm ngoài Hoạt động hoặc Dịch vụ của bạn, thậm chí tạm thời, hãy chuyển tham chiếu mà bạn lưu qua bối cảnh ứng dụng. "
CommonsWare

48

Tôi nghĩ rằng có rất nhiều thứ được ghi lại kém trên trang web SDK, đây là một trong số đó. Yêu cầu mà tôi sẽ đưa ra là dường như tốt hơn là mặc định sử dụng bối cảnh ứng dụng và chỉ sử dụng bối cảnh hoạt động khi bạn thực sự cần. Nơi duy nhất tôi từng thấy rằng bạn cần một bối cảnh hoạt động là một hộp thoại tiến trình. SBERG412 tuyên bố rằng bạn phải sử dụng bối cảnh hoạt động cho tin nhắn bánh mì nướng, tuy nhiên các tài liệu Android hiển thị rõ ràng bối cảnh ứng dụng đang được sử dụng. Tôi đã luôn sử dụng bối cảnh ứng dụng cho bánh mì nướng vì ví dụ Google này. Nếu làm như vậy là sai, thì Google đã thả quả bóng ở đây.

Đây là nhiều hơn để suy nghĩ và xem xét:

Đối với tin nhắn bánh mì nướng, Google Dev Guide sử dụng bối cảnh ứng dụng và nói rõ ràng là sử dụng nó: Thông báo bánh mì nướng

Trong phần hộp thoại của hướng dẫn Dev, bạn thấy rằng AlertDialog.Builder sử dụng bối cảnh ứng dụng và sau đó thanh tiến trình sử dụng bối cảnh hoạt động. Điều này không được Google giải thích. Đối thoại

Có vẻ như một lý do chính đáng để sử dụng bối cảnh ứng dụng là khi bạn muốn xử lý các thay đổi cấu hình như thay đổi hướng và bạn muốn giữ lại các đối tượng cần bối cảnh như Chế độ xem. Nếu bạn nhìn vào đây: Chạy thay đổi thời gian Có một sự thận trọng về việc sử dụng bối cảnh hoạt động, có thể tạo ra rò rỉ. Điều này có thể tránh được với bối cảnh ứng dụng với các khung nhìn sẽ được giữ lại (ít nhất đó là sự hiểu biết của tôi). Trong một ứng dụng tôi đang viết, tôi dự định sử dụng bối cảnh ứng dụng vì tôi đang cố gắng giữ một số chế độ xem và những thứ khác về thay đổi định hướng và tôi vẫn muốn hoạt động bị phá hủy và được tạo lại khi thay đổi hướng. Do đó, tôi phải sử dụng bối cảnh ứng dụng để không gây rò rỉ bộ nhớ (xem phần Tránh rò rỉ bộ nhớ). Đối với tôi dường như có rất nhiều lý do tốt để sử dụng bối cảnh ứng dụng thay vì bối cảnh hoạt động và đối với tôi, dường như bạn sẽ sử dụng nó thường xuyên hơn bối cảnh hoạt động. Đó là điều mà nhiều cuốn sách Android tôi đã trải qua dường như làm và đó là những gì nhiều ví dụ về Google mà tôi đã thấy.

Tài liệu Google thực sự làm cho nó có vẻ như sử dụng bối cảnh ứng dụng là hoàn toàn tốt trong hầu hết các trường hợp và trên thực tế xuất hiện thường xuyên hơn so với sử dụng bối cảnh hoạt động trong các ví dụ của họ (ít nhất là các ví dụ tôi đã thấy). Nếu đó thực sự là một vấn đề như vậy khi sử dụng bối cảnh ứng dụng, thì Google thực sự cần nhấn mạnh hơn vào vấn đề này. Họ cần phải làm rõ, và họ cần làm lại một số ví dụ của họ. Tôi sẽ không đổ lỗi hoàn toàn cho các nhà phát triển thiếu kinh nghiệm vì chính quyền (Google) thực sự làm cho nó có vẻ như không phải là vấn đề khi sử dụng bối cảnh ứng dụng.


5
Tôi hoàn toàn đồng ý. Câu trả lời của CommonsWare là một chút ngạc nhiên đối với tôi. Tôi rất vui vì đã tìm thấy câu hỏi này, bởi vì tài liệu của Google cho thấy rằng việc sử dụng getApplicationContext có thể rất nguy hiểm.
Steve Schwarcz

38

Tôi đã sử dụng bảng này làm hướng dẫn khi nào nên sử dụng các loại Ngữ cảnh khác nhau, chẳng hạn như bối cảnh Ứng dụng (ví dụ getApplicationContext():) và bối cảnh hoạt động , cũng là bối cảnh BroadcastReceiver :

nhập mô tả hình ảnh ở đây

Tất cả các công đức đi đến tác giả ban đầu ở đây để biết thêm.


11

Sử dụng bối cảnh nào?

Có hai loại bối cảnh:

  1. Bối cảnh ứng dụng được liên kết với ứng dụng và sẽ luôn giống nhau trong suốt vòng đời của ứng dụng - nó không thay đổi. Vì vậy, nếu bạn đang sử dụng Toast, bạn có thể sử dụng bối cảnh ứng dụng hoặc thậm chí bối cảnh hoạt động (cả hai) vì bánh mì nướng có thể được hiển thị từ bất cứ đâu có trong ứng dụng của bạn và không được gắn vào một cửa sổ cụ thể. Nhưng có nhiều trường hợp ngoại lệ, một ngoại lệ là khi bạn cần sử dụng hoặc vượt qua bối cảnh hoạt động.

  2. Bối cảnh hoạt động được liên kết với hoạt động và có thể bị hủy nếu hoạt động bị hủy - có thể có nhiều hoạt động (nhiều khả năng) với một ứng dụng. Và đôi khi bạn hoàn toàn cần xử lý bối cảnh hoạt động. Ví dụ: nếu bạn khởi chạy một hoạt động mới, bạn cần sử dụng bối cảnh hoạt động trong Mục đích của nó để hoạt động khởi chạy mới được kết nối với hoạt động hiện tại về mặt ngăn xếp hoạt động. Tuy nhiên, bạn cũng có thể sử dụng bối cảnh của ứng dụng để khởi chạy một hoạt động mới nhưng sau đó bạn cần đặt cờ Intent.FLAG_ACTIVITY_NEW_TASKnhằm mục đích coi đó là một nhiệm vụ mới.

Hãy xem xét một số trường hợp:

  • MainActivity.this đề cập đến bối cảnh MainActivity mở rộng lớp Activity nhưng lớp cơ sở (hoạt động) cũng mở rộng lớp Ngữ cảnh, vì vậy nó có thể được sử dụng để cung cấp bối cảnh hoạt động.

  • getBaseContext() cung cấp bối cảnh hoạt động.

  • getApplication() cung cấp bối cảnh ứng dụng.

  • getApplicationContext() cũng cung cấp bối cảnh ứng dụng.

Để biết thêm thông tin xin vui lòng kiểm tra liên kết này .


Điều gì về trường hợp một người cần hiển thị AlertDialog trong ứng dụng, ví dụ: Quá trình không đồng bộ hiển thị kết quả. Một ví dụ về điều này có thể là : người dùng nhấp vào tải xuống, điều này sẽ kích hoạt yêu cầu tải xuống downloadmanagervà khi nhận được tín hiệu hoàn thành, nó sẽ hiển thị một hộp thoại, ví dụ: "Bạn muốn làm gì với bản tải xuống này?". Giải pháp (hack) của tôi lưu lại gần đây nhất Activitytrong một static Applicationlớp và yêu cầu hiện tại Activitykhi quá trình tải xuống hoàn tất. Tuy nhiên, tôi nghi ngờ đây là thực hiện đúng. TL; DR Làm cách nào để hiển thị AlertDialog ở mọi nơi trong ứng dụng?
CybeX

@KGCybeX Nếu bạn muốn hiển thị mọi thứ và mọi nơi trong ứng dụng của mình khi quá trình tải xuống hoàn tất, bạn nên đăng ký thủ công máy thu phát trên hoạt động của mình để nghe một thông báo cụ thể rằng dịch vụ tải xuống của bạn sẽ phát và làm bất cứ điều gì bạn muốn khi nhận được tin nhắn hoặc đính kèm hoạt động của bạn với dịch vụ đó trực tiếp.
ExiRouS

6

Tôi đã tự hỏi tại sao không sử dụng Bối cảnh ứng dụng cho mọi hoạt động mà nó hỗ trợ. Cuối cùng, nó làm giảm khả năng rò rỉ bộ nhớ và thiếu kiểm tra null cho getContext () hoặc getActivity () (khi sử dụng bối cảnh ứng dụng được chèn hoặc có được thông qua phương thức tĩnh từ Ứng dụng). Các tuyên bố, giống như tuyên bố của bà Hackborn chỉ sử dụng Bối cảnh ứng dụng nếu cần, dường như không thuyết phục đối với tôi mà không có lời giải thích tại sao. Nhưng có vẻ như tôi đã tìm thấy một lý do tại sao:

đã phát hiện ra rằng có một số vấn đề trên một số kết hợp phiên bản / thiết bị Android không tuân theo các quy tắc này. Chẳng hạn, nếu tôi có BroadcastReceiver được thông qua Bối cảnh và tôi chuyển đổi Bối cảnh đó thành Bối cảnh ứng dụng và sau đó thử gọi registerReceiver () trên Bối cảnh ứng dụng, có nhiều trường hợp điều này hoạt động tốt, nhưng cũng có nhiều trường hợp tôi nhận được một sự cố vì một NhậnCallNot ALLowedException. Các sự cố này xảy ra trên một loạt các phiên bản Android từ API 15 đến 22. https://possiblemobile.com/2013/06/context/#comment-2443283153

Bởi vì không đảm bảo rằng tất cả các hoạt động được mô tả như được hỗ trợ bởi Bối cảnh ứng dụng trong bảng bên dưới sẽ hoạt động trên tất cả các thiết bị Android! nhập mô tả hình ảnh ở đây


4

Hai ví dụ tuyệt vời về thời điểm bạn nên sử dụng bối cảnh Hoạt động so với bối cảnh Ứng dụng là khi hiển thị thông báo Toast hoặc thông báo Hộp thoại tích hợp khi sử dụng bối cảnh Ứng dụng sẽ gây ra ngoại lệ:

ProgressDialog.show(this, ....);

hoặc là

Toast t = Toast.makeText(this,....);

Cả hai đều cần thông tin từ ngữ cảnh Hoạt động không được cung cấp trong ngữ cảnh Ứng dụng.


5
Hừm .. Bạn đã thử nghiệm phiên bản HĐH Android nào? Tôi đã thử nghiệm trên 4.4.4 và nó hoạt động tốt. Thêm vào đó, như @Andi Jay đã đề cập, tài liệu chính thức dành cho nhà phát triển Android đã sử dụng bối cảnh ứng dụng trong mã mẫu của họ. developer.android.com/guide/topics/ui/notifier/ '
17/03/15

1
Tên @Chinese, vâng, nó có thể hoạt động nhưng đôi khi trong tương lai của ứng dụng đó, nó cũng sẽ bị sập. Đã xảy ra với tôi nhiều lần.
Ojonugwa Jude Ochalifu 6/07/2015

1
Khi tôi sử dụng bối cảnh Activity trong Toast, nó sẽ rò rỉ bộ nhớ!
Jemshit Iskenderov

3

Bối cảnh ứng dụng trực tiếp cho đến khi ứng dụng của bạn còn sống duy nhất và nó không phải là phụ thuộc vào Hoạt động Vòng đời nhưng, bối cảnh đối tượng giữ tồn tại lâu dài . Nếu đối tượng mà bạn đang sử dụng tạm thời, thời gian đó sử dụng Bối cảnh ứng dụngBối cảnh hoạt động được sử dụng hoàn toàn trái ngược với Bối cảnh ứng dụng.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.