SurfaceView ו-GLSurfaceView

ממשק המשתמש של מסגרת אפליקציית Android מבוסס על היררכיה של אובייקטים שמתחילים ב- View . כל רכיבי ממשק המשתמש עוברים סדרה של מדידות ותהליך פריסה שמתאים אותם לאזור מלבני. לאחר מכן, כל אובייקטי התצוגה הגלויים מוצגים למשטח שהוגדר על ידי WindowManager כאשר האפליקציה הובאה לקדמת הבמה. חוט ממשק המשתמש של האפליקציה מבצע פריסה ורינדור למאגר לכל מסגרת.

SurfaceView

SurfaceView הוא רכיב שאתה יכול להשתמש בו כדי להטמיע שכבה מורכבת נוספת בהיררכיית התצוגה שלך. SurfaceView לוקח את אותם פרמטרי פריסה כמו תצוגות אחרות, כך שניתן לתפעל אותו כמו כל תצוגה אחרת, אבל התוכן של SurfaceView שקוף.

כאשר אתה מעבד עם מקור מאגר חיצוני, כגון הקשר GL או מפענח מדיה, עליך להעתיק מאגרים ממקור המאגר כדי להציג את המאגרים על המסך. שימוש ב-SurfaceView מאפשר לך לעשות זאת.

כאשר רכיב התצוגה של SurfaceView עומד להיות גלוי, המסגרת מבקשת מ- SurfaceControl לבקש משטח חדש מ- SurfaceFlinger. כדי לקבל התקשרות חוזרת כאשר המשטח נוצר או נהרס, השתמש בממשק SurfaceHolder . כברירת מחדל, המשטח החדש שנוצר ממוקם מאחורי משטח ממשק המשתמש של האפליקציה. אתה יכול לעקוף את ברירת המחדל של סדר ה-Z כדי לשים את המשטח החדש למעלה.

רינדור עם SurfaceView מועיל במקרים שבהם עליך לבצע רינדור למשטח נפרד, כגון כאשר אתה מעבד עם ה-API של המצלמה או בהקשר של OpenGL ES. כשאתה מעבד עם SurfaceView, SurfaceFlinger מרכיב ישירות מאגרים למסך. ללא SurfaceView, אתה צריך להרכיב חוצצים למשטח מחוץ למסך, אשר לאחר מכן מתחברים למסך, כך שעיבוד עם SurfaceView מבטל עבודה נוספת. לאחר רינדור עם SurfaceView, השתמש בשרשור ממשק המשתמש כדי לתאם עם מחזור חיי הפעילות ולבצע התאמות לגודל או למיקום התצוגה במידת הצורך. לאחר מכן, ה- 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() . המשטח נשאר פעיל, והעיבוד יכול להמשיך. אתה יכול להמשיך לקבל אירועי כוריאוגרפים אם תמשיך לבקש אותם. אם יש לך מסך נעילה שמכריח כיוון שונה, ייתכן שהפעילות שלך תופעל מחדש כשהמכשיר אינו מרוקן. אחרת, אתה יכול לצאת מהמסך ריק עם אותו משטח כמו קודם.

ניתן לקשור את אורך החיים של החוט למשטח או לפעילות, תלוי מה אתה רוצה שיקרה כשהמסך ריק. השרשור יכול להתחיל/להפסיק בהפעלה/עצירה של פעילות או ביצירה/השמדה של פני השטח.

התחלת/הפסקת השרשור על התחלה/עצירה של פעילות עובד היטב עם מחזור החיים של האפליקציה. אתה מתחיל את שרשור המעבד ב- onResume() ועוצר אותו ב- onStop() . בעת יצירה והגדרה של השרשור, לפעמים המשטח כבר קיים, לפעמים לא (לדוגמה, הוא עדיין פעיל לאחר החלפת המסך עם כפתור ההפעלה). אתה צריך לחכות ליצירת המשטח לפני שאתחול בשרשור. אתה לא יכול לאתחל ב- surfaceCreate() callback מכיוון שהוא לא יופעל שוב אם המשטח לא נוצר מחדש. במקום זאת, בצע שאילתה או שמור במטמון את מצב השטח, והעבר אותו לשרשור המעבד.

האפשרות להתחיל/להפסיק את השרשור על יצירת/להרוס את השרשור עובדת היטב מכיוון שהמשטח והמרנדר שלובים זה בזה באופן הגיוני. אתה מתחיל את השרשור לאחר יצירת המשטח, מה שמונע כמה דאגות תקשורת בין חוטים; והודעות שנוצרו/שונו על פני השטח פשוט מועברות. כדי להבטיח שהרינדור ייפסק כשהמסך ריק ויתחדש כאשר הוא מתבטל, אמור לכוריאוגרף להפסיק להפעיל את ה-frame draw callback. onResume() ממשיך את ההתקשרות לאחור אם שרשור המעבד פועל. עם זאת, אם אתה עושה אנימציה על סמך זמן שחלף בין פריימים, יכול להיות פער גדול לפני שהאירוע הבא יגיע; שימוש בהודעת הפסקה/המשך מפורשת יכול לפתור בעיה זו.

שתי האפשרויות, בין אם תוחלת החיים של השרשור קשורה ל-Activity או למשטח, מתמקדות באופן שבו שרשור המעבד מוגדר והאם הוא מבוצע. דאגה קשורה היא חילוץ מצב מהשרשור כאשר הפעילות נהרגה (ב- onStop() או onSaveInstanceState() ); במקרים כאלה, קשירת תוחלת החיים של השרשור לפעילות עובדת בצורה הטובה ביותר מכיוון שאחרי חיבור השרשור המעובד, ניתן לגשת למצב השרשור המעובד ללא פרימיטיביות סנכרון.

GLSurfaceView

כיתת GLSurfaceView מספקת שיעורי מסייעים לניהול הקשרי EGL, תקשורת בין חוטים ואינטראקציה עם מחזור חיי הפעילות. אינך צריך להשתמש ב-GLSurfaceView כדי להשתמש ב-GLES.

לדוגמה, GLSurfaceView יוצר שרשור לעיבוד ומגדיר שם הקשר EGL. המצב מתנקה אוטומטית כאשר הפעילות מושהית. רוב האפליקציות לא צריכות לדעת שום דבר על EGL כדי להשתמש ב-GLES עם GLSurfaceView.

ברוב המקרים, GLSurfaceView יכול להקל על העבודה עם GLES. במצבים מסוימים, זה יכול להפריע.