Android AsyncTask สำหรับการใช้งานที่ยาวนาน


91

อ้างถึงเอกสารสำหรับ AsyncTask ที่นี่กล่าวว่า:

ควรใช้ AsyncTasks สำหรับการดำเนินการสั้น ๆ (อย่างน้อยที่สุดไม่กี่วินาที) หากคุณต้องการให้เธรดทำงานเป็นเวลานานขอแนะนำให้คุณใช้ API ต่างๆที่มีให้โดย pacakge java.util.concurrent เช่น Executor, ThreadPoolExecutor และ FutureTask

ตอนนี้คำถามของฉันเกิดขึ้น: ทำไม? doInBackground()ฟังก์ชั่นวิ่งออกหัวข้อ UI ดังนั้นสิ่งที่เป็นอันตรายจะมีโดยมีการดำเนินการระยะยาวที่นี่?


2
ปัญหาที่ฉันประสบขณะปรับใช้แอป (ที่ใช้ asyncTask) กับอุปกรณ์จริงคือdoInBackgroundฟังก์ชันที่ใช้งานได้ยาวนานจะหยุดหน้าจอหากไม่ได้ใช้แถบความคืบหน้า
venkatKA

2
เนื่องจาก AsyncTask เชื่อมโยงกับกิจกรรมที่เปิดใช้งานดังนั้นหากกิจกรรมถูกฆ่าอินสแตนซ์ AsyncTask ของคุณก็อาจถูกฆ่าได้เช่นกัน
IgorGanapolsky

จะเกิดอะไรขึ้นถ้าฉันสร้าง AsyncTask ภายในบริการ นั่นจะไม่ช่วยแก้ปัญหาได้หรือ?
วน

1
ใช้ IntentService โซลูชันที่สมบูรณ์แบบเพื่อรันการทำงานที่ยาวนานในพื้นหลัง
Keyur Thumar

คำตอบ:


121

เป็นคำถามที่ดีมากต้องใช้เวลาในฐานะโปรแกรมเมอร์ Android เพื่อทำความเข้าใจปัญหาอย่างถ่องแท้ AsyncTask มีสองประเด็นหลักที่เกี่ยวข้อง:

  • พวกเขาเชื่อมโยงกับวงจรชีวิตของกิจกรรมไม่ดี
  • พวกเขาสร้างการรั่วไหลของหน่วยความจำได้ง่ายมาก

ภายในแอปRoboSpice Motivations ( มีให้บริการบน Google Play ) เราตอบคำถามนั้นโดยละเอียด จะให้มุมมองเชิงลึกของ AsyncTasks, Loaders, คุณสมบัติและข้อเสียและยังแนะนำให้คุณรู้จักกับโซลูชันทางเลือกสำหรับคำขอเครือข่าย: RoboSpice คำขอเครือข่ายเป็นข้อกำหนดทั่วไปใน Android และเป็นไปตามการดำเนินงานที่ยาวนาน นี่คือข้อความที่ตัดตอนมาจากแอพ:

วงจรชีวิตของ AsyncTask และ Activity

AsyncTasks ไม่เป็นไปตามวงจรชีวิตของอินสแตนซ์กิจกรรม หากคุณเริ่ม AsyncTask ภายในกิจกรรมและคุณหมุนอุปกรณ์กิจกรรมนั้นจะถูกทำลายและจะมีการสร้างอินสแตนซ์ใหม่ แต่ AsyncTask จะไม่ตาย มันจะมีชีวิตอยู่ต่อไปจนกว่าจะสำเร็จ

และเมื่อเสร็จสิ้น AsyncTask จะไม่อัปเดต UI ของกิจกรรมใหม่ แน่นอนมันอัปเดตอินสแตนซ์เดิมของกิจกรรมที่ไม่ปรากฏอีกต่อไป สิ่งนี้สามารถนำไปสู่ ​​Exception ของชนิด java.lang.IllegalArgumentException: ดูไม่ได้แนบกับตัวจัดการหน้าต่างถ้าคุณใช้เช่น findViewById เพื่อดึงมุมมองภายในกิจกรรม

