เมื่อใดที่จะเรียกบริบทของกิจกรรมหรือบริบทของแอปพลิเคชัน


265

มีการโพสต์เกี่ยวกับบริบทสองอย่างนี้มากมาย แต่ฉันยังไม่เข้าใจ

ตามที่ฉันเข้าใจแล้ว: แต่ละตัวอย่างของคลาสซึ่งหมายความว่าโปรแกรมเมอร์บางคนแนะนำให้คุณใช้this.getApplicationContext()บ่อยที่สุดเท่าที่จะทำได้เพื่อไม่ให้ "รั่วไหล" หน่วยความจำใด ๆ เนื่องจากอื่น ๆthis(รับActivityบริบทของอินสแตนซ์) ชี้ไปActivityที่ถูกทำลายทุกครั้งที่ผู้ใช้เอียงโทรศัพท์หรือออกจากแอพ ฯลฯ ซึ่งเห็นได้ชัดว่า Garbage Collector (GC) ไม่จับและใช้หน่วยความจำมากเกินไป ..

แต่ทุกคนสามารถมาด้วยตัวอย่างการเขียนโค้ดที่ดีจริง ๆ ซึ่งมันเป็นสิ่งที่ถูกต้องที่จะใช้this(รับบริบทของActivityอินสแตนซ์ปัจจุบัน) และบริบทของแอปพลิเคชันจะไร้ประโยชน์ / ผิด

คำตอบ:


408

getApplicationContext()ผิดเกือบทุกครั้ง นางสาว Hackborn (อื่น) ได้รับอย่างชัดเจนมากที่คุณเพียงใช้getApplicationContext()เมื่อคุณรู้ว่าทำไมคุณกำลังใช้getApplicationContext()และเมื่อคุณต้องการgetApplicationContext()ที่จะใช้

หากต้องการทื่อ "โปรแกรมเมอร์บางคน" ใช้getApplicationContext()(หรือgetBaseContext()ในระดับที่น้อยกว่า) เพราะประสบการณ์ Java ของพวกเขามี จำกัด พวกเขาใช้ระดับชั้น (เช่นการOnClickListenerหาButtonในActivity) Contextและความต้องการ แทนที่จะใช้MyActivity.thisเพื่อรับที่ชั้นนอก ' thisพวกเขาใช้getApplicationContext()หรือgetBaseContext()รับContextวัตถุ

คุณจะใช้ก็ต่อgetApplicationContext()เมื่อคุณรู้ว่าคุณต้องการContextสิ่งที่อาจมีชีวิตยืนยาวกว่าที่Contextคุณมี สถานการณ์รวมถึง:

  • ใช้getApplicationContext()ถ้าคุณต้องการบางสิ่งที่เชื่อมโยงกับContextตัวมันเองจะมีขอบเขตทั่วโลก ฉันใช้getApplicationContext()ตัวอย่างเช่นในWakefulIntentServiceสำหรับสแตติกWakeLockที่จะใช้สำหรับบริการ นับได้ว่าWakeLockเป็นแบบคงที่และฉันต้องการContextที่จะได้รับในการสร้างมันก็เป็นที่ปลอดภัยที่สุดในการใช้งานPowerManagergetApplicationContext()

  • ใช้getApplicationContext()เมื่อคุณเชื่อมโยงกับServiceจากActivityถ้าคุณต้องการที่จะผ่านServiceConnection(เช่นจับเพื่อผูกพัน) ระหว่างอินสแตนซ์ผ่านActivity onRetainNonConfigurationInstance()Android ติดตามการผูกภายในผ่านสิ่งเหล่านี้ServiceConnectionsและเก็บการอ้างอิงถึงสิ่งContextsที่สร้างการเชื่อมโยง หากคุณเชื่อมโยงจากอินสแตนซ์Activityใหม่Activityจะมีการอ้างอิงถึงสิ่งServiceConnectionที่มีการอ้างอิงโดยนัยไปที่เก่าActivityและเก่าActivityไม่สามารถรวบรวมขยะได้

นักพัฒนาบางคนใช้ subclasses กำหนดเองสำหรับข้อมูลทั่วโลกของตัวเองซึ่งพวกเขาเรียกผ่านApplication getApplicationContext()นั่นเป็นไปได้อย่างแน่นอน ฉันชอบสมาชิกข้อมูลแบบคงที่หากไม่มีเหตุผลอื่นนอกจากคุณสามารถมีวัตถุที่กำหนดเองได้เพียงรายการเดียว Applicationฉันสร้างแอปหนึ่งโดยใช้Applicationวัตถุที่กำหนดเองและพบว่ามันเจ็บปวด Ms. Hackborn เห็นด้วยกับตำแหน่งนี้

