getApplication () กับ getApplicationContext ()


417

ฉันไม่สามารถหาคำตอบที่น่าพอใจได้ดังนั้นเราไปที่: จัดการกับอะไรActivity/Service.getApplication()และContext.getApplicationContext()?

ในแอปพลิเคชันของเราทั้งคู่จะส่งคืนวัตถุเดียวกัน ActivityTestCaseอย่างไรก็ตามในการเยาะเย้ยแอปพลิเคชันจะgetApplication()กลับมาพร้อมกับการเยาะเย้ย แต่getApplicationContextจะยังคงกลับตัวอย่างบริบทที่แตกต่างกัน (หนึ่งฉีดโดย Android) นั่นเป็นข้อบกพร่องหรือไม่? มันตั้งใจหรือไม่

ฉันไม่เข้าใจความแตกต่างตั้งแต่แรก มีกรณีนอกชุดทดสอบที่การโทรทั้งสองอาจกลับมาพร้อมกับวัตถุที่แตกต่างกันหรือไม่? เมื่อใดและเพราะเหตุใด ยิ่งไปกว่านั้นคือเหตุผลที่getApplicationกำหนดไว้บนActivityและServiceแต่ไม่ได้อยู่Context? ไม่ควรมีอินสแตนซ์แอปพลิเคชันที่ใช้ได้จากทุกที่ใช่หรือไม่


8
เป็นคำถามที่ดี การทดสอบเป็นเรื่องลึกลับ (อย่างที่คุณรู้) แต่ฉันสงสัยว่าความแตกต่างใด ๆ ปรากฏตัวในสองวิธีนี้หากคุณไม่ได้สร้างApplicationวัตถุในแอปของคุณอย่างชัดเจน
Christopher Orr

คำตอบ:


366

คำถามที่น่าสนใจมาก ฉันคิดว่ามันเป็นความหมายเชิงความหมายส่วนใหญ่และอาจเป็นเพราะเหตุผลทางประวัติศาสตร์

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

ดังนั้นหากคุณต้องการคลาสแอปพลิเคชันที่คุณลงทะเบียนใน Manifest คุณไม่ควรโทรgetApplicationContext()และส่งไปยังแอปพลิเคชันของคุณเพราะมันอาจไม่ใช่อินสแตนซ์ของแอปพลิเคชัน

ทำไม getApplicationContext()มีอยู่ในสถานที่แรก?

getApplication() มีเฉพาะในคลาสกิจกรรมและคลาสบริการเท่านั้น getApplicationContext()มีการประกาศในคลาสบริบท

นั่นหมายถึงสิ่งหนึ่ง: เมื่อเขียนโค้ดในเครื่องรับสัญญาณออกอากาศซึ่งไม่ใช่บริบท แต่ได้รับบริบทในวิธีการ onReceive คุณสามารถโทรได้เท่านั้น getApplicationContext()ของคุณสามารถโทรออกเท่านั้น ซึ่งหมายความว่าคุณไม่รับประกันว่าจะสามารถเข้าถึงแอปพลิเคชันของคุณใน BroadcastReceiver

เมื่อดูรหัส Android คุณจะเห็นว่าเมื่อแนบมาแล้วกิจกรรมจะได้รับบริบทพื้นฐานและแอปพลิเคชันและสิ่งเหล่านั้นเป็นพารามิเตอร์ที่แตกต่างกัน ผู้ได้รับมอบหมายก็เรียกร้องให้getApplicationContext()baseContext.getApplicationContext()

อีกอย่างหนึ่ง: เอกสารบอกว่าส่วนใหญ่คุณไม่จำเป็นต้องใช้คลาสย่อย:

โดยปกติแล้วไม่มีต้อง Applicationsubclass ในสถานการณ์ส่วนใหญ่ Singletons แบบคงที่สามารถให้ฟังก์ชั่นเดียวกันในทางโมดูลาร์มากขึ้น หากซิงเกิลของคุณต้องการบริบทส่วนกลาง (ตัวอย่างเช่นในการลงทะเบียนเครื่องรับสัญญาณออกอากาศ) ฟังก์ชั่นเพื่อดึงข้อมูลสามารถกำหนดสิ่ง Contextที่ใช้ภายในContext.getApplicationContext()เมื่อสร้างซิงเกิลครั้งแรก

