สิ่งที่คุณถามเป็นคำถามที่ค่อนข้างยาก แม้ว่าคุณจะคิดว่ามันเป็นเพียงคำถามเดียว แต่จริงๆแล้วคุณกำลังถามคำถามหลายข้อพร้อมกัน ฉันจะทำให้ดีที่สุดด้วยความรู้ที่ฉันต้องครอบคลุมและหวังว่าคนอื่นจะเข้าร่วมเพื่อปกปิดสิ่งที่ฉันอาจพลาด
คลาสที่ซ้อนกัน: บทนำ
เนื่องจากฉันไม่แน่ใจว่าคุณมีความสะดวกสบายเพียงใดกับ OOP ใน Java สิ่งนี้จะเป็นพื้นฐานสองสามอย่าง คลาสที่ซ้อนกันคือเมื่อนิยามคลาสมีอยู่ภายในคลาสอื่น โดยพื้นฐานแล้วมีสองประเภท: คลาสที่ซ้อนกันแบบคงที่และชั้นใน ความแตกต่างที่แท้จริงระหว่างสิ่งเหล่านี้คือ:
- คลาสที่ซ้อนกันแบบคงที่:
- ถือว่าเป็น "ระดับสูงสุด"
- ไม่ต้องการอินสแตนซ์ของคลาสที่ประกอบด้วยเพื่อสร้าง
- อาจไม่อ้างอิงสมาชิกชั้นเรียนที่มีโดยไม่มีการอ้างอิงที่ชัดเจน
- มีชีวิตของตัวเอง
- คลาสซ้อนภายใน:
- ต้องการอินสแตนซ์ของคลาสที่ประกอบด้วยเพื่อสร้างเสมอ
- มีการอ้างอิงโดยนัยไปยังอินสแตนซ์ที่มีอยู่โดยอัตโนมัติ
- อาจเข้าถึงสมาชิกคลาสของคอนเทนเนอร์โดยไม่มีการอ้างอิง
- อายุการใช้งานควรจะไม่นานกว่าของคอนเทนเนอร์
การรวบรวมขยะและชั้นใน
การรวบรวมขยะเป็นไปโดยอัตโนมัติ แต่พยายามที่จะลบวัตถุตามที่คิดว่ามันถูกใช้งานอยู่ The Garbage Collector นั้นค่อนข้างฉลาด แต่ไม่ไร้ที่ติ มันสามารถตัดสินได้ว่ามีบางสิ่งถูกใช้โดยมีการอ้างอิงวัตถุที่ใช้งานอยู่หรือไม่
ปัญหาที่แท้จริงของที่นี่คือเมื่อชั้นในถูกทำให้มีอายุการใช้งานนานกว่าตู้คอนเทนเนอร์ นี่เป็นเพราะการอ้างอิงโดยนัยถึงคลาสที่มีอยู่ วิธีเดียวที่สามารถเกิดขึ้นได้คือถ้าวัตถุภายนอกคลาสที่มีการเก็บรักษาการอ้างอิงถึงวัตถุภายในโดยไม่คำนึงถึงวัตถุที่ประกอบด้วย
สิ่งนี้สามารถนำไปสู่สถานการณ์ที่วัตถุภายในยังมีชีวิตอยู่ (ผ่านการอ้างอิง) แต่การอ้างอิงไปยังวัตถุที่บรรจุนั้นได้ถูกลบออกจากวัตถุอื่นทั้งหมดแล้ว วัตถุภายในจึงทำให้วัตถุที่มีชีวิตอยู่เพราะมันจะเสมอมีการอ้างอิงถึงมัน ปัญหานี้คือถ้าไม่มีการตั้งโปรแกรมไว้ไม่มีวิธีใดที่จะกลับไปที่วัตถุที่มีอยู่เพื่อตรวจสอบว่ามีชีวิตอยู่หรือไม่
สิ่งสำคัญที่สุดในการทำให้เกิดความเข้าใจนี้คือมันไม่ได้สร้างความแตกต่างไม่ว่าจะเป็นในกิจกรรมหรือเป็นสิ่งที่สามารถดึงได้ คุณจะต้องมีระเบียบเสมอเมื่อใช้คลาสภายในและตรวจสอบให้แน่ใจว่าพวกเขาไม่เคยอยู่ได้นานกว่าวัตถุของภาชนะ โชคดีที่ถ้าไม่ใช่วัตถุหลักของรหัสของคุณการรั่วไหลอาจน้อยเมื่อเปรียบเทียบ น่าเสียดายที่สิ่งเหล่านี้เป็นรอยรั่วที่ยากที่สุดในการค้นหาเพราะพวกเขามีแนวโน้มที่จะไม่มีใครสังเกตจนกว่าพวกเขาจะรั่วไหลออกมา
โซลูชั่น: ชั้นใน
- รับการอ้างอิงชั่วคราวจากวัตถุที่มี
- อนุญาตให้วัตถุที่มีอยู่เป็นวัตถุเดียวที่จะเก็บการอ้างอิงระยะยาวกับวัตถุภายใน
- ใช้รูปแบบที่กำหนดเช่นโรงงาน
- หากคลาสภายในไม่ต้องการการเข้าถึงสมาชิกคลาสที่มีอยู่ให้พิจารณาเปลี่ยนเป็นคลาสแบบสแตติก
- ใช้ด้วยความระมัดระวังไม่ว่าจะอยู่ในกิจกรรมหรือไม่ก็ตาม
กิจกรรมและมุมมอง: บทนำ
กิจกรรมมีข้อมูลจำนวนมากที่สามารถเรียกใช้และแสดงผลได้ กิจกรรมถูกกำหนดโดยคุณลักษณะที่ต้องมีมุมมอง พวกเขายังมีตัวจัดการอัตโนมัติบางอย่าง ไม่ว่าคุณจะระบุหรือไม่กิจกรรมนั้นมีการอ้างอิงโดยนัยถึงมุมมองที่มี
เพื่อให้สามารถสร้างมุมมองได้จะต้องทราบว่าจะสร้างมุมมองนั้นและมีลูก ๆ หรือไม่เพื่อให้สามารถแสดงได้ ซึ่งหมายความว่าทุกมุมมองมีการอ้างอิงถึงกิจกรรม (ผ่านgetContext()
) ยิ่งกว่านั้นทุกมุมมองยังคงอ้างอิงถึงลูก ๆ ของมัน (เช่นgetChildAt()
) สุดท้ายมุมมองแต่ละอันจะเก็บการอ้างอิงไปยังบิตแมปที่แสดงผลซึ่งแสดงถึงการแสดงผลของมัน
เมื่อใดก็ตามที่คุณมีการอ้างอิงถึงกิจกรรม (หรือบริบทของกิจกรรม) หมายความว่าคุณสามารถทำตามโซ่ทั้งหมดลงในลำดับชั้นของเค้าโครง นี่คือเหตุผลที่หน่วยความจำรั่วไหลเกี่ยวกับกิจกรรมหรือมุมมองเป็นเรื่องใหญ่ มันอาจจะเป็นตันของหน่วยความจำการรั่วไหลทั้งหมดในครั้งเดียว
กิจกรรมมุมมองและชั้นใน
จากข้อมูลข้างต้นเกี่ยวกับ Inner Classes สิ่งเหล่านี้เป็นความจำรั่วทั่วไป แต่ก็หลีกเลี่ยงได้บ่อยที่สุด ในขณะที่เป็นที่ต้องการให้คลาสภายในมีการเข้าถึงโดยตรงกับสมาชิกคลาสของกิจกรรม แต่หลายคนก็เต็มใจที่จะทำให้พวกเขาคงที่เพื่อหลีกเลี่ยงปัญหาที่อาจเกิดขึ้น ปัญหาของกิจกรรมและมุมมองนั้นลึกซึ้งยิ่งกว่านั้น
กิจกรรมที่รั่วไหล, การดูและบริบทของกิจกรรม
ทุกอย่างลงมาที่บริบทและวงจรชีวิต มีเหตุการณ์บางอย่าง (เช่นการวางแนว) ที่จะฆ่าบริบทของกิจกรรม เนื่องจากคลาสและเมธอดจำนวนมากต้องการบริบทดังนั้นบางครั้งนักพัฒนาจะพยายามบันทึกโค้ดบางส่วนโดยการอ้างอิงถึงบริบทและกดค้างไว้ มันเกิดขึ้นเมื่อวัตถุหลายอย่างที่เราต้องสร้างเพื่อเรียกใช้กิจกรรมของเราต้องมีอยู่นอก ActivityCycleCycle เพื่อให้กิจกรรมสามารถทำสิ่งที่ต้องทำ หากวัตถุใด ๆ ของคุณมีการอ้างอิงถึงกิจกรรมบริบทหรือมุมมองใด ๆ ของมันเมื่อมันถูกทำลายคุณเพิ่งรั่วไหลกิจกรรมและต้นไม้ทั้งหมดของมุมมอง
โซลูชัน: กิจกรรมและมุมมอง
- หลีกเลี่ยงค่าใช้จ่ายทั้งหมดโดยอ้างอิงจากมุมมองหรือกิจกรรม
- การอ้างอิงถึงบริบทของกิจกรรมทั้งหมดควรมีอายุสั้น (ระยะเวลาของฟังก์ชัน)
- หากคุณต้องการบริบทที่ยาวนานให้ใช้บริบทแอปพลิเคชัน (
getBaseContext()
หรือgetApplicationContext()
) สิ่งเหล่านี้ไม่ได้เก็บการอ้างอิงโดยนัย
- หรือคุณอาจ จำกัด การทำลายกิจกรรมโดยการแทนที่การเปลี่ยนแปลงการกำหนดค่า อย่างไรก็ตามสิ่งนี้จะไม่หยุดเหตุการณ์ที่อาจเกิดขึ้นอื่น ๆ จากการทำลายกิจกรรม ในขณะที่คุณสามารถทำสิ่งนี้ได้คุณอาจต้องการอ้างอิงแนวทางปฏิบัติข้างต้น
Runnables: บทนำ
Runnables ไม่เลวจริง ๆ ฉันหมายถึงพวกมันอาจเป็นได้ แต่จริงๆแล้วเราได้โจมตีพื้นที่อันตรายส่วนใหญ่แล้ว Runnable เป็นการดำเนินการแบบอะซิงโครนัสที่ดำเนินงานอิสระจากเธรดที่สร้างขึ้น Runnables ส่วนใหญ่นั้นอินสแตนซ์จากเธรด UI โดยพื้นฐานแล้วการใช้ Runnable กำลังสร้างเธรดอื่นซึ่งมีการจัดการเพียงเล็กน้อยเท่านั้น หากคุณคลาส Runnable เหมือนคลาสมาตรฐานและปฏิบัติตามแนวทางข้างต้นคุณควรพบปัญหาเล็กน้อย ความจริงก็คือนักพัฒนาหลายคนไม่ทำเช่นนี้
จากความง่ายความสามารถในการอ่านและการไหลของโปรแกรมแบบลอจิคัลนักพัฒนาหลายคนใช้ Anonymous Inner Classes เพื่อกำหนด Runnables เช่นตัวอย่างที่คุณสร้างไว้ด้านบน ผลลัพธ์นี้เป็นตัวอย่างเช่นที่คุณพิมพ์ด้านบน ระดับชั้นในแบบไม่ระบุชื่อนั้นเป็นชั้นในแบบไม่ต่อเนื่อง คุณไม่จำเป็นต้องสร้างคำจำกัดความใหม่ทั้งหมดและเพียงแทนที่วิธีที่เหมาะสม ในทุกแง่มุมอื่น ๆ มันเป็นชั้นในซึ่งหมายความว่ามันทำให้การอ้างอิงโดยนัยไปยังภาชนะของมัน
Runnables และกิจกรรม / มุมมอง
เย้! ส่วนนี้อาจสั้น! เนื่องจากความจริงที่ว่า Runnables ทำงานนอกเธรดปัจจุบันอันตรายของสิ่งเหล่านี้มาจากการดำเนินการแบบอะซิงโครนัสที่ใช้เวลานาน หาก runnable ถูกกำหนดไว้ในกิจกรรมหรือมุมมองเป็น Class Inner ที่ไม่ระบุชื่อหรือชั้นในที่ซ้อนกันมีอันตรายที่ร้ายแรงมาก นี้เป็นเพราะตามที่ระบุไว้ก่อนหน้านี้ก็มีจะรู้ว่าใครเป็นภาชนะที่ใช้บรรจุ ป้อนการเปลี่ยนแปลงการวางแนว (หรือการฆ่าระบบ) ตอนนี้กลับไปดูหัวข้อก่อนหน้าเพื่อทำความเข้าใจกับสิ่งที่เกิดขึ้น ใช่ตัวอย่างของคุณค่อนข้างอันตราย
โซลูชั่น: Runnables
- ลองและขยาย Runnable ถ้ามันไม่ทำลายตรรกะของรหัสของคุณ
- ทำอย่างดีที่สุดเพื่อให้ Runnables แบบคงที่เพิ่มเติมถ้าพวกเขาจะต้องเรียนซ้อน
- หากคุณต้องใช้ Runnables แบบไม่ระบุชื่อหลีกเลี่ยงการสร้างสิ่งเหล่านั้นในวัตถุใด ๆที่มีการอ้างอิงถึงกิจกรรมหรือมุมมองที่ใช้งานมานาน
- Runnables มากมายอาจเป็น AsyncTasks ได้อย่างง่ายดาย พิจารณาใช้ AsyncTask เนื่องจาก VM จัดการโดยค่าเริ่มต้น
ตอบคำถามสุดท้ายทันที
เพื่อตอบคำถามที่ไม่ได้รับการแก้ไขโดยตรงจากส่วนอื่น ๆ ของโพสต์นี้ คุณถามว่า "เมื่อใดที่วัตถุภายในชั้นในจะอยู่รอดได้นานกว่าชั้นนอก" ก่อนที่เราจะไปถึงสิ่งนี้ขอให้ฉันเน้นย้ำถึง: คุณมีสิทธิ์ที่จะกังวลเกี่ยวกับสิ่งนี้ในกิจกรรม แต่มันสามารถทำให้เกิดการรั่วไหลได้ทุกที่ ฉันจะให้ตัวอย่างง่ายๆ (โดยไม่ต้องใช้กิจกรรม) เพียงเพื่อแสดงให้เห็น
ด้านล่างเป็นตัวอย่างทั่วไปของโรงงานพื้นฐาน (ไม่มีรหัส)
public class LeakFactory
{//Just so that we have some data to leak
int myID = 0;
// Necessary because our Leak class is an Inner class
public Leak createLeak()
{
return new Leak();
}
// Mass Manufactured Leak class
public class Leak
{//Again for a little data.
int size = 1;
}
}
นี่ไม่ใช่ตัวอย่างทั่วไป แต่ง่ายพอที่จะสาธิต ที่สำคัญนี่คือตัวสร้าง ...
public class SwissCheese
{//Can't have swiss cheese without some holes
public Leak[] myHoles;
public SwissCheese()
{//Gotta have a Factory to make my holes
LeakFactory _holeDriller = new LeakFactory()
// Now, let's get the holes and store them.
myHoles = new Leak[1000];
for (int i = 0; i++; i<1000)
{//Store them in the class member
myHoles[i] = _holeDriller.createLeak();
}
// Yay! We're done!
// Buh-bye LeakFactory. I don't need you anymore...
}
}
ตอนนี้เรามีรอยรั่ว แต่ไม่มีโรงงาน แม้ว่าเราจะเปิดตัว Factory มันจะยังคงอยู่ในความทรงจำเพราะทุก Leak มีการอ้างอิงถึงมัน มันไม่สำคัญว่าชั้นนอกจะไม่มีข้อมูล สิ่งนี้เกิดขึ้นบ่อยกว่าที่คิด เราไม่ต้องการผู้สร้างเพียงแค่การสร้างสรรค์ ดังนั้นเราจึงสร้างขึ้นชั่วคราว แต่ใช้การสร้างอย่างไม่มีกำหนด
ลองจินตนาการว่าเกิดอะไรขึ้นเมื่อเราเปลี่ยน Constructor เพียงเล็กน้อย
public class SwissCheese
{//Can't have swiss cheese without some holes
public Leak[] myHoles;
public SwissCheese()
{//Now, let's get the holes and store them.
myHoles = new Leak[1000];
for (int i = 0; i++; i<1000)
{//WOW! I don't even have to create a Factory...
// This is SOOOO much prettier....
myHoles[i] = new LeakFactory().createLeak();
}
}
}
ตอนนี้ทุก LeakFactories ใหม่เหล่านั้นเพิ่งรั่วไหลออกมา คุณคิดอย่างไรกับเรื่องนี้? เหล่านี้เป็นสองตัวอย่างทั่วไปของวิธีการที่ชั้นในสามารถอยู่ได้นานกว่าชั้นนอกของประเภทใด ๆ ถ้าชั้นนอกนั้นเป็นกิจกรรมลองคิดดูว่ามันจะแย่ขนาดไหน
ข้อสรุป
รายการเหล่านี้เป็นอันตรายที่รู้จักกันดีของการใช้วัตถุเหล่านี้อย่างไม่เหมาะสม โดยทั่วไปโพสต์นี้ควรครอบคลุมคำถามส่วนใหญ่ของคุณ แต่ฉันเข้าใจว่าเป็นโพสต์ของ loooong ดังนั้นหากคุณต้องการคำชี้แจงเพียงแค่แจ้งให้เราทราบ ตราบใดที่คุณปฏิบัติตามแนวทางข้างต้นคุณจะมีความกังวลเล็กน้อยเกี่ยวกับการรั่วไหล