ในการเขียนโปรแกรม Android ทั่วไป ตัวจัดการมักใช้เมื่อดำเนินการแบบอะซิงโครนัสและประมวลผลผลลัพธ์ที่ส่งคืน โดยปกติโค้ดของเราจะถูกนำไปใช้ในลักษณะนี้
คัดลอกรหัสรหัสดังต่อไปนี้:
SampleActivity คลาสสาธารณะขยายกิจกรรม {
ตัวจัดการสุดท้ายส่วนตัว mLeakyHandler = ตัวจัดการใหม่ () {
@แทนที่
โมฆะสาธารณะ handleMessage (ข้อความข้อความ) {
-
-
-
-
อย่างไรก็ตาม ที่จริงแล้ว โค้ดด้านบนอาจทำให้หน่วยความจำรั่วไหล เมื่อคุณใช้เครื่องมือ Android Lint คุณจะได้รับคำเตือนดังกล่าว
คัดลอกรหัสรหัสดังต่อไปนี้:
ใน Android คลาส Handler ควรเป็นแบบคงที่หรืออาจเกิดการรั่วไหล ข้อความที่เข้าคิวบน MessageQueue ของเธรดแอปพลิเคชันจะคงตัวจัดการเป้าหมายไว้เช่นกัน หาก Handler เป็นคลาสภายใน คลาสภายนอกจะถูกเก็บไว้เช่นกัน เพื่อหลีกเลี่ยงการรั่วไหลของคลาสภายนอก ประกาศตัวจัดการเป็นคลาสที่ซ้อนกันแบบสแตติกโดยมี WeakReference ให้กับคลาสภายนอก
เห็นแบบนี้แล้วคุณอาจจะยังสับสนอยู่ว่าตรงไหนในโค้ดที่อาจทำให้หน่วยความจำรั่ว และจะทำให้หน่วยความจำรั่วได้อย่างไร? แล้วมาวิเคราะห์กันอย่างช้าๆ
1. เมื่อแอปพลิเคชัน Android เริ่มต้นขึ้น อินสแตนซ์ Looper จะถูกสร้างขึ้นโดยอัตโนมัติเพื่อใช้โดยเธรดหลักของแอปพลิเคชัน งานหลักของ Looper คือการประมวลผลออบเจ็กต์ข้อความในคิวข้อความทีละรายการ ใน Android กิจกรรมทั้งหมดของเฟรมเวิร์ก Android (เช่น การเรียกวิธีวงจรชีวิตกิจกรรม และการคลิกปุ่ม ฯลฯ) จะถูกใส่ลงในข้อความ จากนั้นจึงเพิ่มลงในคิวข้อความที่ Looper จะต้องประมวลผล และ Looper จะรับผิดชอบในการประมวลผลเหตุการณ์เหล่านั้น โดยหนึ่ง วงจรชีวิตของ Looper ในเธรดหลักจะยาวนานเท่ากับแอปพลิเคชันปัจจุบัน
2. เมื่อตัวจัดการเริ่มต้นในเธรดหลัก และเราส่งข้อความที่กำหนดเป้าหมายตัวจัดการนี้ไปยังคิวข้อความที่ประมวลผลโดย Looper ข้อความที่ส่งจริงๆ แล้วมีการอ้างอิงถึงอินสแตนซ์ของตัวจัดการ ในลักษณะนี้เท่านั้นที่จะประมวลผล Looper เมื่อสิ่งนี้เท่านั้น ได้รับข้อความแล้ว สามารถเรียก Handler#handleMessage(Message) เพื่อให้การประมวลผลข้อความถูกต้องสมบูรณ์
3. ใน Java คลาสภายในที่ไม่คงที่และคลาสภายในที่ไม่ระบุชื่อมีการอ้างอิงถึงคลาสภายนอกโดยปริยาย คลาสภายในแบบคงที่ไม่มีการอ้างอิงถึงคลาสภายนอก สำหรับข้อมูลเพิ่มเติมเกี่ยวกับเรื่องนี้ โปรดดู Chat Java: ตัวแก้ไขส่วนตัว "ไม่ถูกต้อง"
เป็นเรื่องจริงที่ตัวอย่างโค้ดข้างต้นตรวจพบหน่วยความจำรั่วได้ยากเล็กน้อย ดังนั้นตัวอย่างต่อไปนี้จึงชัดเจนมาก
คัดลอกรหัสรหัสดังต่อไปนี้:
SampleActivity คลาสสาธารณะขยายกิจกรรม {
ตัวจัดการสุดท้ายส่วนตัว mLeakyHandler = ตัวจัดการใหม่ () {
@แทนที่
โมฆะสาธารณะ handleMessage (ข้อความข้อความ) {
-
-
-
@แทนที่
ป้องกันเป็นโมฆะ onCreate (บันเดิลที่บันทึกไว้InstanceState) {
super.onCreate(savedInstanceState);
// โพสต์ข้อความและชะลอการดำเนินการเป็นเวลา 10 นาที
mLeakyHandler.postDelayed (รันใหม่ () {
@แทนที่
การรันโมฆะสาธารณะ () { /* ... */ }
}, 1,000 * 60 * 10);
// กลับไปยังกิจกรรมก่อนหน้า
เสร็จ();
-
-
จากการวิเคราะห์โค้ดข้างต้น เมื่อเราดำเนินการตามวิธีการเสร็จสิ้นของกิจกรรม ข้อความที่ล่าช้าจะอยู่ในคิวข้อความเธรดหลักเป็นเวลา 10 นาทีก่อนที่จะถูกประมวลผล และข้อความนี้มีการอ้างอิงถึงตัวจัดการ และตัวจัดการนั้นไม่ระบุชื่อ อินสแตนซ์ของ คลาสภายในมีการอ้างอิงถึง SampleActivity ภายนอก ดังนั้นสิ่งนี้จึงทำให้ SampleActivity ไม่สามารถรีไซเคิลได้ ด้วยเหตุนี้ ทรัพยากรจำนวนมากที่เก็บไว้โดย SampleActivity จึงไม่สามารถรีไซเคิลได้ นี่คือสิ่งที่เรามักเรียกว่าหน่วยความจำรั่ว
โปรดทราบว่า Runnable ใหม่ด้านบนยังถูกนำไปใช้โดยคลาสภายในที่ไม่ระบุชื่ออีกด้วย นอกจากนี้ยังมีการอ้างอิงถึง SampleActivity และป้องกันไม่ให้ SampleActivity ถูกนำกลับมาใช้ใหม่
เพื่อแก้ไขปัญหานี้ แนวคิดคือไม่ใช้คลาสภายในที่ไม่คงที่ เมื่อสืบทอด Handler ให้วางมันไว้ในไฟล์คลาสที่แยกจากกัน หรือใช้คลาสภายในแบบคงที่ เนื่องจากคลาสภายในแบบคงที่ไม่มีการอ้างอิงถึงคลาสภายนอก จึงจะไม่ทำให้หน่วยความจำรั่วไหลของอินสแตนซ์คลาสภายนอก เมื่อคุณต้องการเรียกกิจกรรมภายนอกในคลาสภายในแบบคงที่ เราสามารถใช้การอ้างอิงที่อ่อนแอเพื่อจัดการได้ นอกจากนี้ Runnable ยังต้องตั้งค่าเป็นแอตทริบิวต์สมาชิกแบบคงที่ด้วย หมายเหตุ: อินสแตนซ์คลาสภายในที่ไม่ระบุชื่อคงที่ไม่มีการอ้างอิงถึงคลาสภายนอก รหัสที่จะไม่ทำให้หน่วยความจำรั่วหลังจากการดัดแปลงมีดังนี้:
คัดลอกรหัสรหัสดังต่อไปนี้:
SampleActivity คลาสสาธารณะขยายกิจกรรม {
-
* อินสแตนซ์ของคลาสภายในแบบคงที่ไม่ถือเป็นนัย
* อ้างอิงถึงชนชั้นนอกของพวกเขา
-
คลาสคงที่ส่วนตัว MyHandler ขยายตัวจัดการ {
WeakReference สุดท้ายส่วนตัว mActivity <SampleActivity>;
MyHandler สาธารณะ (กิจกรรมตัวอย่าง) {
mActivity = WeakReference ใหม่ <SampleActivity>(กิจกรรม);
-
@แทนที่
โมฆะสาธารณะ handleMessage (ข้อความข้อความ) {
กิจกรรม SampleActivity = mActivity.get();
ถ้า (กิจกรรม != null) {
-
-
-
-
MyHandler สุดท้ายส่วนตัว mHandler = MyHandler ใหม่ (สิ่งนี้);
-
* อินสแตนซ์ของคลาสที่ไม่เปิดเผยตัวตนไม่ถือเป็นนัย
* อ้างอิงถึงคลาสภายนอกเมื่อเป็นแบบ "คงที่"
-
Runnable สุดท้ายแบบคงที่ส่วนตัว sRunnable = Runnable ใหม่ () {
@แทนที่
การรันโมฆะสาธารณะ () { /* ... */ }
-
@แทนที่
ป้องกันเป็นโมฆะ onCreate (บันเดิลที่บันทึกไว้InstanceState) {
super.onCreate(savedInstanceState);
// โพสต์ข้อความและชะลอการดำเนินการเป็นเวลา 10 นาที
mHandler.postDelayed (sRunnable, 1,000 * 60 * 10);
// กลับไปยังกิจกรรมก่อนหน้า
เสร็จ();
-
-
ในความเป็นจริงหน่วยความจำรั่วไหลจำนวนมากใน Android เกิดจากการใช้คลาสภายในที่ไม่คงที่ในกิจกรรมดังที่กล่าวไว้ในบทความนี้ดังนั้นเราจึงต้องให้ความสนใจเป็นพิเศษเมื่อใช้คลาสภายในที่ไม่คงที่หากอินสแตนซ์ของวงจรชีวิตของ วัตถุที่เก็บไว้มีขนาดใหญ่กว่าวัตถุคลาสภายนอก มันอาจทำให้หน่วยความจำรั่ว โดยส่วนตัวแล้ว ฉันมักจะใช้คลาสคงที่ของบทความและวิธีการอ้างอิงที่ไม่รัดกุมเพื่อแก้ไขปัญหานี้