ในการตอบคำถามนี้คุณต้องเจาะลึก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ซึ่งในกรณีนี้เราจะเรียก
กิจกรรม / วงจรชีวิตส่วนมีอะไรจะทำอย่างไรกับการตัดสินใจที่จะใช้อย่างใดอย่างหนึ่งหรือวิธีการอื่น ๆ (และมีความจำเป็นที่จะติดตามการเรียกใช้ธงหนึ่งยิงขณะที่ไซมอนแนะนำ)! การตัดสินใจนี้ขึ้นอยู่กับ "ความต้องการ" สำหรับรถตักใหม่เท่านั้น ถ้าเราต้องการที่จะเรียกใช้แบบสอบถามเดียวกันกับที่เราใช้ถ้าเราต้องการที่จะเรียกใช้แบบสอบถามที่เราแตกต่างกันinitLoaderrestartLoader
เราสามารถใช้ได้เสมอ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คุณจะ?