SurfaceView و GLSurfaceView

رابط کاربری چارچوب برنامه اندروید بر اساس سلسله مراتبی از اشیاء است که با یک View شروع می‌شوند. تمام عناصر رابط کاربری از یک سری اندازه‌گیری‌ها و یک فرآیند طرح‌بندی عبور می‌کنند که آنها را در یک ناحیه مستطیلی قرار می‌دهد. سپس، چارچوب، تمام اشیاء نمای قابل مشاهده را به سطحی که توسط WindowManager هنگام آوردن برنامه به پیش‌زمینه تنظیم شده است، رندر می‌کند. نخ رابط کاربری برنامه، طرح‌بندی و رندر را در یک بافر در هر فریم انجام می‌دهد.

سرفیس ویو

SurfaceView کامپوننتی است که می‌توانید از آن برای جاسازی یک لایه ترکیبی اضافی در سلسله مراتب view خود استفاده کنید. SurfaceView همان پارامترهای طرح‌بندی را مانند سایر viewها می‌گیرد، بنابراین می‌توان آن را مانند هر view دیگری دستکاری کرد، اما محتوای SurfaceView شفاف است.

وقتی با یک منبع بافر خارجی، مانند GL context یا یک media decoder، رندر می‌کنید، باید بافرها را از منبع بافر کپی کنید تا بافرها روی صفحه نمایش داده شوند. استفاده از SurfaceView به شما امکان انجام این کار را می‌دهد.

وقتی کامپوننت نمای SurfaceView در شرف قابل مشاهده شدن است، فریم‌ورک از SurfaceControl می‌خواهد که یک سطح جدید از SurfaceFlinger درخواست کند. برای دریافت فراخوانی‌ها هنگام ایجاد یا از بین رفتن سطح، از رابط SurfaceHolder استفاده کنید. به طور پیش‌فرض، فریم‌ورک سطح تازه ایجاد شده را پشت سطح رابط کاربری برنامه قرار می‌دهد. می‌توانید ترتیب Z پیش‌فرض را لغو کنید تا سطح جدید روی آن قرار گیرد.

رندر کردن با SurfaceView در مواردی که نیاز به رندر کردن در یک سطح جداگانه دارید، مانند زمانی که با Camera API یا یک OpenGL ES context رندر می‌کنید، مفید است. وقتی با SurfaceView رندر می‌کنید، SurfaceFlinger مستقیماً بافرها را روی صفحه نمایش می‌دهد. بدون SurfaceView، باید بافرها را روی یک سطح خارج از صفحه نمایش ترکیب کنید، که سپس روی صفحه نمایش ترکیب می‌شود، بنابراین رندر کردن با SurfaceView کار اضافی را از بین می‌برد. پس از رندر کردن با SurfaceView، از نخ UI برای هماهنگی با چرخه حیات فعالیت استفاده کنید و در صورت نیاز، تنظیماتی را در اندازه یا موقعیت نما انجام دهید. سپس، Hardware Composer رابط کاربری برنامه و لایه‌های دیگر را ترکیب می‌کند.

سطح جدید، سمت تولیدکننده‌ی یک BufferQueue است که مصرف‌کننده‌ی آن یک لایه‌ی SurfaceFlinger است. شما می‌توانید سطح را با هر مکانیزمی که بتواند یک BufferQueue را تغذیه کند، به‌روزرسانی کنید، مانند توابع Canvas ارائه شده توسط سطح، اتصال یک EGLSurface و ترسیم روی سطح با GLES، یا پیکربندی یک رمزگشای رسانه برای نوشتن سطح.

SurfaceView و چرخه حیات فعالیت

هنگام استفاده از SurfaceView، سطح را از نخی غیر از نخ اصلی رابط کاربری رندر کنید.

برای یک فعالیت با SurfaceView، دو ماشین حالت جداگانه اما وابسته به هم وجود دارد:

  • برنامه onCreate / onResume / onPause
  • سطح ایجاد شده/تغییر یافته/تخریب شده

وقتی اکتیویتی شروع می‌شود، فراخوانی‌های برگشتی به این ترتیب دریافت می‌شوند:

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

اگر روی دکمه‌ی بازگشت کلیک کنید، موارد زیر را دریافت خواهید کرد:

  1. onPause()
  2. surfaceDestroyed() (درست قبل از اینکه سطح از بین برود، فراخوانی می‌شود)

اگر صفحه را بچرخانید، اکتیویتی از هم جدا شده و دوباره ساخته می‌شود و شما چرخه کامل را خواهید دید. با بررسی isFinishing() می‌توانید متوجه شوید که این یک راه‌اندازی مجدد سریع است. می‌توان یک اکتیویتی را آنقدر سریع شروع/متوقف کرد که surfaceCreated() بعد از onPause() اتفاق بیفتد.

