SurfaceView و GLSurfaceView

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

SurfaceView

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

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

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

رندر با SurfaceView در مواردی که نیاز به رندر گرفتن در یک سطح جداگانه دارید، مفید است، مانند زمانی که با دوربین API یا زمینه OpenGL ES رندر می‌کنید. وقتی با 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() دریافت خواهید کرد. سطح فعال باقی می ماند و رندر می تواند ادامه یابد. اگر همچنان به درخواست آنها ادامه دهید، می توانید به دریافت رویدادهای رقصنده ادامه دهید. اگر صفحه قفلی دارید که جهت گیری متفاوتی را مجبور می کند، وقتی دستگاه خالی است، ممکن است فعالیت شما مجدداً راه اندازی شود. در غیر این صورت، می توانید با همان سطح قبلی از حالت خالی خارج شوید.

طول عمر نخ را می توان به سطح یا فعالیت مرتبط کرد، بسته به اینکه می خواهید در هنگام خالی شدن صفحه چه اتفاقی بیفتد. نخ می تواند شروع/توقف کند یا در Activity start/stop یا روی surface create/destroy.

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

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

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

GLSurfaceView

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

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

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