เมื่อคอมโพเนนต์ของแอปพลิเคชันเริ่มต้นทำงานและแอปพลิเคชันไม่มีคอมโพเนนต์อื่นทำงานอยู่ ระบบ Android จะเริ่มกระบวนการ Linux ใหม่สำหรับแอปพลิเคชันที่มีเธรดการดำเนินการเดียว โดยค่าเริ่มต้น คอมโพเนนต์ทั้งหมดของแอปพลิเคชันเดียวกันจะทำงานในกระบวนการและเธรดเดียวกัน ซึ่งเรียกว่าเธรด หลัก
หากคอมโพเนนต์ของแอปพลิเคชันเริ่มต้นทำงานและมีกระบวนการสำหรับแอปพลิเคชันนั้นอยู่แล้วเนื่องจากคอมโพเนนต์อื่นจากแอปพลิเคชันเริ่มต้นทำงาน คอมโพเนนต์จะเริ่มต้นทำงานภายในกระบวนการนั้นและใช้เธรดการดำเนินการเดียวกัน อย่างไรก็ตาม คุณสามารถจัดให้คอมโพเนนต์ต่างๆ ในแอปพลิเคชันทำงานในกระบวนการแยกกันได้ และสร้างเธรดเพิ่มเติมสำหรับกระบวนการใดก็ได้
เอกสารนี้จะอธิบายวิธีการทำงานของกระบวนการและเธรดในแอปพลิเคชัน Android
กระบวนการ
โดยค่าเริ่มต้น คอมโพเนนต์ทั้งหมดของแอปพลิเคชันจะทำงานในกระบวนการเดียวกัน และแอปพลิเคชันส่วนใหญ่จะไม่เปลี่ยนแปลงการตั้งค่านี้ อย่างไรก็ตาม หากคุณพบว่าจำเป็นต้องควบคุมกระบวนการที่คอมโพเนนต์หนึ่งๆ สังกัดอยู่ คุณสามารถทำได้ในไฟล์ Manifest
รายการ Manifest สำหรับองค์ประกอบคอมโพเนนต์แต่ละประเภท ได้แก่ <activity>, <service>, <receiver>, และ <provider> รองรับแอตทริบิวต์ android:process ที่ระบุกระบวนการที่คอมโพเนนต์ทำงานอยู่ได้ คุณสามารถตั้งค่าแอตทริบิวต์นี้เพื่อให้คอมโพเนนต์แต่ละรายการทำงานในกระบวนการของตัวเอง หรือเพื่อให้คอมโพเนนต์บางรายการใช้กระบวนการร่วมกันในขณะที่คอมโพเนนต์อื่นๆ ไม่ใช้
นอกจากนี้ คุณยังตั้งค่า android:process เพื่อให้คอมโพเนนต์ของแอปพลิเคชันต่างๆ ทำงานในกระบวนการเดียวกันได้ โดยมีเงื่อนไขว่าแอปพลิเคชันเหล่านั้นใช้รหัสผู้ใช้ Linux เดียวกันและลงชื่อด้วยใบรับรองเดียวกัน
องค์ประกอบ <application>
ยังรองรับแอตทริบิวต์ android:process ซึ่งคุณสามารถใช้เพื่อตั้งค่าเริ่มต้นที่ใช้กับคอมโพเนนต์ทั้งหมดได้
Android อาจตัดสินใจปิดกระบวนการในบางจุดเมื่อกระบวนการอื่นๆ ที่ให้บริการแก่ผู้ใช้โดยตรงกว่าจำเป็นต้องใช้ทรัพยากร คอมโพเนนต์ของแอปพลิเคชันที่ทำงานในกระบวนการที่ปิดอยู่จะถูกทำลายไปด้วย ระบบจะเริ่มกระบวนการอีกครั้งสำหรับคอมโพเนนต์เหล่านั้นเมื่อมีงานให้ทำ
เมื่อตัดสินใจว่าจะปิดกระบวนการใด ระบบ Android จะพิจารณาความสำคัญสัมพัทธ์ของกระบวนการนั้นๆ ต่อผู้ใช้ เช่น ระบบจะปิดกระบวนการที่โฮสต์กิจกรรมที่ไม่ปรากฏบนหน้าจอได้ง่ายกว่ากระบวนการที่โฮสต์กิจกรรมที่ปรากฏ ดังนั้น การตัดสินใจว่าจะสิ้นสุดกระบวนการหรือไม่จึงขึ้นอยู่กับสถานะของคอมโพเนนต์ที่ทำงานในกระบวนการนั้น
รายละเอียดของวงจรการทำงานของกระบวนการและความสัมพันธ์กับสถานะของแอปพลิเคชันจะอธิบายไว้ใน หัวข้อกระบวนการและวงจรการทำงานของแอป
เธรด
เมื่อเปิดแอปพลิเคชัน ระบบจะสร้างเธรดการดำเนินการสำหรับแอปพลิเคชัน ซึ่งเรียกว่า เธรดหลัก เธรดนี้มีความสำคัญมากเนื่องจากมีหน้าที่ส่งเหตุการณ์ไปยังวิดเจ็ตอินเทอร์เฟซผู้ใช้ที่เหมาะสม รวมถึงเหตุการณ์การวาด นอกจากนี้ เธรดนี้ยังเป็นเธรดที่แอปพลิเคชันของคุณโต้ตอบกับคอมโพเนนต์จากแพ็กเกจ android.widget และ android.view ของชุดเครื่องมือ Android UI เกือบตลอดเวลา
ด้วยเหตุนี้ บางครั้งจึงเรียกเธรดหลักว่า เธรด UI อย่างไรก็ตาม ในบางกรณีพิเศษ เธรดหลักของแอปอาจไม่ใช่เธรด UI ดูข้อมูลเพิ่มเติมได้ที่ คำอธิบายประกอบเธรด
ระบบ จะไม่ สร้างเธรดแยกต่างหากสำหรับแต่ละอินสแตนซ์ของคอมโพเนนต์ คอมโพเนนต์ทั้งหมดที่ทำงานในกระบวนการเดียวกันจะได้รับการสร้างอินสแตนซ์ในเธรด UI และการเรียกใช้ระบบไปยังคอมโพเนนต์แต่ละรายการจะถูกส่งจากเธรดนั้น ดังนั้น เมธอดที่ตอบสนองต่อการเรียกกลับของระบบ เช่น onKeyDown() เพื่อรายงานการดำเนินการของผู้ใช้ หรือเมธอดการเรียกกลับของวงจรการทำงาน จะทำงานในเธรด UI ของกระบวนการเสมอ
ตัวอย่างเช่น เมื่อผู้ใช้แตะปุ่มบนหน้าจอ เธรด UI ของแอปจะส่งการโต้ตอบแบบสัมผัสไปยังวิดเจ็ต ซึ่งจะตั้งค่าสถานะเป็น "กด" และโพสต์คำขอให้วาดใหม่ไปยังคิวเหตุการณ์ เธรด UI จะนำคำขอออกจากคิวและแจ้งให้วิดเจ็ตวาดตัวเองใหม่
หากคุณไม่ได้ใช้แอปพลิเคชันอย่างเหมาะสม โมเดลเธรดเดียวนี้อาจทำให้ประสิทธิภาพการทำงานไม่ดีเมื่อแอปทำงานหนักเพื่อตอบสนองต่อการโต้ตอบของผู้ใช้ การดำเนินการที่ใช้เวลานานในเธรด UI เช่น การเข้าถึงเครือข่ายหรือการค้นหาฐานข้อมูล จะบล็อก UI ทั้งหมด เมื่อเธรดถูกบล็อก ระบบจะไม่สามารถส่งเหตุการณ์ใดๆ ได้ รวมถึงเหตุการณ์การวาด
จากมุมมองของผู้ใช้ แอปพลิเคชันจะดูเหมือนหยุดตอบสนอง และที่แย่กว่านั้นคือ หากเธรด UI ถูกบล็อกนานกว่า 2-3 วินาที ผู้ใช้จะเห็นกล่องโต้ตอบ "แอปพลิเคชันไม่ ตอบสนอง" (ANR) จากนั้นผู้ใช้อาจตัดสินใจออกจากแอปพลิเคชันหรือถอนการติดตั้งแอปพลิเคชัน
โปรดทราบว่าชุดเครื่องมือ Android UI ไม่ใช่ เธรดที่ปลอดภัย ดังนั้นอย่าจัดการ UI จากเธรดผู้ปฏิบัติงาน ให้จัดการอินเทอร์เฟซผู้ใช้ทั้งหมดจากเธรด UI โมเดลเธรดเดียวของ Android มีกฎ 2 ข้อดังนี้
- อย่าบล็อกเธรด UI
- อย่าเข้าถึงชุดเครื่องมือ Android UI จากภายนอกเธรด UI
เธรดผู้ปฏิบัติงาน
เนื่องจากโมเดลเธรดเดียวนี้ การไม่บล็อกเธรด UI จึงมีความสำคัญอย่างยิ่งต่อการตอบสนองของ UI ของแอปพลิเคชัน หากมีการดำเนินการที่ต้องใช้เวลาในการดำเนินการ โปรดดำเนินการในเธรด เบื้องหลัง หรือเธรด ผู้ปฏิบัติงาน แยกต่างหาก เพียงจำไว้ว่าคุณไม่สามารถอัปเดต UI จากเธรดอื่นที่ไม่ใช่เธรด UI หรือเธรดหลัก
Android มีหลายวิธีในการเข้าถึงเธรด UI จากเธรดอื่นๆ เพื่อช่วยให้คุณปฏิบัติตามกฎเหล่านี้ได้ รายการเมธอดที่ช่วยคุณได้มีดังนี้
ตัวอย่างต่อไปนี้แสดงวิธีลดภาระงานไปยังเธรดเบื้องหลังและอัปเดตเธรด UI เมื่อทำงานเสร็จแล้ว
Kotlin
// Kotlin coroutines implementation. fun onClick(v: View) { // Launch a coroutine in the lifecycle scope (e.g., in an Activity or Fragment). lifecycleScope.launch { // Run the blocking task on the IO dispatcher. val bitmap = withContext(Dispatchers.IO) { BitmapFactory.decodeFile("image.png") } // Back on the main thread, update the UI. imageView.setImageBitmap(bitmap) } }
Java
// Java Executor implementation. // (executorService is assumed to be defined elsewhere). public void onClick(View v) { executorService.execute(() -> { // Run the heavy task on a background thread. Bitmap bitmap = BitmapFactory.decodeFile("image.png"); // Update the View on the UI thread. imageView.post(() -> imageView.setImageBitmap(bitmap)); }); }
การใช้งานนี้เป็นเธรดที่ปลอดภัยเนื่องจากระบบจะดำเนินการเบื้องหลังจากเธรดแยกต่างหากในขณะที่ระบบจะจัดการ ImageView จากเธรด UI เสมอ
อย่างไรก็ตาม เมื่อการดำเนินการมีความซับซ้อนมากขึ้น โค้ดประเภทนี้อาจซับซ้อนและดูแลรักษายาก หากต้องการจัดการการโต้ตอบที่ซับซ้อนมากขึ้นกับเธรดผู้ปฏิบัติงาน คุณอาจพิจารณาใช้ Handler ในเธรดผู้ปฏิบัติงานเพื่อประมวลผลข้อความที่ส่งจากเธรด UI ดูคำอธิบายโดยละเอียดเกี่ยวกับวิธี
จัดกำหนดการงานในเธรดเบื้องหลังและสื่อสารกลับไปยังเธรด UI ได้ที่
ภาพรวมงานเบื้องหลัง
เมธอดที่ปลอดภัยต่อเธรด
ในบางสถานการณ์ ระบบจะเรียกเมธอดที่คุณใช้จากเธรดมากกว่า 1 เธรด ดังนั้นคุณจึงต้องเขียนเมธอดให้ปลอดภัยต่อเธรด
โดยเฉพาะอย่างยิ่งสำหรับเมธอดที่เรียกจากระยะไกลได้ เช่น เมธอดในบริการที่มีผลผูกพัน เมื่อการเรียกใช้เมธอดที่ใช้ใน IBinder มาจากกระบวนการเดียวกันกับที่
IBinder ทำงานอยู่ ระบบจะดำเนินการเมธอดในเธรดของผู้เรียก
อย่างไรก็ตาม เมื่อการเรียกใช้มาจากกระบวนการอื่น ระบบจะดำเนินการเมธอดในเธรดที่เลือกจากพูลเธรดที่ระบบดูแลรักษาไว้ในกระบวนการเดียวกันกับ IBinder
ระบบจะไม่ดำเนินการในเธรด UI ของกระบวนการ
ตัวอย่างเช่น แม้ว่าระบบจะเรียกเมธอด
onBind() ของบริการจากเธรด UI ของกระบวนการของ
บริการ แต่ระบบจะเรียกเมธอดที่ใช้ในออบเจ็กต์ที่ onBind() แสดงผล เช่น คลาสย่อย
ที่ใช้เมธอดการเรียกใช้ขั้นตอนจากระยะไกล (RPC) จากเธรด
ในพูล เนื่องจากบริการอาจมีไคลเอ็นต์มากกว่า 1 รายการ เธรดพูลมากกว่า 1 เธรดจึงอาจเรียกใช้เมธอด IBinder เดียวกันในเวลาเดียวกัน ดังนั้นคุณจึงต้องใช้เมธอด IBinder ให้ปลอดภัยต่อเธรด
ในทำนองเดียวกัน ผู้ให้บริการเนื้อหาอาจได้รับคำขอข้อมูลที่มาจากกระบวนการอื่นๆ
คลาส ContentResolver และ ContentProvider จะซ่อนรายละเอียดเกี่ยวกับวิธีจัดการการสื่อสารระหว่างโปรเซส (IPC) แต่ระบบจะเรียกเมธอด ContentProvider ที่ตอบสนองต่อคำขอเหล่านั้น ได้แก่ เมธอด query(), insert(), delete(), update(), และ getType()—จากพูลเทรดในกระบวนการของ Content Provider ไม่ใช่เธรด UI สำหรับกระบวนการ เนื่องจากระบบอาจเรียกเมธอดเหล่านี้จากเธรดจำนวนเท่าใดก็ได้ในเวลาเดียวกัน คุณจึงต้องใช้เมธอดเหล่านี้ให้ปลอดภัยต่อเธรดด้วย
การสื่อสารระหว่างโปรเซส
Android มีกลไกสำหรับการสื่อสารระหว่างกระบวนการ (IPC) โดยใช้ RPC ซึ่งกิจกรรมหรือคอมโพเนนต์ของแอปพลิเคชันอื่นๆ จะเรียกเมธอด แต่ระบบจะดำเนินการเมธอดจากระยะไกลในกระบวนการอื่น และส่งผลลัพธ์กลับไปยังผู้เรียก ซึ่งเกี่ยวข้องกับการแยกการเรียกเมธอดและข้อมูลที่เกี่ยวข้องออกเป็นระดับที่ระบบปฏิบัติการเข้าใจได้ การส่งข้อมูลจากการเรียกเมธอดจากกระบวนการและพื้นที่ที่อยู่ภายในไปยังกระบวนการและพื้นที่ที่อยู่นอกระบบ และจากนั้นประกอบและดำเนินการเรียกใช้ใหม่
จากนั้นระบบจะส่งค่าที่แสดงผลในทิศทางตรงกันข้าม Android มีโค้ดทั้งหมดเพื่อดำเนินการธุรกรรม IPC เหล่านี้ คุณจึงมุ่งเน้นไปที่การกำหนดและใช้อินเทอร์เฟซการเขียนโปรแกรม RPC ได้
หากต้องการดำเนินการ IPC แอปพลิเคชันของคุณต้องผูกกับบริการโดยใช้ bindService() ดูข้อมูลเพิ่มเติมได้ที่ภาพรวมของบริการ