SurfaceView และ GLSurfaceView

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

SurfaceView

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

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

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

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

พื้นผิวใหม่เป็นด้านผู้ผลิตของ BufferQueue ซึ่งผู้บริโภคเป็นเลเยอร์ SurfaceFlinger คุณสามารถอัปเดตพื้นผิวด้วยกลไกใดๆ ก็ตามที่สามารถป้อน BufferQueue เช่น ฟังก์ชัน Canvas ที่ให้พื้นผิว การติด EGLSurface และการวาดบนพื้นผิวด้วย GLES หรือการกำหนดค่าตัวถอดรหัสสื่อเพื่อเขียนพื้นผิว

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

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

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

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

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

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

หากคุณคลิกกลับ คุณจะได้รับ:

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

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

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

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

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

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

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

GLSurfaceView

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

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

ในกรณีส่วนใหญ่ GLSurfaceView จะทำให้การทำงานกับ GLES ง่ายขึ้น ในบางสถานการณ์ก็สามารถขวางทางได้