ปัญหาหน่วยความจำรั่ว

สะดวกมากในการสร้าง AsyncTasks เป็นคลาสภายในของกิจกรรมของคุณ เนื่องจาก AsyncTask จะต้องปรับเปลี่ยนมุมมองของกิจกรรมเมื่องานเสร็จสมบูรณ์หรืออยู่ระหว่างดำเนินการการใช้คลาสภายในของกิจกรรมจึงดูสะดวก: คลาสภายในสามารถเข้าถึงฟิลด์ใดก็ได้ของคลาสชั้นนอกโดยตรง

อย่างไรก็ตามมันหมายความว่าคลาสภายในจะมีการอ้างอิงที่มองไม่เห็นในอินสแตนซ์คลาสภายนอกนั่นคือกิจกรรม

ในระยะยาวสิ่งนี้ก่อให้เกิดการรั่วไหลของหน่วยความจำ: หาก AsyncTask ใช้งานได้นานจะทำให้กิจกรรม "มีชีวิต" ในขณะที่ Android ต้องการกำจัดมันออกไปเนื่องจากไม่สามารถแสดงได้อีก กิจกรรมนี้ไม่สามารถรวบรวมขยะได้และเป็นกลไกหลักสำหรับ Android ในการรักษาทรัพยากรในอุปกรณ์


เป็นความคิดที่แย่มากที่จะใช้ AsyncTasks สำหรับการทำงานที่ยาวนาน อย่างไรก็ตามเหมาะสำหรับคนที่มีชีวิตสั้น ๆ เช่นการอัปเดตมุมมองหลังจาก 1 หรือ 2 วินาที

ฉันขอแนะนำให้คุณดาวน์โหลดแอป RoboSpice Motivationsซึ่งจะอธิบายในเชิงลึกนี้และให้ตัวอย่างและการสาธิตวิธีต่างๆในการดำเนินการเบื้องหลังบางอย่าง


@Snicolas หวัดดีค่ะ ฉันมีแอพที่สแกนข้อมูลจากแท็ก NFC และส่งไปยังเซิร์ฟเวอร์ ทำงานได้ดีในพื้นที่สัญญาณดี แต่ไม่มีสัญญาณ AsyncTask ที่ทำให้ webcall ทำงานต่อไป เช่นกล่องโต้ตอบความคืบหน้าจะทำงานเป็นนาทีจากนั้นเมื่อมันหายไปหน้าจอจะเปลี่ยนเป็นสีดำและไม่ตอบสนอง AsyncTask ของฉันเป็นคลาสภายใน ฉันกำลังเขียน Handler เพื่อยกเลิกงานหลังจาก X วินาที ดูเหมือนว่าแอปจะส่งข้อมูลเก่าไปยังเซิร์ฟเวอร์หลายชั่วโมงหลังการสแกน อาจเป็นเพราะ AsyncTask ไม่จบและอาจจะเสร็จหลายชั่วโมงหลังจากนั้น? ฉันจะขอบคุณข้อมูลเชิงลึกใด ๆ ขอบคุณ
เต่าบอย

ติดตามเพื่อดูว่าเกิดอะไรขึ้น แต่ใช่มันเป็นไปได้มากทีเดียว! หากคุณต้องการ asynctask ของคุณคุณสามารถยกเลิกได้อย่างถูกต้องนั่นจะเป็นจุดเริ่มต้นที่ดีหากคุณไม่ต้องการย้ายไปยัง RS หรือบริการ ...
Snicolas

