ฉันเขียนคำตอบนี้ในปี '09 เมื่อ Android ค่อนข้างใหม่และมีหลายพื้นที่ที่ไม่ได้รับการยอมรับในการพัฒนา Android ฉันได้เพิ่มภาคผนวกยาวที่ด้านล่างของโพสต์นี้ที่อยู่การวิจารณ์บางส่วนและรายละเอียดความขัดแย้งทางปรัชญาที่ฉันมีกับการใช้งานของ Singletons มากกว่าการประยุกต์ใช้ subclassing อ่านความเสี่ยงของคุณเอง
คำตอบเดิม:
ปัญหาทั่วไปที่คุณพบคือวิธีการบันทึกสถานะในหลาย ๆ กิจกรรมและทุกส่วนของแอปพลิเคชันของคุณ ตัวแปรแบบสแตติก (ตัวอย่างเช่น singleton) เป็นวิธีการใช้จาวาทั่วไปในการบรรลุเป้าหมายนี้ อย่างไรก็ตามฉันพบว่าวิธีที่หรูหรากว่าใน Android คือการเชื่อมโยงสถานะของคุณกับบริบทแอปพลิเคชัน
ดังที่คุณทราบแต่ละกิจกรรมเป็นบริบทซึ่งเป็นข้อมูลเกี่ยวกับสภาพแวดล้อมการดำเนินการในแง่ที่กว้างที่สุด แอปพลิเคชันของคุณมีบริบทและ Android รับประกันว่าจะมีอยู่เป็นอินสแตนซ์เดียวในแอปพลิเคชันของคุณ
วิธีการทำเช่นนี้คือการสร้างคลาสย่อยของคุณเองandroid.app.Applicationแล้วระบุคลาสนั้นในแท็กแอปพลิเคชันในรายการของคุณ ตอนนี้ Android จะสร้างตัวอย่างของคลาสนั้นโดยอัตโนมัติและทำให้แอปพลิเคชันของคุณพร้อมใช้งาน คุณสามารถเข้าถึงได้จากการcontext
ใช้Context.getApplicationContext()
วิธีการใด ๆ( Activity
นอกจากนี้ยังมีวิธีการgetApplication()
ที่มีผลเหมือนกันแน่นอน) ต่อไปนี้เป็นตัวอย่างที่ง่ายมากโดยมี caveats ที่จะปฏิบัติตาม:
class MyApp extends Application {
private String myState;
public String getState(){
return myState;
}
public void setState(String s){
myState = s;
}
}
class Blah extends Activity {
@Override
public void onCreate(Bundle b){
...
MyApp appState = ((MyApp)getApplicationContext());
String state = appState.getState();
...
}
}
สิ่งนี้มีผลเช่นเดียวกับการใช้ตัวแปรแบบสแตติกหรือซิงเกิล แต่รวมเข้ากับกรอบ Android ที่มีอยู่ค่อนข้างดี โปรดทราบว่าสิ่งนี้จะไม่ทำงานในกระบวนการต่าง ๆ (หากแอปของคุณเป็นหนึ่งในรายการที่หายากที่มีหลายกระบวนการ)
สิ่งที่ควรทราบจากตัวอย่างด้านบน สมมติว่าเราทำสิ่งที่ต้องการแทน:
class MyApp extends Application {
private String myState = /* complicated and slow initialization */;
public String getState(){
return myState;
}
}
ตอนนี้การเริ่มต้นช้า (เช่นการกดปุ่มดิสก์เครือข่ายการกดปุ่มการบล็อกอะไรเป็นต้น) จะดำเนินการทุกครั้งที่แอปพลิเคชันถูกสร้างขึ้น! คุณอาจคิดว่านี่เป็นเพียงขั้นตอนเดียวสำหรับกระบวนการและฉันจะต้องจ่ายค่าใช้จ่ายใช่มั้ย ตัวอย่างเช่นตามที่ Dianne Hackborn ระบุไว้ด้านล่างเป็นไปได้ทั้งหมดที่กระบวนการของคุณจะถูกสร้างอินสแตนซ์ - เพียงแค่ - เพื่อจัดการกับเหตุการณ์ออกอากาศเบื้องหลัง หากการประมวลผลการออกอากาศของคุณไม่จำเป็นต้องมีสถานะนี้คุณอาจต้องทำการดำเนินการทั้งชุดที่ซับซ้อนและช้าเพื่ออะไร Lazy instantiation เป็นชื่อของเกมที่นี่ ต่อไปนี้เป็นวิธีที่ซับซ้อนกว่าเล็กน้อยในการใช้แอปพลิเคชันซึ่งเหมาะสมกว่าสำหรับทุกสิ่งยกเว้นการใช้งานที่ง่ายที่สุด:
class MyApp extends Application {
private MyStateManager myStateManager = new MyStateManager();
public MyStateManager getStateManager(){
return myStateManager ;
}
}
class MyStateManager {
MyStateManager() {
/* this should be fast */
}
String getState() {
/* if necessary, perform blocking calls here */
/* make sure to deal with any multithreading/synchronicity issues */
...
return state;
}
}
class Blah extends Activity {
@Override
public void onCreate(Bundle b){
...
MyStateManager stateManager = ((MyApp)getApplicationContext()).getStateManager();
String state = stateManager.getState();
...
}
}
ในขณะที่ฉันต้องการ Application subclassing เพื่อใช้ singletons ที่นี่เป็นทางออกที่หรูหรากว่าฉันอยากให้นักพัฒนาใช้ singletons ถ้าจำเป็นจริงๆโดยไม่ต้องคิดถึงเรื่องทั้งหมดผ่านการทำงาน
หมายเหตุ 1:เช่นเดียวกับที่ anticafe แสดงความคิดเห็นเพื่อผูกการแทนที่แอปพลิเคชันของคุณกับแอปพลิเคชันของคุณอย่างถูกต้องจำเป็นต้องมีแท็กในไฟล์รายการ ดูเอกสาร Android อีกครั้งเพื่อดูข้อมูลเพิ่มเติม ตัวอย่าง:
<application
android:name="my.application.MyApp"
android:icon="..."
android:label="...">
</application>
หมายเหตุ 2: user608578 ถามด้านล่างว่ามันทำงานอย่างไรกับการจัดการวงจรชีวิตวัตถุพื้นเมือง ฉันไม่ได้ใช้ความเร็วในการใช้รหัสเนทีฟกับ Android ในระดับที่น้อยที่สุดและฉันก็ไม่ได้มีคุณสมบัติที่จะตอบว่าจะโต้ตอบกับโซลูชันของฉันอย่างไร หากใครบางคนมีคำตอบสำหรับเรื่องนี้ฉันยินดีที่จะให้เครดิตพวกเขาและใส่ข้อมูลในโพสต์นี้เพื่อการมองเห็นสูงสุด
ภาคผนวก:
ดังที่คนบางคนตั้งข้อสังเกตว่านี่ไม่ใช่วิธีแก้ปัญหาสำหรับสภาวะถาวรบางสิ่งที่ฉันควรจะเน้นในคำตอบเดิมมากขึ้น เช่นนี้ไม่ได้หมายถึงการแก้ปัญหาสำหรับการบันทึกผู้ใช้หรือข้อมูลอื่น ๆ ที่มีความตั้งใจที่จะยืนยันตลอดอายุการใช้งานของแอปพลิเคชัน ดังนั้นฉันคิดว่าการวิพากษ์วิจารณ์ส่วนใหญ่ด้านล่างที่เกี่ยวข้องกับแอปพลิเคชันที่ถูกฆ่าตายในเวลาใดก็ได้ ฯลฯ ... , moot เป็นสิ่งที่เคยจำเป็นต้องยืนยันกับดิสก์ไม่ควรเก็บไว้ผ่านคลาสย่อยของแอปพลิเคชัน มันจะหมายถึงการจะแก้ปัญหาสำหรับการจัดเก็บชั่วคราวได้อย่างง่ายดายอีกครั้ง creatable สถานะโปรแกรม (ไม่ว่าจะเป็นผู้ใช้ที่ถูกบันทึกไว้ในตัวอย่าง) และส่วนประกอบซึ่งเป็นเช่นเดียว (ผู้จัดการเครือข่ายพลิเคชันสำหรับตัวอย่าง) ( ไม่เดี่ยว!) ในธรรมชาติ
Dayerman ใจดีพอที่จะชี้ให้เห็นการสนทนาที่น่าสนใจกับ Reto Meier และ Dianne Hackbornซึ่งการใช้คลาสย่อยของ Application นั้นไม่สนับสนุนรูปแบบซิงเกิล Somatik ชี้ให้เห็นบางสิ่งบางอย่างในลักษณะนี้ก่อนหน้านี้แม้ว่าฉันจะไม่เห็นมันในเวลานั้น เนื่องจากบทบาทของ Reto และ Dianne ในการบำรุงรักษาแพลตฟอร์ม Android ฉันจึงไม่สามารถแนะนำโดยไม่สนใจคำแนะนำของพวกเขาได้ สิ่งที่พวกเขาพูดไป ฉันต้องการที่จะไม่เห็นด้วยกับความคิดเห็นที่แสดงออกถึงการเลือกซิงเกิลตันมากกว่าคลาสย่อยของแอปพลิเคชัน ในความขัดแย้งของฉันฉันจะใช้ประโยชน์จากแนวคิดที่อธิบายได้ดีที่สุดในคำอธิบาย StackExchange ของรูปแบบการออกแบบซิงเกิลนี้ดังนั้นฉันไม่จำเป็นต้องนิยามคำในคำตอบนี้ ฉันขอแนะนำให้ข้ามลิงก์ก่อนดำเนินการต่อ ทีละจุด:
Dianne กล่าวว่า "ไม่มีเหตุผลใดที่จะซับคลาสจากแอปพลิเคชันไม่แตกต่างจากการสร้างซิงเกิลตัน ... " การอ้างสิทธิ์ครั้งแรกนี้ไม่ถูกต้อง มีสองเหตุผลหลักสำหรับเรื่องนี้ 1) คลาสแอปพลิเคชันให้การรับประกันอายุการใช้งานที่ดีขึ้นสำหรับนักพัฒนาแอปพลิเคชัน รับประกันได้ว่าจะมีอายุการใช้งานของแอปพลิเคชัน ซิงเกิลตันไม่ได้เชื่อมโยงอย่างชัดเจนกับอายุการใช้งานของแอปพลิเคชัน (แม้ว่าจะมีประสิทธิภาพ) นี่อาจไม่ใช่ปัญหาสำหรับนักพัฒนาแอปพลิเคชั่นโดยเฉลี่ยของคุณ แต่ฉันจะเถียงว่านี่เป็นประเภทสัญญาที่ Android API ควรเสนอและมีความยืดหยุ่นมากขึ้นกับระบบ Android เช่นกันโดยลดอายุการใช้งานที่เกี่ยวข้อง ข้อมูล. 2) แอปพลิเคชั่นคลาสให้ผู้พัฒนาแอปพลิเคชันพร้อมตัวยึดอินสแตนซ์เดี่ยวสำหรับสถานะ ซึ่งแตกต่างจากผู้มีอำนาจรัฐเดี่ยวมาก สำหรับรายการความแตกต่างให้ดูลิงก์คำอธิบายซิงเกิลด้านบน
Dianne กล่าวต่อว่า "... น่าจะเป็นสิ่งที่คุณต้องเสียใจในอนาคตเมื่อคุณพบว่าแอปพลิเคชันวัตถุของคุณกลายเป็นเรื่องยุ่งเหยิงที่ยิ่งใหญ่ของสิ่งที่ควรเป็นตรรกะแอปพลิเคชันอิสระ" สิ่งนี้ไม่ถูกต้อง แต่นี่ไม่ใช่เหตุผลสำหรับการเลือกซิงเกิลตันของคลาสย่อยของแอปพลิเคชัน ไม่มีข้อโต้แย้งของไดแอนที่ให้เหตุผลว่าการใช้ซิงเกิลดีกว่าคลาสย่อยของแอปพลิเคชันทั้งหมดที่เธอพยายามสร้างคือการใช้ซิงเกิลตันนั้นไม่เลวร้ายไปกว่าซับคลาสแอปพลิเคชันซึ่งฉันเชื่อว่าเป็นเท็จ
เธอกล่าวต่อ "และสิ่งนี้นำไปสู่การจัดการสิ่งเหล่านี้อย่างเป็นธรรมชาติมากขึ้น สิ่งนี้จะละเว้นความจริงที่ว่าไม่มีเหตุผลที่คุณไม่สามารถเริ่มต้นตามคำขอได้โดยใช้คลาสย่อยของ Application เช่นกัน อีกครั้งไม่มีความแตกต่าง
Dianne ลงท้ายด้วย "กรอบงานนั้นมีตันแบบซิงเกิลตันสำหรับข้อมูลที่แชร์เล็ก ๆ น้อย ๆ ทั้งหมดที่แอปเก็บไว้เช่นแคชทรัพยากรที่มีจำนวนมากกลุ่มของวัตถุ ฯลฯ มันใช้งานได้ดีมาก" ฉันไม่ได้โต้แย้งว่าการใช้ซิงเกิลตันไม่สามารถทำงานได้ดีหรือไม่ใช่ทางเลือกที่ถูกกฎหมาย ฉันโต้เถียงว่า Singletons ไม่ได้ให้สัญญาที่แข็งแกร่งกับระบบ Android เช่นเดียวกับการใช้คลาสย่อยของ Application และยิ่งไปกว่านั้นการใช้ Singletons โดยทั่วไปชี้ไปที่การออกแบบที่ยืดหยุ่นซึ่งไม่ได้แก้ไขได้ง่ายและนำไปสู่ปัญหามากมาย IMHO สัญญาที่แข็งแกร่งที่ Android API เสนอให้กับแอพพลิเคชั่นสำหรับนักพัฒนาเป็นหนึ่งในแง่มุมที่น่าดึงดูดใจและน่าพอใจที่สุดของการเขียนโปรแกรมกับ Android และช่วยนำไปสู่การยอมรับนักพัฒนาในยุคแรกซึ่งผลักดันแพลตฟอร์ม Android ให้ประสบความสำเร็จ
Dianne ได้แสดงความคิดเห็นไว้ด้านล่างเช่นกันซึ่งกล่าวถึงข้อเสียเพิ่มเติมของการใช้คลาสย่อยของแอปพลิเคชันพวกเขาอาจส่งเสริมหรือทำให้การเขียนรหัสประสิทธิภาพลดลงได้ง่ายขึ้น นี่เป็นเรื่องจริงและฉันได้แก้ไขคำตอบนี้เพื่อเน้นความสำคัญของการพิจารณาความสมบูรณ์แบบที่นี่และใช้วิธีการที่ถูกต้องหากคุณใช้คลาสย่อยของแอปพลิเคชัน ในฐานะที่เป็น Dianne กล่าวว่าเป็นสิ่งสำคัญที่ต้องจำไว้ว่าคลาสแอปพลิเคชันของคุณจะถูกสร้างอินสแตนซ์ทุกครั้งที่กระบวนการของคุณถูกโหลด (อาจมีหลายครั้งในคราวเดียวถ้าแอปพลิเคชันของคุณทำงานในหลายกระบวนการ!) แม้ว่ากระบวนการ เหตุการณ์ ดังนั้นจึงเป็นเรื่องสำคัญที่จะต้องใช้คลาสแอปพลิเคชันเป็นแหล่งเก็บข้อมูลสำหรับตัวชี้ไปยังคอมโพเนนต์ที่ใช้ร่วมกันของแอปพลิเคชันของคุณแทนที่จะเป็นสถานที่สำหรับการประมวลผลใด ๆ !
ฉันปล่อยให้คุณมีรายการข้อเสียต่อไปนี้เป็น Singletons ซึ่งถูกขโมยจากลิงก์ StackExchange ก่อนหน้านี้:
- ไม่สามารถใช้คลาส abstract หรืออินเตอร์เฟส
- ไม่สามารถคลาสย่อย;
- การมีเพศสัมพันธ์สูงทั่วทั้งแอปพลิเคชัน (ยากที่จะแก้ไข);
- ทดสอบยาก (ไม่สามารถปลอม / จำลองในการทดสอบหน่วย)
- ยากต่อการขนานในกรณีที่สถานะไม่แน่นอน (ต้องใช้การล็อคอย่างกว้างขวาง);
และเพิ่มของฉันเอง:
- สัญญาที่ไม่ชัดเจนและไม่สามารถจัดการได้ตลอดอายุการใช้งานสำหรับการพัฒนา Android (หรืออื่น ๆ ส่วนใหญ่);