اگر برای خالی کردن صفحه نمایش، دکمه پاور را فشار دهید، فقط onPause() بدون surfaceDestroyed() نمایش داده می‌شود. سطح فعال باقی می‌ماند و رندرینگ می‌تواند ادامه یابد. اگر به درخواست رویدادهای Choreographer ادامه دهید، می‌توانید آنها را دریافت کنید. اگر صفحه قفلی دارید که جهت‌گیری متفاوتی را اعمال می‌کند، ممکن است فعالیت شما پس از خالی شدن صفحه نمایش، مجدداً راه‌اندازی شود. در غیر این صورت، می‌توانید با همان سطح قبلی از حالت خالی بودن صفحه نمایش خارج شوید.

طول عمر نخ می‌تواند به سطح یا به فعالیت گره خورده باشد، بسته به اینکه می‌خواهید هنگام خالی شدن صفحه چه اتفاقی بیفتد. نخ می‌تواند یا در زمان شروع/توقف فعالیت یا در زمان ایجاد/تخریب سطح شروع/متوقف شود.

شروع/توقف نخ روی شروع/توقف فعالیت، به خوبی با چرخه حیات برنامه سازگار است. شما نخ رندر را در onResume() شروع می‌کنید و آن را در onStop() متوقف می‌کنید. هنگام ایجاد و پیکربندی نخ، گاهی اوقات سطح از قبل وجود دارد، گاهی اوقات وجود ندارد (برای مثال، پس از تغییر وضعیت صفحه با دکمه پاور، هنوز فعال است). قبل از مقداردهی اولیه در نخ، باید منتظر بمانید تا سطح ایجاد شود. نمی‌توانید در فراخوانی surfaceCreate() مقداردهی اولیه انجام دهید زیرا اگر سطح دوباره ایجاد نشده باشد، دوباره اجرا نمی‌شود. در عوض، وضعیت سطح را پرس‌وجو یا ذخیره کنید و آن را به نخ رندر ارسال کنید.

شروع/توقف نخ روی ایجاد/نابودی سطح به خوبی کار می‌کند زیرا سطح و رندرکننده منطقاً در هم تنیده شده‌اند. شما نخ را پس از ایجاد سطح شروع می‌کنید، که از برخی نگرانی‌های ارتباطی بین نخ‌ها جلوگیری می‌کند؛ و پیام‌های ایجاد/تغییر سطح ارسال می‌شوند. برای تأیید اینکه رندرینگ با خالی شدن صفحه متوقف می‌شود و با رفع خالی شدن از سر گرفته می‌شود، به Choreographer بگویید که فراخوانی تابع فراخوانی frame draw را متوقف کند. onResume() در صورت اجرای رشته رندرکننده، فراخوانی‌ها را از سر می‌گیرد. با این حال، اگر انیمیشن را بر اساس زمان سپری شده بین فریم‌ها انجام دهید، ممکن است قبل از رسیدن رویداد بعدی فاصله زیادی وجود داشته باشد. استفاده از یک پیام مکث/از سرگیری صریح می‌تواند این مشکل را حل کند.

هر دو گزینه، چه طول عمر نخ به Activity گره خورده باشد و چه به surface، بر نحوه پیکربندی نخ رندرکننده و اینکه آیا در حال اجرا است یا خیر، تمرکز دارند. یک نگرانی مرتبط، استخراج حالت از نخ هنگام از بین رفتن activity (در onStop() یا onSaveInstanceState() ) است. در چنین مواردی، گره زدن طول عمر نخ به activity بهترین عملکرد را دارد زیرا پس از اتصال نخ رندرکننده، می‌توان به حالت نخ رندرشده بدون استفاده از توابع اولیه همگام‌سازی دسترسی پیدا کرد.

جی‌ال‌سرفیس‌ویو

کلاس GLSurfaceView کلاس‌های کمکی برای مدیریت زمینه‌های EGL، ارتباط بین رشته‌ای و تعامل با چرخه حیات فعالیت ارائه می‌دهد. برای استفاده از GLES نیازی به استفاده از GLSurfaceView ندارید.

برای مثال، GLSurfaceView یک thread برای رندر کردن ایجاد می‌کند و یک زمینه EGL را در آنجا پیکربندی می‌کند. وقتی فعالیت متوقف می‌شود، وضعیت به طور خودکار پاک می‌شود. اکثر برنامه‌ها برای استفاده از GLES با GLSurfaceView نیازی به دانستن چیزی در مورد EGL ندارند.

در بیشتر موارد، GLSurfaceView می‌تواند کار با GLES را آسان‌تر کند. در برخی شرایط، می‌تواند مانع شود.

