Giao diện người dùng khung ứng dụng Android dựa trên hệ thống phân cấp của các đối tượng bắt đầu bằng Chế độ xem . Tất cả các thành phần giao diện người dùng đều trải qua một loạt phép đo và quy trình bố trí sao cho chúng phù hợp với khu vực hình chữ nhật. Sau đó, tất cả các đối tượng chế độ xem hiển thị sẽ được hiển thị trên một bề mặt được WindowManager thiết lập khi ứng dụng được đưa lên nền trước. Chuỗi giao diện người dùng của ứng dụng thực hiện bố cục và hiển thị vào bộ đệm trên mỗi khung hình.
SurfaceView
SurfaceView là một thành phần mà bạn có thể sử dụng để nhúng một lớp tổng hợp bổ sung trong hệ thống phân cấp chế độ xem của mình. SurfaceView có các tham số bố cục giống như các chế độ xem khác, do đó, nó có thể được thao tác giống như bất kỳ chế độ xem nào khác, nhưng nội dung của SurfaceView thì trong suốt.
Khi kết xuất bằng nguồn bộ đệm bên ngoài, chẳng hạn như bối cảnh GL hoặc bộ giải mã phương tiện, bạn cần sao chép bộ đệm từ nguồn bộ đệm để hiển thị bộ đệm trên màn hình. Sử dụng SurfaceView cho phép bạn làm điều đó.
Khi thành phần khung nhìn của SurfaceView sắp hiển thị, khung sẽ yêu cầu SurfaceControl yêu cầu một bề mặt mới từ SurfaceFlinger. Để nhận lệnh gọi lại khi bề mặt được tạo hoặc hủy, hãy sử dụng giao diện SurfaceHolder . Theo mặc định, bề mặt mới tạo được đặt phía sau bề mặt giao diện người dùng ứng dụng. Bạn có thể ghi đè thứ tự Z mặc định để đặt bề mặt mới lên trên.
Kết xuất bằng SurfaceView có lợi trong trường hợp bạn cần kết xuất trên một bề mặt riêng biệt, chẳng hạn như khi bạn kết xuất bằng API Camera hoặc ngữ cảnh OpenGL ES. Khi bạn kết xuất bằng SurfaceView, SurfaceFlinger trực tiếp tạo bộ đệm cho màn hình. Nếu không có SurfaceView, bạn cần tổng hợp các bộ đệm vào một bề mặt ngoài màn hình, sau đó được tổng hợp vào màn hình, do đó việc hiển thị bằng SurfaceView sẽ loại bỏ công việc bổ sung. Sau khi kết xuất bằng SurfaceView, hãy sử dụng chuỗi giao diện người dùng để phối hợp với vòng đời hoạt động và thực hiện các điều chỉnh về kích thước hoặc vị trí của chế độ xem nếu cần. Sau đó, Trình soạn thảo phần cứng sẽ trộn giao diện người dùng ứng dụng và các lớp khác.
Bề mặt mới là phía sản xuất của BufferQueue, nơi tiêu thụ của nó là lớp SurfaceFlinger. Bạn có thể cập nhật bề mặt bằng bất kỳ cơ chế nào có thể cung cấp BufferQueue, chẳng hạn như các hàm Canvas do bề mặt cung cấp, gắn EGLSurface và vẽ trên bề mặt bằng GLES hoặc định cấu hình bộ giải mã phương tiện để ghi bề mặt.
SurfaceView và vòng đời hoạt động
Khi sử dụng SurfaceView, hãy hiển thị bề mặt từ một luồng không phải là luồng UI chính.
Đối với một hoạt động có SurfaceView, có hai máy trạng thái riêng biệt nhưng phụ thuộc lẫn nhau:
- Ứng dụng
onCreate
/onResume
/onPause
- Bề mặt được tạo/thay đổi/hủy
Khi hoạt động bắt đầu, bạn sẽ nhận được lệnh gọi lại theo thứ tự sau:
-
onCreate()
-
onResume()
-
surfaceCreated()
-
surfaceChanged()
Nếu bạn nhấp lại, bạn sẽ nhận được:
-
onPause()
-
surfaceDestroyed()
(được gọi ngay trước khi bề mặt biến mất)
Nếu bạn xoay màn hình, hoạt động sẽ được chia nhỏ và được tạo lại và bạn có được toàn bộ chu trình. Bạn có thể biết đó là khởi động lại nhanh bằng cách kiểm tra isFinishing()
. Có thể bắt đầu/dừng một hoạt động nhanh đến mức surfaceCreated()
xảy ra sau onPause()
.
Nếu bạn nhấn vào nút nguồn để làm trống màn hình, bạn chỉ nhận được onPause()
mà không có surfaceDestroyed()
. Bề mặt vẫn hoạt động và quá trình kết xuất có thể tiếp tục. Bạn có thể tiếp tục nhận các sự kiện Biên đạo nếu bạn tiếp tục yêu cầu chúng. Nếu màn hình khóa của bạn buộc phải xoay theo hướng khác thì hoạt động của bạn có thể được khởi động lại khi thiết bị được bỏ trống. Nếu không, bạn có thể thoát khỏi màn hình trống với bề mặt giống như trước.
Tuổi thọ của sợi chỉ có thể được gắn với bề mặt hoặc hoạt động, tùy thuộc vào điều bạn muốn xảy ra khi màn hình trống. Chuỗi có thể bắt đầu/dừng khi bắt đầu/dừng hoạt động hoặc khi tạo/hủy trên bề mặt.
Việc bắt đầu/dừng chuỗi trên Bắt đầu/dừng hoạt động sẽ hoạt động tốt với vòng đời của ứng dụng. Bạn bắt đầu chuỗi kết xuất trong onResume()
và dừng nó trong onStop()
. Khi tạo và định cấu hình luồng, đôi khi bề mặt đã tồn tại, đôi khi thì không (ví dụ: nó vẫn hoạt động sau khi chuyển đổi màn hình bằng nút nguồn). Bạn phải đợi bề mặt được tạo trước khi khởi tạo trong luồng. Bạn không thể khởi tạo lệnh gọi lại surfaceCreate()
vì nó sẽ không kích hoạt lại nếu bề mặt không được tạo lại. Thay vào đó, hãy truy vấn hoặc lưu trữ trạng thái bề mặt và chuyển tiếp nó tới luồng kết xuất.
Việc bắt đầu/dừng luồng trên tạo/hủy bề mặt hoạt động tốt vì bề mặt và trình kết xuất được đan xen một cách hợp lý. Bạn bắt đầu luồng sau khi bề mặt được tạo, điều này tránh được một số lo ngại về giao tiếp giữa các luồng; và các tin nhắn được tạo/thay đổi bề mặt được chuyển tiếp đơn giản. Để đảm bảo kết xuất dừng khi màn hình trống và tiếp tục lại khi màn hình không trống, hãy yêu cầu Biên đạo ngừng gọi lại lệnh gọi lại vẽ khung. onResume()
tiếp tục các lệnh gọi lại nếu luồng trình kết xuất đang chạy. Tuy nhiên, nếu bạn tạo hoạt ảnh dựa trên thời gian trôi qua giữa các khung hình, có thể có một khoảng cách lớn trước khi sự kiện tiếp theo diễn ra; sử dụng thông báo tạm dừng/tiếp tục rõ ràng có thể giải quyết vấn đề này.
Cả hai tùy chọn, cho dù tuổi thọ của luồng được gắn với Hoạt động hay bề mặt, đều tập trung vào cách cấu hình luồng của trình kết xuất và liệu nó có đang thực thi hay không. Mối quan tâm liên quan là trích xuất trạng thái từ luồng khi hoạt động bị hủy (trong onStop()
hoặc onSaveInstanceState()
); trong những trường hợp như vậy, việc gắn tuổi thọ của luồng với hoạt động là tốt nhất vì sau khi luồng của trình kết xuất được nối, trạng thái của luồng được kết xuất có thể được truy cập mà không cần đồng bộ hóa nguyên thủy.
GLSurfaceView
Lớp GLSurfaceView cung cấp các lớp trợ giúp để quản lý bối cảnh EGL, giao tiếp giữa các luồng và tương tác với vòng đời hoạt động. Bạn không cần sử dụng GLSurfaceView để sử dụng GLES.
Ví dụ: GLSurfaceView tạo một luồng để hiển thị và định cấu hình ngữ cảnh EGL ở đó. Trạng thái được dọn sạch tự động khi hoạt động tạm dừng. Hầu hết các ứng dụng không cần biết gì về EGL để sử dụng GLES với GLSurfaceView.
Trong hầu hết các trường hợp, GLSurfaceView có thể giúp làm việc với GLES dễ dàng hơn. Trong một số tình huống, nó có thể cản trở.