นี่คือเหตุผลที่ทำไมไม่ใช้getApplicationContext()ทุกที่ที่คุณไป:

  • มันไม่สมบูรณ์Contextสนับสนุนทุกอย่างที่Activityทำ สิ่งต่างๆที่คุณจะพยายามที่จะทำอย่างไรกับเรื่องนี้Contextจะล้มเหลวที่เกี่ยวข้องส่วนใหญ่จะกุย

  • มันสามารถสร้างการรั่วไหลของหน่วยความจำหากContextจากการgetApplicationContext()ระงับการสิ่งที่สร้างขึ้นโดยการโทรของคุณในนั้นที่คุณไม่ได้ทำความสะอาด ด้วยการActivityถ้ามันเก็บบางสิ่งบางอย่างเมื่อActivityขยะได้รับการรวบรวมทุกอย่างอื่นก็ล้างออกด้วย Applicationวัตถุซากสำหรับอายุการใช้งานของกระบวนการของคุณ


1
ขอบคุณมากสำหรับคำตอบนี้ ลิงค์อื่นที่ฉันพบก่อนฉันอ่านคำตอบนี้อาจช่วยให้บางคนออกไปด้วย stackoverflow.com/questions/7298731/… - ลิงก์นี้อธิบายข้อกังวลของฉันเกี่ยวกับหน่วยความจำรั่ว
Norfeldt

27
@Norfeldt: FYI ลิงก์ในลิงก์ความคิดเห็นของคุณกลับไปที่คำตอบนี้
CommonsWare

2
ขอบคุณ .. นี่คือลิงค์: stackoverflow.com/questions/5796611/ …มันอธิบายถึงการรั่วไหลของหน่วยความจำที่ฉันกลัวที่จะได้รับจากการใช้สิ่งนี้
Norfeldt

6
@djaqeel: ส่วนท้ายของคำพูดของคุณเกือบเป็นจริง มันเป็นการใช้ถ้อยคำที่ดีกว่าว่า "อย่าให้บริบทของกิจกรรมกับสิ่งที่จะอยู่ได้นานกว่ากิจกรรมที่ต้องการเช่นสมาชิกข้อมูลแบบคงที่" อย่างไรก็ตามคุณจะยังคงใช้งานได้ก็ต่อgetApplicationContext()เมื่อคุณทราบสาเหตุที่คุณต้องการในสถานการณ์ที่กำหนด เค้าโครงพองตัว? ใช้กิจกรรม ผูกไว้กับบริการที่คุณต้องการผูกที่เพื่อความอยู่รอดการเปลี่ยนแปลงการกำหนดค่า? ใช้getApplicationContext()ดังนั้นการรวมจะไม่ผูกกับActivityอินสแตนซ์
CommonsWare

7
@Sever: ฉันครอบคลุมในคำตอบของฉัน Dave Smith ยังมีบล็อกโพสต์ที่ยอดเยี่ยมครอบคลุมบริบท: doubleencore.com/2013/06/contextย่อหน้าสรุปของเขา: "ในกรณีส่วนใหญ่ใช้บริบทที่มีให้คุณโดยตรงจากองค์ประกอบที่คุณทำงานอยู่ภายในคุณสามารถเก็บไว้ได้อย่างปลอดภัย การอ้างอิงถึงตราบเท่าที่การอ้างอิงนั้นไม่ได้ขยายเกินระยะเวลาการใช้งานของส่วนประกอบนั้นทันทีที่คุณจำเป็นต้องบันทึกการอ้างอิงไปยังบริบทจากวัตถุที่อยู่นอกเหนือกิจกรรมหรือบริการของคุณแม้เป็นการชั่วคราวให้เปลี่ยนการอ้างอิงที่คุณบันทึกไว้ ไปที่บริบทแอปพลิเคชัน "
CommonsWare

48

