SurfaceView และ GLSurfaceView

UI ของเฟรมเวิร์กแอป Android อิงตามลำดับชั้นของออบเจ็กต์ที่เริ่มต้นด้วย View องค์ประกอบ UI ทั้งหมดจะผ่านกระบวนการวัดและเลย์เอาต์หลายขั้นตอนเพื่อ จัดวางองค์ประกอบเหล่านั้นในพื้นที่สี่เหลี่ยมผืนผ้า จากนั้นเฟรมเวิร์กจะแสดงผลออบเจ็กต์ View ที่มองเห็นได้ทั้งหมดไปยัง Surface ที่ตั้งค่าโดย WindowManager เมื่อแอป ถูกนำมาไว้ที่เบื้องหน้า เธรด UI ของแอปจะทำการจัดวางและ การแสดงผลไปยังบัฟเฟอร์ต่อเฟรม

SurfaceView

SurfaceView เป็นคอมโพเนนต์ที่คุณใช้ฝังเลเยอร์คอมโพสิตเพิ่มเติมภายในลำดับชั้นของ View ได้ SurfaceView ใช้พารามิเตอร์เลย์เอาต์เดียวกันกับ View อื่นๆ จึงสามารถจัดการได้เหมือนกับ View อื่นๆ แต่เนื้อหาของ SurfaceView จะโปร่งใส

เมื่อแสดงผลด้วยแหล่งที่มาของบัฟเฟอร์ภายนอก เช่น บริบท GL หรือ ตัวถอดรหัสสื่อ คุณต้องคัดลอกบัฟเฟอร์จากแหล่งที่มาของบัฟเฟอร์เพื่อแสดง บัฟเฟอร์บนหน้าจอ ซึ่งทำได้โดยใช้ SurfaceView

เมื่อคอมโพเนนต์มุมมองของ SurfaceView กำลังจะปรากฏ เฟรมเวิร์กจะขอให้ SurfaceControl ส่งคำขอ Surface ใหม่จาก SurfaceFlinger หากต้องการรับการเรียกกลับเมื่อมีการสร้างหรือทำลาย Surface ให้ใช้อินเทอร์เฟซ SurfaceHolder โดยค่าเริ่มต้น เฟรมเวิร์กจะวาง Surface ที่สร้างขึ้นใหม่ไว้ด้านหลัง Surface ของ UI แอป คุณสามารถลบล้างการจัดลำดับ Z เริ่มต้นเพื่อวางพื้นผิวใหม่ไว้ด้านบนได้

การแสดงผลด้วย SurfaceView มีประโยชน์ในกรณีที่คุณต้องแสดงผล ไปยัง Surface อื่น เช่น เมื่อคุณแสดงผลด้วย Camera API หรือบริบท OpenGL ES เมื่อคุณแสดงผลด้วย SurfaceView, SurfaceFlinger จะ เขียนบัฟเฟอร์ลงในหน้าจอโดยตรง หากไม่มี SurfaceView คุณจะต้องคอมโพสิตบัฟเฟอร์ไปยังพื้นผิวที่อยู่นอกหน้าจอ ซึ่งจะได้รับการคอมโพสิตไปยังหน้าจออีกครั้ง ดังนั้นการแสดงผลด้วย SurfaceView จึงช่วยลดงานที่ไม่จำเป็น หลังจากแสดงผลด้วย SurfaceView แล้ว ให้ใช้เทรด UI เพื่อประสานงานกับวงจรของกิจกรรม และปรับขนาดหรือตำแหน่งของ View หากจำเป็น จากนั้น Hardware Composer จะผสาน UI ของแอปกับเลเยอร์อื่นๆ

Surface ใหม่คือฝั่ง Producer ของ BufferQueue ซึ่งมีเลเยอร์ SurfaceFlinger เป็น Consumer คุณอัปเดต Surface ด้วยกลไกใดก็ได้ที่ป้อน BufferQueue ได้ เช่น ฟังก์ชัน Canvas ที่ Surface จัดหาให้ การแนบ EGLSurface และการวาดบน Surface ด้วย GLES หรือการกำหนดค่าตัวถอดรหัสสื่อให้เขียน Surface

SurfaceView และวงจรของกิจกรรม

เมื่อใช้ SurfaceView ให้แสดงผล Surface จากเธรดอื่นที่ไม่ใช่ เธรด UI หลัก

สำหรับกิจกรรมที่มี SurfaceView จะมีเครื่องสถานะ 2 เครื่องที่แยกกันแต่ขึ้นอยู่กับกัน

  • แอป onCreate/onResume/onPause
  • สร้าง/เปลี่ยน/ทำลายพื้นผิว

เมื่อกิจกรรมเริ่มต้น คุณจะได้รับ Callback ตามลำดับต่อไปนี้

  1. onCreate()
  2. onResume()
  3. surfaceCreated()
  4. surfaceChanged()

หากคลิกย้อนกลับ คุณจะได้รับสิ่งต่อไปนี้

  1. onPause()
  2. surfaceDestroyed() (เรียกใช้ก่อนที่พื้นผิวจะหายไป)

