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 ที่จัดหาโดย Surface การแนบ 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 ได้ง่ายขึ้น ในบางสถานการณ์ก็อาจขวางทางได้