ฉันคิดว่ามีหลายสิ่งหลายอย่างที่มีเอกสารไม่ดีในไซต์ SDK นี่เป็นหนึ่งในนั้น การอ้างสิทธิ์ที่ฉันจะทำคือดูเหมือนว่าจะเป็นการดีกว่าหากคุณเริ่มต้นใช้บริบทของแอปพลิเคชันและใช้บริบทของกิจกรรมเมื่อคุณต้องการจริงๆเท่านั้น ที่เดียวที่ฉันเคยเห็นว่าคุณต้องการบริบทของกิจกรรมนั้นมีไว้สำหรับกล่องโต้ตอบความคืบหน้า SBERG412 อ้างว่าคุณต้องใช้บริบทกิจกรรมสำหรับข้อความขนมปังปิ้ง แต่เอกสาร Android จะแสดงบริบทแอปพลิเคชันที่ใช้อย่างชัดเจน ฉันใช้บริบทแอปพลิเคชันสำหรับขนมปังปิ้งเสมอเนื่องจากตัวอย่างของ Google นี้ หากทำผิด Google จะส่งลูกบอลที่นี่

ต่อไปนี้เป็นสิ่งที่ควรพิจารณาและตรวจทาน:

สำหรับข้อความของขนมปังปิ้ง Google Dev Guide ใช้บริบทของแอปพลิเคชันและพูดอย่างชัดเจนว่าจะใช้: Toast Notifications

ในส่วนข้อความโต้ตอบของคู่มือ Dev คุณจะเห็นว่า AlertDialog.Builder ใช้บริบทของแอปพลิเคชันจากนั้นแถบความคืบหน้าจะใช้บริบทของกิจกรรม นี้ไม่ได้อธิบายโดย Google โต้ตอบ

ดูเหมือนว่าเหตุผลที่ดีในการใช้บริบทของแอปพลิเคชันคือเมื่อคุณต้องการจัดการการเปลี่ยนแปลงการกำหนดค่าเช่นการเปลี่ยนทิศทางและคุณต้องการเก็บรักษาวัตถุที่ต้องการบริบทเช่น Views หากคุณดูที่นี่: การเปลี่ยนแปลงเวลารัน มีความระมัดระวังเกี่ยวกับการใช้บริบทของกิจกรรมซึ่งสามารถสร้างการรั่วไหลได้ สิ่งนี้สามารถหลีกเลี่ยงได้ด้วยบริบทแอปพลิเคชันที่มีมุมมองที่จะรักษาไว้ (อย่างน้อยก็คือความเข้าใจของฉัน) ในแอพที่ฉันเขียนฉันตั้งใจจะใช้บริบทแอปพลิเคชันเนื่องจากฉันพยายามเก็บมุมมองและสิ่งอื่น ๆ เกี่ยวกับการเปลี่ยนแปลงการวางแนวและฉันยังต้องการให้กิจกรรมถูกทำลาย ดังนั้นฉันต้องใช้บริบทของแอพเพื่อไม่ให้หน่วยความจำรั่ว (ดูที่การหลีกเลี่ยงการรั่วไหลของหน่วยความจำ)) สำหรับฉันดูเหมือนว่ามีเหตุผลที่ดีมากมายที่จะใช้บริบทของแอปพลิเคชันแทนที่จะเป็นบริบทของกิจกรรมและสำหรับฉันดูเหมือนว่าคุณจะใช้มันบ่อยกว่าบริบทของกิจกรรม นั่นคือสิ่งที่หนังสือ Android หลายเล่มที่ฉันได้ทำและดูเหมือนว่าเป็นตัวอย่าง Google ที่ฉันได้ทำ

เอกสารของ Google ทำให้ดูเหมือนว่าการใช้บริบทของแอปพลิเคชันนั้นสมบูรณ์แบบในกรณีส่วนใหญ่และในความเป็นจริงแล้วมักปรากฏขึ้นมากกว่าการใช้บริบทของกิจกรรมในตัวอย่างของพวกเขา (อย่างน้อยตัวอย่างที่ฉันเคยเห็น) หากเป็นปัญหาดังกล่าวในการใช้บริบทแอปพลิเคชัน Google จำเป็นต้องให้ความสำคัญกับสิ่งนี้มากขึ้น พวกเขาต้องทำให้ชัดเจนและพวกเขาจำเป็นต้องทำซ้ำตัวอย่างบางส่วน ฉันจะไม่โทษเรื่องนี้กับผู้พัฒนาที่ไม่มีประสบการณ์เนื่องจากผู้มีอำนาจ (Google) ทำให้ดูเหมือนว่าจะไม่มีปัญหาในการใช้บริบทของแอปพลิเคชัน