@Snicolas ขอบคุณสำหรับการตอบกลับ ฉันโพสต์ใน SO เมื่อวานนี้โดยสรุปปัญหาของฉันและแสดงรหัสตัวจัดการที่ฉันเขียนขึ้นเพื่อลองหยุด AsyncTask หลังจาก 8 วินาที คุณช่วยพิจารณาดูไหมถ้าคุณมีเวลา การเรียก AsyncTask.cancel (true) จาก Handler จะยกเลิกงานอย่างถูกต้องหรือไม่ ฉันรู้ว่าฉันควรตรวจสอบค่า iscancelled () เป็นระยะ ๆ ใน doInBackgroud ของฉัน แต่ฉันไม่คิดว่าจะใช้ได้กับสถานการณ์ของฉันเพราะฉันแค่สร้างเว็บคอล HttpPost เพียงบรรทัดเดียวและไม่ได้เผยแพร่การอัปเดตบน UI มีการเปลี่ยนแปลง AsyncTask หรือไม่ เช่นสามารถสร้าง HttPost จาก IntentService ได้หรือไม่
turtleboy

นี่คือลิงค์stackoverflow.com/questions/17725767/…
turtleboy

คุณควรลองที่ RoboSpice (บน github) ;)
Snicolas

38

เพราะอะไร?

เนื่องจากAsyncTaskโดยค่าเริ่มต้นใช้เธรดพูลที่คุณไม่ได้สร้างขึ้น อย่าผูกทรัพยากรจากพูลที่คุณไม่ได้สร้างเนื่องจากคุณไม่รู้ว่าข้อกำหนดของพูลนั้นคืออะไร และอย่าผูกทรัพยากรจากพูลที่คุณไม่ได้สร้างขึ้นหากเอกสารประกอบสำหรับพูลนั้นบอกให้คุณไม่ทำเช่นเดียวกับในกรณีนี้

โดยเฉพาะอย่างยิ่งการเริ่มต้นกับ Android 3.2, สระว่ายน้ำด้ายที่ใช้โดยAsyncTaskโดยค่าเริ่มต้น (สำหรับแอปกับandroid:targetSdkVersionชุดถึง 13 หรือสูงกว่า) มีเพียงหนึ่งด้ายในนั้น - ถ้าคุณผูกขึ้นหัวข้อนี้ไปเรื่อย ๆ ไม่มีงานอื่น ๆ ของคุณจะทำงาน


ขอบคุณสำหรับคำอธิบายนี้ .. ฉันไม่พบข้อผิดพลาดอย่างแท้จริงเกี่ยวกับการใช้ AsyncTasks สำหรับการดำเนินการที่ยาวนาน (แม้ว่าโดยปกติฉันจะมอบหมายให้บริการด้วยตนเอง) แต่ข้อโต้แย้งของคุณเกี่ยวกับการผูก ThreadPool นั้น (ซึ่งคุณสามารถทำได้แน่นอน อย่าตั้งสมมติฐานเกี่ยวกับขนาดของมัน) ดูเหมือนเฉพาะจุด เพื่อเป็นส่วนเสริมของโพสต์ของ Anup เกี่ยวกับการใช้บริการ: บริการนั้นควรเรียกใช้งานบนเธรดพื้นหลังด้วยและไม่บล็อกเธรดหลักของตัวเอง ตัวเลือกอาจเป็น IntentService หรือสำหรับข้อกำหนดการทำงานพร้อมกันที่ซับซ้อนมากขึ้นให้ใช้กลยุทธ์มัลติเธรดของคุณเอง
baske

จะเหมือนกันหรือไม่แม้ว่าฉันจะเริ่ม AsyncTask ในบริการ ?? ฉันหมายความว่าการดำเนินการที่ยาวนานจะยังคงเป็นปัญหาหรือไม่?
วน

1
@eddy: ใช่เนื่องจากไม่ได้เปลี่ยนลักษณะของเธรดพูล สำหรับ a Serviceเพียงใช้ a ThreadหรือThreadPoolExecutor.
CommonsWare