،

رابط کاربری چارچوب برنامه اندروید بر اساس سلسله مراتبی از اشیاء است که با یک View شروع می‌شوند. تمام عناصر رابط کاربری از یک سری اندازه‌گیری‌ها و یک فرآیند طرح‌بندی عبور می‌کنند که آنها را در یک ناحیه مستطیلی قرار می‌دهد. سپس، چارچوب، تمام اشیاء نمای قابل مشاهده را به سطحی که توسط WindowManager هنگام آوردن برنامه به پیش‌زمینه تنظیم شده است، رندر می‌کند. نخ رابط کاربری برنامه، طرح‌بندی و رندر را در یک بافر در هر فریم انجام می‌دهد.

سرفیس ویو

SurfaceView کامپوننتی است که می‌توانید از آن برای جاسازی یک لایه ترکیبی اضافی در سلسله مراتب view خود استفاده کنید. SurfaceView همان پارامترهای طرح‌بندی را مانند سایر viewها می‌گیرد، بنابراین می‌توان آن را مانند هر view دیگری دستکاری کرد، اما محتوای SurfaceView شفاف است.

وقتی با یک منبع بافر خارجی، مانند GL context یا یک media decoder، رندر می‌کنید، باید بافرها را از منبع بافر کپی کنید تا بافرها روی صفحه نمایش داده شوند. استفاده از SurfaceView به شما امکان انجام این کار را می‌دهد.

وقتی کامپوننت نمای SurfaceView در شرف قابل مشاهده شدن است، فریم‌ورک از SurfaceControl می‌خواهد که یک سطح جدید از SurfaceFlinger درخواست کند. برای دریافت فراخوانی‌ها هنگام ایجاد یا از بین رفتن سطح، از رابط SurfaceHolder استفاده کنید. به طور پیش‌فرض، فریم‌ورک سطح تازه ایجاد شده را پشت سطح رابط کاربری برنامه قرار می‌دهد. می‌توانید ترتیب Z پیش‌فرض را لغو کنید تا سطح جدید روی آن قرار گیرد.

رندر کردن با SurfaceView در مواردی که نیاز به رندر کردن در یک سطح جداگانه دارید، مانند زمانی که با Camera API یا یک OpenGL ES context رندر می‌کنید، مفید است. وقتی با SurfaceView رندر می‌کنید، SurfaceFlinger مستقیماً بافرها را روی صفحه نمایش می‌دهد. بدون SurfaceView، باید بافرها را روی یک سطح خارج از صفحه نمایش ترکیب کنید، که سپس روی صفحه نمایش ترکیب می‌شود، بنابراین رندر کردن با SurfaceView کار اضافی را از بین می‌برد. پس از رندر کردن با SurfaceView، از نخ UI برای هماهنگی با چرخه حیات فعالیت استفاده کنید و در صورت نیاز، تنظیماتی را در اندازه یا موقعیت نما انجام دهید. سپس، Hardware Composer رابط کاربری برنامه و لایه‌های دیگر را ترکیب می‌کند.

سطح جدید، سمت تولیدکننده‌ی یک BufferQueue است که مصرف‌کننده‌ی آن یک لایه‌ی SurfaceFlinger است. شما می‌توانید سطح را با هر مکانیزمی که بتواند یک BufferQueue را تغذیه کند، به‌روزرسانی کنید، مانند توابع Canvas ارائه شده توسط سطح، اتصال یک EGLSurface و ترسیم روی سطح با GLES، یا پیکربندی یک رمزگشای رسانه برای نوشتن سطح.

SurfaceView و چرخه حیات فعالیت

هنگام استفاده از SurfaceView، سطح را از نخی غیر از نخ اصلی رابط کاربری رندر کنید.

برای یک فعالیت با SurfaceView، دو ماشین حالت جداگانه اما وابسته به هم وجود دارد:

  • برنامه onCreate / onResume / onPause
  • سطح ایجاد شده/تغییر یافته/تخریب شده

وقتی اکتیویتی شروع می‌شود، فراخوانی‌های برگشتی به این ترتیب دریافت می‌شوند:

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

اگر روی دکمه‌ی بازگشت کلیک کنید، موارد زیر را دریافت خواهید کرد:

  1. onPause()
  2. surfaceDestroyed() (درست قبل از اینکه سطح از بین برود، فراخوانی می‌شود)

اگر صفحه را بچرخانید، اکتیویتی از هم جدا شده و دوباره ساخته می‌شود و شما چرخه کامل را خواهید دید. با بررسی isFinishing() می‌توانید متوجه شوید که این یک راه‌اندازی مجدد سریع است. می‌توان یک اکتیویتی را آنقدر سریع شروع/متوقف کرد که surfaceCreated() بعد از onPause() اتفاق بیفتد.