5
ฉันเห็นด้วยอย่างยิ่ง คำตอบ CommonsWare มาแปลกใจเล็กน้อยสำหรับฉัน ฉันดีใจที่ฉันพบคำถามนี้เนื่องจากเอกสารของ Google แสดงว่าการใช้ getApplicationContext นั้นอาจเป็นอันตราย
Steve Schwarcz

38

ฉันใช้ตารางนี้เป็นแนวทางสำหรับเมื่อใช้บริบทประเภทต่างๆเช่น บริบทแอพลิเคชัน (เช่น: getApplicationContext()) และบริบทกิจกรรมยังบริบท BroadcastReceiver :

ป้อนคำอธิบายรูปภาพที่นี่

ข้อดีทั้งหมดไปที่ผู้เขียนต้นฉบับที่นี่สำหรับข้อมูลเพิ่มเติม


11

บริบทที่จะใช้

บริบทมีสองประเภท:

  1. บริบทของแอปพลิเคชันเกี่ยวข้องกับแอปพลิเคชันและจะเหมือนกันตลอดอายุการใช้งาน - ไม่เปลี่ยนแปลง ดังนั้นหากคุณกำลังใช้ Toast คุณสามารถใช้บริบทแอปพลิเคชันหรือบริบทของกิจกรรม (ทั้งสองอย่าง) เนื่องจาก Toast สามารถแสดงได้จากทุกที่ในแอปพลิเคชันของคุณและไม่ได้เชื่อมต่อกับหน้าต่างที่ระบุ แต่มีข้อยกเว้นมากมายหนึ่งข้อยกเว้นคือเมื่อคุณต้องการใช้หรือผ่านบริบทของกิจกรรม

  2. บริบทกิจกรรมเกี่ยวข้องกับกิจกรรมและสามารถทำลายได้หากกิจกรรมถูกทำลาย - อาจมีหลายกิจกรรม (มากกว่าที่เป็นไปได้) ด้วยแอปพลิเคชันเดียว และบางครั้งคุณต้องมีการจัดการบริบทของกิจกรรม ตัวอย่างเช่นหากคุณเริ่มต้นกิจกรรมใหม่คุณต้องใช้บริบทของกิจกรรมในเจตนาเพื่อให้กิจกรรมการเรียกใช้ใหม่เชื่อมต่อกับกิจกรรมปัจจุบันในแง่ของสแต็กกิจกรรม อย่างไรก็ตามคุณอาจใช้บริบทของแอปพลิเคชันด้วยเพื่อเริ่มกิจกรรมใหม่ แต่คุณต้องตั้งค่าสถานะโดยIntent.FLAG_ACTIVITY_NEW_TASKเจตนาเพื่อให้มันเป็นงานใหม่

ลองพิจารณาบางกรณี:

  • MainActivity.this อ้างถึงบริบท MainActivity ซึ่งขยายคลาสกิจกรรม แต่คลาสพื้นฐาน (กิจกรรม) ยังขยายคลาส Context ดังนั้นจึงสามารถใช้เพื่อเสนอบริบทของกิจกรรม

  • getBaseContext() นำเสนอบริบทกิจกรรม

  • getApplication() นำเสนอบริบทแอปพลิเคชัน

  • getApplicationContext() ยังมีบริบทของแอปพลิเคชัน

สำหรับข้อมูลเพิ่มเติมโปรดตรวจสอบลิงค์นี้


เกี่ยวกับกรณีที่ต้องแสดง AlertDialog ในแอปเช่นกระบวนการ async แสดงผลลัพธ์ ตัวอย่างนี้อาจเป็น : ผู้ใช้คลิกที่ดาวน์โหลดการทำเช่นนี้จะเป็นการร้องขอการดาวน์โหลดdownloadmanagerและเมื่อได้รับสัญญาณเสร็จแล้วควรแสดงกล่องโต้ตอบเช่น "คุณต้องการทำอะไรกับการดาวน์โหลดนี้" โซลูชันของฉัน (แฮ็ค) บันทึกล่าสุดActivityในstatic Applicationชั้นเรียนและขอกระแสActivityเมื่อการดาวน์โหลดเสร็จสมบูรณ์ อย่างไรก็ตามฉันสงสัยว่านี่เป็นการใช้งานที่เหมาะสม TL; DRจะแสดง AlertDialog ได้ทุกที่ในแอพอย่างไร
CybeX