หากหมุนหน้าจอ ระบบจะปิดและสร้างกิจกรรมขึ้นใหม่ และคุณจะเห็นวงจรทั้งหมด คุณจะทราบว่าเป็นการรีสตาร์ทอย่างรวดเร็วได้โดยดูที่ isFinishing() คุณอาจเริ่ม/หยุดกิจกรรมอย่างรวดเร็ว จนทำให้ surfaceCreated() เกิดขึ้นหลังจาก onPause()

หากแตะปุ่มเปิด/ปิดเพื่อทำให้หน้าจอว่าง คุณจะเห็นเฉพาะ onPause() โดยไม่มี surfaceDestroyed() พื้นผิว ยังคงใช้งานได้ และการแสดงผลจะดำเนินต่อไป คุณจะยังคงรับเหตุการณ์ Choreographer ได้หากยังคงส่งคำขอ หากคุณมีหน้าจอล็อกที่บังคับให้ใช้การวางแนวอื่น ระบบอาจรีสตาร์ทกิจกรรมเมื่ออุปกรณ์เลิกแสดงหน้าจอว่าง มิเช่นนั้น คุณจะออกจากหน้าจอว่างได้โดยใช้ พื้นผิวเดิม

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

การให้เธรดเริ่ม/หยุดเมื่อกิจกรรมเริ่ม/หยุดจะทำงานได้ดีกับวงจรของแอป คุณเริ่มเธรดโปรแกรมแสดงผลใน onResume() และหยุด ใน onStop() เมื่อสร้างและกำหนดค่าเธรด บางครั้งพื้นผิวมีอยู่แล้ว แต่บางครั้งก็ไม่มี (เช่น ยังคงใช้งานได้หลังจากสลับหน้าจอด้วยปุ่มเปิด/ปิด) คุณต้องรอให้สร้าง Surface ก่อนจึงจะเริ่มต้นในเธรดได้ คุณไม่สามารถ เริ่มต้นในแฮนเดิล surfaceCreate() ได้เนื่องจากจะไม่ทริกเกอร์ อีกครั้งหากไม่ได้สร้าง Surface ใหม่ แต่ให้ค้นหาหรือแคชสถานะ Surface แล้วส่งต่อให้เธรดโปรแกรมแสดงผลแทน

การเริ่ม/หยุดเทรดใน Surface Create/Destroy ทำงานได้ดีเนื่องจาก Surface และ Renderer เกี่ยวพันกันในเชิงตรรกะ คุณเริ่มเธรด หลังจากสร้างพื้นผิวแล้ว ซึ่งจะช่วยหลีกเลี่ยงข้อกังวลเกี่ยวกับการสื่อสารระหว่างเธรด และระบบจะส่งต่อข้อความที่สร้าง/เปลี่ยนแปลงพื้นผิว หากต้องการยืนยันว่า การแสดงผลจะหยุดเมื่อหน้าจอว่างเปล่าและกลับมาทำงานต่อเมื่อหน้าจอไม่ว่างเปล่าแล้ว ให้บอก Choreographer ให้หยุดเรียกใช้โค้ดเรียกกลับการวาดเฟรม onResume() จะกลับมาเรียกใช้แฮนเดิลการเรียกกลับหากเธรดของโปรแกรมแสดงผลทำงานอยู่ อย่างไรก็ตาม หากคุณ สร้างภาพเคลื่อนไหวตามเวลาที่ผ่านไประหว่างเฟรม อาจมีช่องว่างขนาดใหญ่ก่อนที่ เหตุการณ์ถัดไปจะมาถึง การใช้ข้อความหยุดชั่วคราว/กลับมาทำงานต่อที่ชัดเจนจะช่วยแก้ปัญหานี้ได้

ทั้ง 2 ตัวเลือก ไม่ว่าอายุการใช้งานของเธรดจะเชื่อมโยงกับกิจกรรม หรือพื้นผิว จะมุ่งเน้นที่วิธี กำหนดค่าเธรดโปรแกรมแสดงผลและดูว่าเธรดกำลังดำเนินการอยู่หรือไม่ ข้อกังวลที่เกี่ยวข้องคือการดึงสถานะ จากเธรดเมื่อกิจกรรมถูกปิด (ใน onStop() หรือ onSaveInstanceState()) ในกรณีเช่นนี้ การเชื่อมโยงอายุการใช้งานของ เธรดกับกิจกรรมจะดีที่สุดเนื่องจาก หลังจากเข้าร่วมเธรดของโปรแกรมแสดงผลแล้ว ก็จะเข้าถึงสถานะของเธรดที่แสดงผลได้โดยไม่ต้องใช้ Primitive การซิงโครไนซ์

GLSurfaceView

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

ตัวอย่างเช่น GLSurfaceView จะสร้างเธรดสำหรับการแสดงผลและกำหนดค่าบริบท EGL ที่นั่น ระบบจะล้างสถานะโดยอัตโนมัติเมื่อกิจกรรม หยุดชั่วคราว แอปส่วนใหญ่ไม่จำเป็นต้องทราบเกี่ยวกับ EGL เพื่อใช้ GLES กับ GLSurfaceView

ในกรณีส่วนใหญ่ GLSurfaceView จะช่วยให้การทำงานกับ GLES ง่ายขึ้น ในบางสถานการณ์ การแสดงผลอาจขัดขวางการทำงาน