ขอบคุณ @CommonsWare เป็นเพียงคำถามสุดท้าย TimerTasks แบ่งปันข้อเสียเดียวกันของ AsyncTasks หรือไม่? หรือมันแตกต่างกันทั้งหมด?
วน

1
@eddy: TimerTaskมาจาก Java มาตรฐานไม่ใช่ Android TimerTaskได้ถูกละทิ้งไปโดยส่วนใหญ่ScheduledExecutorService(ซึ่งแม้จะมีชื่อ แต่ก็เป็นส่วนหนึ่งของ Java มาตรฐาน) ไม่ได้เชื่อมโยงกับ Android ดังนั้นคุณยังต้องใช้บริการหากคุณคาดหวังว่าสิ่งเหล่านั้นจะทำงานในพื้นหลัง และคุณควรพิจารณาจาก Android จริงๆAlarmManagerดังนั้นคุณไม่จำเป็นต้องใช้บริการเพียงแค่ดูนาฬิกา
CommonsWare

4

งาน Aysnc เป็นเธรดพิเศษที่ยังคงใช้กับ GUI ของแอปของคุณ แต่ในขณะที่ยังคงรักษางานที่ต้องใช้ทรัพยากรมากของเธรด UI ดังนั้นเมื่อสิ่งต่างๆเช่นการอัปเดตรายการการเปลี่ยนมุมมองของคุณและอื่น ๆ ทำให้คุณต้องดำเนินการดึงข้อมูลหรืออัปเดตคุณควรใช้งาน async เพื่อให้การดำเนินการเหล่านี้ไม่อยู่ในเธรด UI แต่โปรดทราบว่าการดำเนินการเหล่านี้ยังคงเชื่อมต่อกับ UI อยู่ .

สำหรับงานที่ทำงานเป็นเวลานานซึ่งไม่จำเป็นต้องมีการอัปเดต UI คุณสามารถใช้บริการแทนได้เนื่องจากสามารถใช้งานได้แม้ไม่มี UI

ดังนั้นสำหรับงานสั้น ๆ ให้ใช้งาน async เพราะระบบปฏิบัติการอาจถูกฆ่าได้หลังจากกิจกรรมการวางไข่ของคุณตาย (โดยปกติจะไม่ตายระหว่างการทำงาน แต่จะทำงานให้เสร็จสิ้น) และสำหรับงานที่ต้องใช้เวลานานและซ้ำ ๆ ให้ใช้บริการแทน

สำหรับข้อมูลเพิ่มเติมโปรดดูหัวข้อ:

AsyncTask นานกว่าสองสามวินาที?

และ

AsyncTask จะไม่หยุดแม้ว่ากิจกรรมจะถูกทำลายไปแล้วก็ตาม


1

ปัญหาของ AsyncTask คือหากกำหนดให้เป็นคลาสภายในที่ไม่คงที่ของกิจกรรมจะมีการอ้างอิงถึงกิจกรรม ในสถานการณ์ที่กิจกรรมคอนเทนเนอร์ของงาน async เสร็จสิ้น แต่การทำงานเบื้องหลังใน AsyncTask ยังคงดำเนินต่อไปอ็อบเจ็กต์กิจกรรมจะไม่ถูกเก็บรวบรวมขยะเนื่องจากมีการอ้างอิงถึงทำให้หน่วยความจำรั่วไหล

วิธีแก้ไขปัญหานี้คือกำหนดasync task เป็นคลาสกิจกรรมภายในแบบคงที่และใช้การอ้างอิงที่ไม่เหมาะสมกับบริบท

แต่ถึงกระนั้นก็เป็นความคิดที่ดีที่จะใช้สำหรับงานพื้นหลังที่เรียบง่ายและรวดเร็ว ในการพัฒนาแอปด้วยรหัสที่สะอาดควรใช้RxJavaเพื่อรันงานเบื้องหลังที่ซับซ้อนและอัปเดต UI ด้วยผลลัพธ์จากแอป

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.