اگر برای خالی کردن صفحه نمایش، دکمه پاور را فشار دهید، فقط onPause() بدون surfaceDestroyed() نمایش داده می‌شود. سطح فعال باقی می‌ماند و رندرینگ می‌تواند ادامه یابد. اگر به درخواست رویدادهای Choreographer ادامه دهید، می‌توانید آنها را دریافت کنید. اگر صفحه قفلی دارید که جهت‌گیری متفاوتی را اعمال می‌کند، ممکن است فعالیت شما پس از خالی شدن صفحه نمایش، مجدداً راه‌اندازی شود. در غیر این صورت، می‌توانید با همان سطح قبلی از حالت خالی بودن صفحه نمایش خارج شوید.

طول عمر نخ می‌تواند به سطح یا به فعالیت گره خورده باشد، بسته به اینکه می‌خواهید هنگام خالی شدن صفحه چه اتفاقی بیفتد. نخ می‌تواند یا در زمان شروع/توقف فعالیت یا در زمان ایجاد/تخریب سطح شروع/متوقف شود.

شروع/توقف نخ روی شروع/توقف فعالیت، به خوبی با چرخه حیات برنامه سازگار است. شما نخ رندر را در onResume() شروع می‌کنید و آن را در onStop() متوقف می‌کنید. هنگام ایجاد و پیکربندی نخ، گاهی اوقات سطح از قبل وجود دارد، گاهی اوقات وجود ندارد (برای مثال، پس از تغییر وضعیت صفحه با دکمه پاور، هنوز فعال است). قبل از مقداردهی اولیه در نخ، باید منتظر بمانید تا سطح ایجاد شود. نمی‌توانید در فراخوانی surfaceCreate() مقداردهی اولیه انجام دهید زیرا اگر سطح دوباره ایجاد نشده باشد، دوباره اجرا نمی‌شود. در عوض، وضعیت سطح را پرس‌وجو یا ذخیره کنید و آن را به نخ رندر ارسال کنید.

شروع/توقف نخ روی ایجاد/نابودی سطح به خوبی کار می‌کند زیرا سطح و رندرکننده منطقاً در هم تنیده شده‌اند. شما نخ را پس از ایجاد سطح شروع می‌کنید، که از برخی نگرانی‌های ارتباطی بین نخ‌ها جلوگیری می‌کند؛ و پیام‌های ایجاد/تغییر سطح ارسال می‌شوند. برای تأیید اینکه رندرینگ با خالی شدن صفحه متوقف می‌شود و با رفع خالی شدن از سر گرفته می‌شود، به Choreographer بگویید که فراخوانی تابع فراخوانی frame draw را متوقف کند. onResume() در صورت اجرای رشته رندرکننده، فراخوانی‌ها را از سر می‌گیرد. با این حال، اگر انیمیشن را بر اساس زمان سپری شده بین فریم‌ها انجام دهید، ممکن است قبل از رسیدن رویداد بعدی فاصله زیادی وجود داشته باشد. استفاده از یک پیام مکث/از سرگیری صریح می‌تواند این مشکل را حل کند.

هر دو گزینه، چه طول عمر نخ به Activity گره خورده باشد و چه به surface، بر نحوه پیکربندی نخ رندرکننده و اینکه آیا در حال اجرا است یا خیر، تمرکز دارند. یک نگرانی مرتبط، استخراج حالت از نخ هنگام از بین رفتن activity (در onStop() یا onSaveInstanceState() ) است. در چنین مواردی، گره زدن طول عمر نخ به activity بهترین عملکرد را دارد زیرا پس از اتصال نخ رندرکننده، می‌توان به حالت نخ رندرشده بدون استفاده از توابع اولیه همگام‌سازی دسترسی پیدا کرد.

جی‌ال‌سرفیس‌ویو

کلاس GLSurfaceView کلاس‌های کمکی برای مدیریت زمینه‌های EGL، ارتباط بین رشته‌ای و تعامل با چرخه حیات فعالیت ارائه می‌دهد. برای استفاده از GLES نیازی به استفاده از GLSurfaceView ندارید.

برای مثال، GLSurfaceView یک thread برای رندر کردن ایجاد می‌کند و یک زمینه EGL را در آنجا پیکربندی می‌کند. وقتی فعالیت متوقف می‌شود، وضعیت به طور خودکار پاک می‌شود. اکثر برنامه‌ها برای استفاده از GLES با GLSurfaceView نیازی به دانستن چیزی در مورد EGL ندارند.

در بیشتر موارد، GLSurfaceView می‌تواند کار با GLES را آسان‌تر کند. در برخی شرایط، می‌تواند مانع شود.