ฉันรู้ว่านี่ไม่ใช่คำตอบที่แน่นอนและแม่นยำ แต่ถึงกระนั้นนั่นก็ตอบคำถามของคุณได้หรือไม่?


89
@ Piwaï: อย่าฟังเอกสาร การทำคลาสย่อยandroid.app.Applicationนั้นมีประโยชน์อย่างเต็มที่ ตัวอย่างเช่นฉันมีปัญหาในการเริ่มต้นฐานข้อมูล เมื่อเข้ามาApplication.onCreateมันก็ทำงานเหมือนมีเสน่ห์ ตอนนี้ฉันทำการเริ่มต้นทั้งระบบอย่างกว้างขวางApplicationและฉันจะไม่เขียนแอปอื่นโดยไม่ใช้
Martin

9
@Martin การฟังเอกสารโดยทั่วไปหมายความว่ารหัสของคุณอาจแตกหักในอนาคตหรือแม้กระทั่งในขณะนี้ภายใต้เงื่อนไขที่ไม่คาดคิดสูญเสียการพกพาทำงานได้ไม่ดีป้องกันไม่ให้นักพัฒนาแพลตฟอร์มทำการเปลี่ยนแปลงที่เป็นประโยชน์ (ซึ่งทำลายสมมติฐานที่คุณทำผิด ขึ้นอยู่กับการใช้งานในปัจจุบันไม่ใช่เอกสาร) ฉันคิดว่านี่เป็นพฤติกรรมที่ไม่ดีและเป็นคำแนะนำที่แย่มาก
Palec

17
@Palec:“ ตามปกติแล้วไม่จำเป็นต้องใช้คลาสย่อย” - นั่นเป็นเพียงคำใบ้ ฉันยังคงใช้ฟังก์ชั่นที่ทำเป็นเอกสารอย่างเป็นทางการตามที่ตั้งใจไว้ - ฉันเคยใช้ "singletons คงที่" เหล่านั้นในตอนแรกและพวกเขากลายเป็นความเจ็บปวดในการ ... - การเริ่มต้นขี้เกียจมีปัญหา โดยเฉพาะอย่างยิ่งเมื่อใช้กับการทดสอบเครื่องมือ - ฉันยังมีซิงเกิลตันสำหรับโมดุล แต่ฉันสร้างอินสแตนซ์เหล่านั้นบล็อกใน onCreate ของคลาสย่อย android.app.Application - ทำงานเหมือนจับใจ
Martin

9
@ มาร์ตินฉันควรทำให้ชัดเจน: ปฏิกิริยาของฉันเกี่ยวข้องกับประโยคแรกเท่านั้น “ อย่าฟังหมอ” โดยทั่วไปเป็นคำแนะนำที่อันตรายมาก แต่“ นี่เป็นเพียงคำใบ้ - คุณสามารถเพิกเฉยต่อหมอในกรณีนี้ถ้าคุณมีเหตุผลและฉันจะแสดงให้คุณ…” ฟังดูดีสำหรับฉัน
Palec

3
"เมื่อเขียนโค้ดในเครื่องรับสัญญาณออกอากาศซึ่งไม่ใช่บริบท แต่ได้รับบริบทในวิธีการของ OnReceive คุณสามารถเรียกใช้ getApplicationContext () เท่านั้นซึ่งหมายความว่าคุณไม่รับประกันว่าจะสามารถเข้าถึงแอปพลิเคชันของคุณใน BroadcastReceiver " .So เราจะทำอย่างไรเพื่อเข้าถึงคลาสแอปพลิเคชันของฉันใน BroadcastReceiver
Dr.jacky

30

เปรียบเทียบและgetApplication()getApplicationContext()

getApplicationส่งกลับApplicationวัตถุซึ่งจะช่วยให้คุณสามารถจัดการกับสถานะโปรแกรมทั่วโลกของคุณและตอบสนองต่อสถานการณ์บางอย่างเช่นอุปกรณ์และonLowMemory()onConfigurationChanged()

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

ฉันเดาว่ามันขึ้นอยู่กับว่าโค้ดของคุณทำอะไรไม่ว่าจะเป็นแบบนี้หรืออาจจะไม่เหมือนกัน - แม้ว่าในการใช้งานปกติฉันคาดหวังให้มันแตกต่างกัน