@KGCybeX หากคุณต้องการแสดงทุกอย่างในแอปของคุณเมื่อดาวน์โหลดเสร็จคุณควรลงทะเบียนตัวรับสัญญาณออกอากาศในกิจกรรมของคุณที่รับฟังข้อความเฉพาะที่บริการดาวน์โหลดของคุณจะออกอากาศและทำสิ่งที่คุณต้องการเมื่อได้รับข้อความหรือแนบ กิจกรรมของคุณต่อบริการนั้นโดยตรง
ExiRouS

6

ฉันสงสัยว่าทำไมไม่ใช้ Application Context สำหรับทุกการดำเนินการที่รองรับ ในท้ายที่สุดมันลดโอกาสของการรั่วไหลของหน่วยความจำและการตรวจสอบ null ที่ขาดหายไปสำหรับ getContext () หรือ getActivity () (เมื่อใช้บริบทแอปพลิเคชันแบบฉีดหรือได้มาจากวิธีคงที่จาก Application) ข้อความเช่นMs. Hackborn ที่จะใช้บริบทของแอปพลิเคชันเฉพาะในกรณีที่จำเป็นดูเหมือนจะไม่น่าเชื่อสำหรับฉันโดยไม่มีคำอธิบายว่าทำไม แต่ดูเหมือนว่าฉันได้พบสาเหตุที่ไม่ตอบ:

พบว่ามีปัญหาเกี่ยวกับการรวมกันของรุ่น / อุปกรณ์ Android ที่ไม่เป็นไปตามกฎเหล่านี้ ตัวอย่างเช่นถ้าฉันมี BroadcastReceiver ที่ถูกส่งผ่านบริบทและฉันแปลงบริบทนั้นเป็นบริบทของแอปพลิเคชันแล้วลองเรียก registerReceiver () บนบริบทของแอปพลิเคชันมีหลายกรณีที่มันใช้ได้ดี แต่ก็มีอีกหลายครั้งที่ฉันได้รับ ข้อผิดพลาดเนื่องจาก ReceiverCallNotAllowedException ข้อขัดข้องเหล่านี้เกิดขึ้นใน Android รุ่นต่างๆตั้งแต่ API 15 ถึง 22 https://possiblemobile.com/2013/06/context/#comment-2443283153

เนื่องจากไม่รับประกันว่าการทำงานทั้งหมดที่อธิบายไว้ตาม Application Context ในตารางด้านล่างจะทำงานบนอุปกรณ์ Android ทั้งหมด! ป้อนคำอธิบายรูปภาพที่นี่


4

ตัวอย่างที่ดีสองข้อเมื่อคุณควรใช้บริบทของกิจกรรมกับบริบทของแอปพลิเคชันคือเมื่อแสดงข้อความ Toast หรือข้อความโต้ตอบในตัวเช่นเดียวกับการใช้บริบทของแอปพลิเคชันจะทำให้เกิดข้อยกเว้น:

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

หรือ

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

ข้อมูลความต้องการทั้งคู่จากบริบทของกิจกรรมที่ไม่ได้ระบุไว้ในบริบทของแอปพลิเคชัน


5
หืมม. คุณใช้รุ่น Android OS รุ่นใด? ฉันทดสอบที่ 4.4.4 และใช้งานได้ดี Plus อย่างที่ @Andi Jay พูดถึงเอกสารนักพัฒนา Android อย่างเป็นทางการใช้บริบทแอปพลิเคชันในโค้ดตัวอย่างของพวกเขา developer.android.com/guide/topics/ui/notifiers/
......

1
@ ชื่อจีนใช่มันอาจจะใช้งานได้ แต่บางครั้งในอนาคตของแอพนั้นมันก็จะพังอีกด้วยฉันมาหลายครั้งแล้ว
Ojonugwa Jude Ochalifu

1
เมื่อฉันใช้บริบทของกิจกรรมใน Toast จะทำให้หน่วยความจำรั่ว!
Jemshit Iskenderov

3

บริบทการประยุกต์ใช้ชีวิตอยู่จนกว่าใบสมัครของคุณยังมีชีวิตอยู่เท่านั้นและมันไม่ได้ขึ้นอยู่กับกิจกรรม Life Cycle แต่บริบทวัตถุเก็บระยะยาวอยู่ใน หากวัตถุที่คุณใช้ชั่วคราวเวลานั้นใช้บริบทแอปพลิเคชันและบริบทกิจกรรมจะถูกใช้โดยสิ้นเชิงกับบริบทของแอปพลิเคชัน

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.