ในการตอบคำถามนี้คุณต้องเจาะลึกLoaderManager
รหัส ในขณะที่เอกสารสำหรับ LoaderManager เองยังไม่ชัดเจนเพียงพอ (หรือไม่มีคำถามนี้) เอกสารสำหรับ LoaderManagerImpl ซึ่งเป็นคลาสย่อยของ LoaderManager แบบนามธรรมนั้นให้ความกระจ่างมากขึ้น
initLoader
โทรเพื่อเริ่มต้น ID เฉพาะด้วย Loader หาก ID นี้มี Loader เชื่อมโยงอยู่แล้ว ID นี้จะไม่เปลี่ยนแปลงและการเรียกกลับก่อนหน้านี้จะถูกแทนที่ด้วย ID ที่ให้มาใหม่ หากขณะนี้ไม่มี Loader สำหรับ ID จะมีการสร้างและเริ่มต้นใหม่
โดยทั่วไปควรใช้ฟังก์ชั่นนี้เมื่อคอมโพเนนต์กำลังเริ่มต้นเพื่อให้แน่ใจว่ามีการสร้าง Loader ที่ต้องใช้ สิ่งนี้ช่วยให้สามารถใช้ข้อมูลของ Loader ที่มีอยู่ซ้ำได้หากมีอยู่แล้วดังนั้นตัวอย่างเช่นเมื่อมีการสร้างกิจกรรมใหม่หลังจากการเปลี่ยนแปลงการกำหนดค่าก็ไม่จำเป็นต้องสร้างตัวโหลดขึ้นมาใหม่
restartLoader
เรียกร้องให้สร้าง Loader ใหม่ที่เชื่อมโยงกับ ID เฉพาะ หากปัจจุบันมี Loader เชื่อมโยงกับ ID นี้จะถูกยกเลิก / หยุด / ทำลายตามความเหมาะสม Loader ใหม่พร้อมอาร์กิวเมนต์ที่กำหนดจะถูกสร้างขึ้นและข้อมูลจะถูกส่งถึงคุณเมื่อพร้อมใช้งาน
[... ] หลังจากเรียกใช้ฟังก์ชันนี้ Loaders ก่อนหน้าใด ๆ ที่เชื่อมโยงกับ ID นี้จะถือว่าไม่ถูกต้องและคุณจะไม่ได้รับการอัปเดตข้อมูลเพิ่มเติมจากพวกเขา
โดยทั่วไปมีสองกรณี:
- ไม่มีตัวโหลดที่มี id: ทั้งสองวิธีจะสร้างตัวโหลดใหม่ดังนั้นจึงไม่มีความแตกต่างกัน
- ตัวโหลดที่มี id มีอยู่แล้ว:
initLoader
จะแทนที่เฉพาะการเรียกกลับที่ส่งผ่านเป็นพารามิเตอร์ แต่จะไม่ยกเลิกหรือหยุดตัวโหลด ในกรณีนี้CursorLoader
หมายความว่าเคอร์เซอร์ยังคงเปิดอยู่และทำงานอยู่ (หากเป็นเช่นนั้นก่อนการinitLoader
โทร) ในทางกลับกัน startLoader จะยกเลิกหยุดและทำลายตัวโหลด (และปิดแหล่งข้อมูลพื้นฐานเช่นเคอร์เซอร์) และสร้างตัวโหลดใหม่ (ซึ่งจะสร้างเคอร์เซอร์ใหม่และเรียกใช้แบบสอบถามอีกครั้งหากตัวโหลดเป็น CursorLoader)
นี่คือรหัสแบบง่ายสำหรับทั้งสองวิธี:
initLoader
LoaderInfo info = mLoaders.get(id);
if (info == null) {
// Loader doesn't already exist -> create new one
info = createAndInstallLoader(id, args, LoaderManager.LoaderCallbacks<Object>)callback);
} else {
// Loader exists -> only replace callbacks
info.mCallbacks = (LoaderManager.LoaderCallbacks<Object>)callback;
}
restartLoader
LoaderInfo info = mLoaders.get(id);
if (info != null) {
LoaderInfo inactive = mInactiveLoaders.get(id);
if (inactive != null) {
// does a lot of stuff to deal with already inactive loaders
} else {
// Keep track of the previous instance of this loader so we can destroy
// it when the new one completes.
info.mLoader.abandon();
mInactiveLoaders.put(id, info);
}
}
info = createAndInstallLoader(id, args, (LoaderManager.LoaderCallbacks<Object>)callback);
ดังที่เราเห็นในกรณีที่ไม่มีตัวโหลด (ข้อมูล == null) ทั้งสองวิธีจะสร้างตัวโหลดใหม่ (info = createAndInstallLoader (... )) ในกรณีที่ตัวโหลดมีอยู่แล้วinitLoader
จะแทนที่การเรียกกลับเท่านั้น (info.mCallbacks = ... ) ในขณะที่restartLoader
ปิดการใช้งานตัวโหลดเก่า (จะถูกทำลายเมื่อตัวโหลดใหม่ทำงานเสร็จสิ้น) จากนั้นสร้างใหม่
ดังนั้นจึงมีความชัดเจนว่าจะใช้initLoader
เมื่อใดและเมื่อใดควรใช้restartLoader
และเหตุใดจึงเหมาะสมที่จะมีสองวิธีนี้
initLoader
ใช้เพื่อให้แน่ใจว่ามีตัวโหลดเริ่มต้น หากไม่มีอยู่จะมีการสร้างใหม่หากมีอยู่แล้วจะถูกนำมาใช้ใหม่ เราใช้วิธีนี้เสมอไม่ว่าเราจะต้องมีตัวโหลดใหม่เนื่องจากแบบสอบถามที่จะเรียกใช้มีการเปลี่ยนแปลง (ไม่ใช่ข้อมูลพื้นฐาน แต่เป็นแบบสอบถามจริงเช่นในคำสั่ง SQL สำหรับ CursorLoader) ซึ่งในกรณีนี้เราจะเรียกว่าrestartLoader
ซึ่งในกรณีนี้เราจะเรียก
กิจกรรม / วงจรชีวิตส่วนมีอะไรจะทำอย่างไรกับการตัดสินใจที่จะใช้อย่างใดอย่างหนึ่งหรือวิธีการอื่น ๆ (และมีความจำเป็นที่จะติดตามการเรียกใช้ธงหนึ่งยิงขณะที่ไซมอนแนะนำ)! การตัดสินใจนี้ขึ้นอยู่กับ "ความต้องการ" สำหรับรถตักใหม่เท่านั้น ถ้าเราต้องการที่จะเรียกใช้แบบสอบถามเดียวกันกับที่เราใช้ถ้าเราต้องการที่จะเรียกใช้แบบสอบถามที่เราแตกต่างกันinitLoader
restartLoader
เราสามารถใช้ได้เสมอrestartLoader
แต่จะไม่มีประสิทธิภาพ หลังจากการหมุนหน้าจอหรือหากผู้ใช้ออกจากแอปและกลับไปที่กิจกรรมเดียวกันในภายหลังเรามักจะต้องการแสดงผลการสืบค้นเดียวกันดังนั้นจึงrestartLoader
จะสร้างตัวโหลดขึ้นมาใหม่โดยไม่จำเป็นและปิดผลการสืบค้นที่อ้างอิง (อาจมีราคาแพง)
สิ่งสำคัญมากที่จะต้องเข้าใจความแตกต่างระหว่างข้อมูลที่โหลดและ "แบบสอบถาม" เพื่อโหลดข้อมูลนั้น สมมติว่าเราใช้ CursorLoader ในการสอบถามตารางสำหรับคำสั่งซื้อ หากมีการเพิ่มคำสั่งใหม่ลงในตารางนั้น CursorLoader จะใช้ onContentChanged () เพื่อแจ้งให้ UI อัปเดตและแสดงคำสั่งใหม่ (ไม่จำเป็นต้องใช้restartLoader
ในกรณีนี้) หากเราต้องการแสดงเฉพาะคำสั่งซื้อที่เปิดอยู่เราจำเป็นต้องมีการสืบค้นใหม่และเราจะใช้restartLoader
เพื่อส่งคืน CursorLoader ใหม่ที่สะท้อนการสืบค้นใหม่
มีความสัมพันธ์ระหว่างสองวิธีนี้หรือไม่?
พวกเขาแชร์รหัสเพื่อสร้าง Loader ใหม่ แต่จะทำสิ่งที่แตกต่างออกไปเมื่อมีตัวโหลดอยู่แล้ว
โทรrestartLoader
ตลอดinitLoader
ไหม?
ไม่มันไม่เคยทำ
โทรมาrestartLoader
ไม่ต้องโทรได้initLoader
ไหม
ใช่.
ปลอดภัยไหมที่จะโทรinitLoader
สองครั้งเพื่อรีเฟรชข้อมูล
ปลอดภัยที่จะโทรinitLoader
สองครั้ง แต่จะไม่มีการรีเฟรชข้อมูล
ฉันควรใช้หนึ่งในสองเมื่อใดและเพราะเหตุใด
(หวังว่า) จะชัดเจนหลังจากคำอธิบายของฉันข้างต้น
การเปลี่ยนแปลงการกำหนดค่า
LoaderManager ยังคงรักษาสถานะของการเปลี่ยนแปลงการกำหนดค่า (รวมถึงการเปลี่ยนแปลงการวางแนว) ดังนั้นคุณจะคิดว่าไม่มีอะไรเหลือให้เราทำ คิดใหม่อีกครั้ง...
ประการแรก LoaderManager จะไม่เก็บการโทรกลับไว้ดังนั้นหากคุณไม่ทำอะไรเลยคุณจะไม่ได้รับสายไปยังวิธีการโทรกลับของคุณเช่นonLoadFinished()
และสิ่งที่คล้ายกันและอาจทำให้แอปของคุณเสียหายได้
ดังนั้นเราต้องโทรอย่างน้อยinitLoader
เพื่อเรียกคืนวิธีการโทรกลับ ( restartLoader
แน่นอนว่าเป็นไปได้เช่นกัน) เอกสารฯ :
หาก ณ จุดที่เรียกผู้โทรอยู่ในสถานะเริ่มต้นและตัวโหลดที่ร้องขอมีอยู่แล้วและได้สร้างข้อมูลแล้วการโทรกลับonLoadFinished(Loader, D)
จะถูกเรียกทันที (ภายในฟังก์ชันนี้) [... ]
นั่นหมายความว่าถ้าเราโทรไปinitLoader
หลังจากเปลี่ยนการวางแนวเราจะได้รับonLoadFinished
สายทันทีเพราะข้อมูลถูกโหลดแล้ว (สมมติว่าเป็นกรณีก่อนการเปลี่ยนแปลง) ในขณะที่ฟังดูตรงไปตรงมาอาจเป็นเรื่องยุ่งยาก (เราทุกคนไม่ชอบ Android ... )
เราต้องแยกแยะระหว่างสองกรณี:
- จัดการการกำหนดค่าเปลี่ยนแปลงตัวเอง: กรณีนี้สำหรับ Fragments ที่ใช้ setRetainInstance (true) หรือสำหรับกิจกรรมที่มี
android:configChanges
แท็กตามในรายการ ส่วนประกอบเหล่านี้จะไม่ได้รับการโทรแบบ onCreate หลังจากเช่นการหมุนหน้าจอดังนั้นอย่าลืมโทร
initLoader/restartLoader
ด้วยวิธีการโทรกลับแบบอื่น (เช่นใน
onActivityCreated(Bundle)
) เพื่อให้สามารถเริ่มการทำงานของ Loader ได้จำเป็นต้องจัดเก็บรหัสตัวโหลด (เช่นในรายการ) เนื่องจากส่วนประกอบถูกเก็บไว้ในการเปลี่ยนแปลงการกำหนดค่าเราจึงสามารถวนซ้ำรหัสตัวโหลดที่มีอยู่และเรียกinitLoader(loaderid,
...)
ใช้
- ไม่จัดการการเปลี่ยนแปลงการกำหนดค่าเอง: ในกรณีนี้ Loaders สามารถเริ่มต้นใน onCreate ได้ แต่เราจำเป็นต้องเก็บรหัสตัวโหลดด้วยตนเองมิฉะนั้นเราจะไม่สามารถทำการเรียก หากรหัสถูกเก็บไว้ใน ArrayList เราจะทำ
outState.putIntegerArrayList(loaderIdsKey, loaderIdsArray)
ใน onSaveInstanceState และกู้คืนรหัสใน onCreate:
loaderIdsArray =
savedInstanceState.getIntegerArrayList(loaderIdsKey)
ก่อนที่เราจะทำการเรียก initLoader
initLoader
(และการโทรกลับทั้งหมดเสร็จสิ้น Loader ไม่ได้ใช้งาน) หลังจากการหมุนคุณจะไม่ได้รับการonLoadFinished
ติดต่อกลับ แต่ถ้าคุณใช้restartLoader
คุณจะ?