19
แต่Application เป็นContext (มันสืบทอดจากมัน) และที่รันไทม์วิธีการทั้งสองกลับมาเช่นเดียวกัน ดังนั้นความแตกต่างคืออะไร?
Matthias

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

16
ฉันคิดว่าคุณอาจจะอ่านคำถามผิด ฉันไม่ได้ขอความแตกต่างระหว่างActivityบริบทและApplicationบริบท ฉันไตร่ตรองความแตกต่างระหว่างApplication(ซึ่งเป็นบริบทแอปพลิเคชันระดับโลกที่ไม่ซ้ำใคร) และสิ่งใดก็ตามที่getApplicationContextคืน อันที่จริงแล้วมันใช้งานไม่ได้ก่อน Android 1.6; nullมันเคยเสมอกลับ
Matthias

1
@ Matias ในความคิดของฉันมันยังคงมีความเกี่ยวข้อง บริบทถูกฉีด (นำไปใช้) โดยระบบ Android เองขณะที่แอปพลิเคชันสืบทอดและขยายบริบท คลาสแอปพลิเคชันสามารถเยาะเย้ยได้อย่างง่ายดาย (ดังที่คุณพูด) แล้วมันไม่ใช่การเดิมพันที่ปลอดภัยที่แสดงให้เห็นว่าคลาสแอปพลิเคชันทำ "เวทมนต์" (ในโครงการทดสอบ) เพื่อให้บรรลุ
Audrius

3
มาอีกครั้ง? ฉันขอโทษฉันยังไม่เห็นว่าจะตอบคำถามของฉันได้อย่างไร
Matthias

30

ดูเหมือนว่าจะเกี่ยวข้องกับการตัดคำบริบท คลาสที่ได้รับส่วนใหญ่มาจากความContextเป็นจริงContextWrapperซึ่งโดยหลักแล้วจะมอบหมายให้กับบริบทอื่นอาจมีการเปลี่ยนแปลงโดยเสื้อคลุม

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

กิจกรรมและบริการเกี่ยวข้องกับApplicationวัตถุโดยเฉพาะ ฉันเชื่อว่าประโยชน์ของสิ่งนี้คือคุณสามารถสร้างและลงทะเบียนในคลาสที่กำหนดเองที่ได้มาจากApplicationและให้แน่ใจว่าActivity.getApplication()หรือService.getApplication()จะส่งคืนวัตถุเฉพาะของประเภทนั้นซึ่งคุณสามารถส่งไปยังที่ได้รับมาApplicationการเรียนและการใช้งานสำหรับสิ่งที่ วัตถุประสงค์ที่กำหนดเอง

กล่าวอีกนัยหนึ่งgetApplication()คือรับประกันการส่งคืนApplicationวัตถุในขณะที่getApplicationContext()มีอิสระในการส่งคืนพร็อกซีแทน


เมื่อคุณพูดว่า "บริบทเป็นสิ่งที่เป็นนามธรรมโดยทั่วไปที่สนับสนุนการล้อเลียนและการมอบฉันทะ" คุณหมายถึงอะไรโดย "ผู้รับมอบฉันทะ" อย่างแน่นอน? คุณช่วยชี้ฉันไปที่การอ้างอิงบางอย่างได้ไหม? ฉันพบว่าสิ่งที่บริบททั้งหมดค่อนข้างซับซ้อน
Tiago

@Tiago คำตอบนี้จะช่วยให้คุณเข้าใจได้ดีขึ้น: stackoverflow.com/questions/10641144/…
superuser

-13

หากต้องการตอบคำถาม getApplication () จะส่งคืนแอปพลิเคชันวัตถุและ getApplicationContext () จะส่งคืนออบเจ็กต์บริบท จากการสังเกตของคุณเองฉันจะสมมติว่าบริบทของทั้งสองนั้นเหมือนกัน (เช่นเบื้องหลังฉากที่คลาสแอปพลิเคชันเรียกใช้ฟังก์ชันหลังเพื่อเติมส่วนบริบทของคลาสพื้นฐานหรือดำเนินการเทียบเท่าบางอย่าง) ไม่ควรสำคัญว่าฟังก์ชั่นใดที่คุณเรียกใช้ถ้าคุณต้องการบริบท

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