PracticU

The Listener Pattern

בלי כתוביותעם כתוביות
PracticU › קורס פיתוח אפליקציות - לאנדרואיד - Android - ב-JAVA › The Listener Pattern
מילון מושגים:
XML - שפת סימון לייצוג נתונים מובְנים בעזרת תגיות, המשמשת להחלפת מידע בין מערכות ולקובצי הגדרות.
Java - שפת תכנות עילית ומונחית עצמים, הרצה על מכונה וירטואלית (JVM) ומשמשת לפיתוח מגוון יישומים.
ArrayList - מבנה נתונים של רשימה דינמית שגודלה משתנה, הנפוץ בשפות כמו Java ו-C#.
Integer - סוג נתונים למספר שלם, כלומר מספר ללא חלק עשרוני.
תהליכון - יחידת ביצוע בתוך תהליך, המאפשרת להריץ כמה מהלכים במקביל ולחלוק את אותו זיכרון.
Activity - ב-Android, רכיב שמייצג מסך יחיד של האפליקציה שאיתו המשתמש מתקשר.
תגית - סימון בקוד, למשל ב-HTML, שתוחם רכיב ומגדיר את תפקידו או תכונותיו. גם תווית לסימון וקיטלוג פריטים.
Callback - פונקציה שמועברת כארגומנט לפונקציה אחרת כדי שתופעל מאוחר יותר, לרוב בסיום פעולה.

The Listener Pattern
אירוע הוא דיווח שמחלקה שולחת לעולם החיצון כשמשהו קרה בתוכה, כמו לחיצה על לחצן. ההרצאה בונה מחלקה משלה שמדווחת על אירועים באמצעות ה-Listener Design Pattern, ואז מראה שלוש דרכים להאזין לדיווחים האלה.
למה לא מספיק onClick ב-XML
1. רק onClick נתמך ב-XML: לאירועים אחרים, כמו לחיצה ארוכה (long click) או מקשים שנלחצו ב-EditText, אי אפשר להירשם דרך ה-XML.
2. רכיבים שנוצרים בקוד: לחצן שנוצר ב-Java לא מופיע בכלל בקובץ ה-XML, ולכן צריך להירשם אליו בקוד.
הדוגמה: Primes Manager
המחלקה PrimesManager מקבלת גבול עליון ומחזירה ArrayList<Integer> של כל המספרים הראשוניים עד אליו. הבדיקה isPrime מחזירה false למספר שאינו גדול מ-1, ובודקת אם מספר כלשהו מ-2 ועד המספר מחלק אותו. כדי לדמות פעולה ארוכה מוסיפים Thread.sleep של 100 אלפיות שנייה, בתוך try/catch. הלחצן Find Primes מציג את התוצאה ב-TextView, ולחיצה על ה-activity מנקה אותו. הלחצן נתקע כמה שניות והתוצאה מופיעה רק בסוף, אבל אנחנו רוצים לקבל דיווח בכל פעם שנמצא מספר ראשוני.
הבעיה והפתרון
המחלקה PrimesManager נכתבה לפני MainActivity, ולכן אינה מכירה אותה ואינה יכולה לקרוא לפונקציה שלה. כאן נכנס ה-Listener Pattern: מקבלים אובייקט שמממש ממשק מוסכם, וקוראים לפונקציה שבו בלי להכיר את המחלקה שלו.
השלבים העיקריים
1. מגדירים interface: בשם OnFoundPrimeListener, עם פונקציה אחת onFoundPrime(int prime). שם הממשק מתחיל ב-On ומסתיים ב-Listener.
2. משתנה במחלקה: ב-PrimesManager משתנה פרטי מסוג הממשק, listener.
3. פונקציית רישום: setOnFoundPrimeListener(OnFoundPrimeListener listener) שומרת את האובייקט. set שומר אובייקט אחד, ו-add מוסיף לאוסף של מאזינים, כשבכל מקרה כל המאזינים מקבלים דיווח.
4. קריאה בלולאה: כשנמצא מספר ראשוני, אחרי שמוסיפים אותו לאוסף, קוראים ל-listener.onFoundPrime(i), אבל רק אם listener != null. כך המחלקה לא תלויה בכך שמישהו נרשם.
5. נרשמים מה-MainActivity: קוראים ל-setOnFoundPrimeListener לפני הפעלת הפעולה, ומעבירים אובייקט שמממש את הממשק.
שלוש הדרכים לממש את המאזין
1. מחלקה חיצונית: PrimesLogger מממשת את הממשק (Alt+Enter ו-Implement methods) וכותבת ל-LogCat עם Log.i ועם תג PrimesTester. מייצרים ממנה אובייקט ושולחים אותו. הדרך הכי פחות נפוצה, מתאימה אם הקוד רב.
2. מחלקה אנונימית: new PrimesManager.OnFoundPrimeListener() { ... } בתוך הקריאה ל-setOnFoundPrimeListener. Java יוצרת מאחורי הקלעים מחלקה ואובייקט, והקוד כתוב במקום. נפוצה כשהקוד קצר, ויש בה גישה ל-activity עצמו.
3. ה-activity מממש את הממשק: שולחים this. מציעים ב-Alt+Enter את Make MainActivity implement, והפונקציה onFoundPrime נכתבת ב-activity. מתאימה כשהקוד מתארך ורוצים להוציאו מהלחצן.
הערות
- בדרך כלל מגדירים את הממשק בתוך המחלקה שמדווחת, ולכן מחוץ אליה כותבים PrimesManager.OnFoundPrimeListener.
- פונקציה שמקבלת דיווחים כזו נקראת callback function.
- המחלקה המדווחת לא מכירה את המאזין, ולכן היא יציבה וניתנת לשימוש חוזר בפרויקטים אחרים.
- הצגת המספרים תוך כדי הפעולה על המסך תלויה ב-threads, נושא שנראה בהמשך.
אחרי ההרצאה אפשר לבנות מחלקה שמדווחת על אירועים, ולהאזין לה בשלוש דרכים לפי גודל הקוד.
השיעור הבא בקורס: Dynamic Registration

פרקי ההרצאה: The Listener Pattern

  1. 00:00

    מהו אירוע ורישום אליו

  2. 02:21

    אירועים שאי אפשר לרשום ב-XML

  3. 04:07

    מטרת ה-Listener Pattern

  4. 05:21

    בניית המחלקה PrimesManager

  5. 06:59

    פונקציה לבדיקת מספר ראשוני

  6. 08:54

    הדמיית חישוב ארוך עם Thread.sleep

  7. 10:27

    הצגת המספרים הראשוניים במסך

  8. 14:08

    דיווח מתוך המחלקה בזמן החישוב

תמלול ההרצאה: The Listener Pattern

הטקסט המלא של הדקות הראשונות בהרצאה «The Listener Pattern» בקורס הווידאו של PracticU – כתוביות בעברית עם זמן התחלה לכל פסקה. כך תוכלו לקרוא ולחפש את הנושאים לפני שמתחילים ללמוד.

  1. 00:02

    אנחנו מגיעים לסדרת סרטונים בהם אנחנו הולכים לדבר על אירועים ולראות כל מיני דברים בנוגע לאירועים שעדיין לא ראינו.

  2. 00:12

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

  3. 00:23

    לדוגמה, אם יש לנו לחצן, לחצן זה אובייקט ממחלקה Button, והלחצן יודע שהיוזר לחץ עליו קליק, ואז אנחנו יכולים להירשם לאירוע קליק, איך תמיד היינו עושים את זה?

  4. 00:37

    היינו הולכים כאן לקובץ ה-XML, היינו רושמים onclick=do something, ובשם של פונקציה ניגשים אלט אנטר, אוד אנטר, מגיעים לקוד הג'אווה וכותבים כאן קוד.

  5. 00:53

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

  6. 01:25

    הקריאה לפונקציה ומבצעים את הקוד הדרוש זאת אומרת הלחצן קורא לפונקציה הזאת כאשר לוחצים עליו קליק זה כל הסיפור בנוגע לאירוע משהו קרה במחלקה במקרה הזה user לחץ קליק על אובייקט ואז המחלקה רוצה להודיע לנו את זה.

  7. 01:46

    המחלקה לא מכירה אותנו, היא לא יודעת מי זה הקוד שלנו, אנחנו היינו צריכים לבוא ולומר למחלקה, הנה יש לנו פונקציה, הנה הפונקציה היא נקראת do something, בבקשה תקראי לפונקציה הזאת, כאשר היוזר ילחץ קליק.

  8. 02:00

    אז מה בדיוק כל הבעיה בסיפור הזה? אז למעשה עד עכשיו לא ראינו שום בעיה, כאשר רצינו לכתוב איזשהו קוד שיתבצע כתגובה לאירוע קליק, אז זה מה שעשינו.

  9. 02:13

    אבל הנה כמה סיבות שהדבר הזה לא יספיק מקרים מסוימים דבר ראשון רק עבור אירועי און קליק אפשר להירשם דרך הxml יש עוד המון אירועים אחרים לדוגמה long click לחיצה ארוכה על ידי הלחצן שהוא לוחץ על הלחצן או על רכיב אחר ולא עוזב את הלחצן או את הרכיב ורק אחרי הרבה זמן עוזב ואז האירוע הזה

  10. 02:38

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

  11. 02:53

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

  12. 03:02

    רק אירוע on-click ניתן להירשם אליו דרך קוד ה-XML. זאת אומרת שאנחנו צריכים כן להירשם לכל האירועים האלה בדרך אחרת, והדרך הזאת היא דרך קוד ה-Java.

  13. 03:13

    קוד ה-Java מאפשר לנו להירשם לכל האירועים, לקלוט אותם כשהם מתרחשים ולכתוב קוד שיתבצע כתגובה לאירועים האלה. אז קודם כל זאת סיבה ראשונה למה רישום דרך קובץ ה-XML הוא לפעמים בלתי אפשרי בכלל.

  14. 03:29

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

  15. 03:42

    אם אנחנו יוצרים לחצן חדש כמובן שאנחנו רוצים להירשם לאירועים שלו ואז אנחנו נוכל לעשות את זה רק בקוד הג'אווה. אז יש כל מיני סיבות שבגללם אנחנו לא יכולים לבצע את הרישום הזה בצורה כזאת.

  16. 03:58

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

  17. 04:15

    אנחנו לא יכולים להסתכל ולכתוב את לחצן הבאטן מפני שהלחצן הזה הוא מוכן קיים אנחנו לא מתעסקים איתו.

  18. 04:23

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

  19. 04:51

    למה אנחנו מחוץ למחלקה צריכים להירשם איך שאנחנו נרשמים וכך אנחנו נוכל בעצם להירשם לכל אירוע עתידי דרך קוד הג'אווה ועם הבנה מלאה מה בעצם קורה כאן. ויש המון מקרים כאלה אנחנו צריכים לעשות את זה המון פעמים בכל אפליקציה אנחנו פשוט עד היום לא נתקלנו במקרים כאלה.

  20. 05:15

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

  21. 05:36

    primes manager זאת מחלקה שהמטרה שלה זה לייצר לנו מספרים ראשוניים. אנחנו רוצים פונקציה, אנחנו רוצים לתת לפונקציה איזשהו לימיט כזה עליון, ואנחנו רוצים שהפונקציה תחזיר לנו את כל המספרים הראשונים שקיימים עד לאותו הלימיט.

  22. 05:53

    ותוך כדי הבנייה נבין למה בעצם אנחנו צריכים להתעסק פה עם הקונספט של אירועים. אז בואו נבנה את הפונקציה הזאת, נרשום public arraylist integer getprimes מקבלת איזשהו int limit כלשהו.

  23. 06:17

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

  24. 06:47

    שהוא ראשוני הוא 2 אז אנחנו כבר יכולים לרוץ מ-2 ופה אנחנו רוצים לבצע איזושהי בדיקה האם ה-i הזה שאנחנו סורקים אותו כרגע הוא ראשוני כן או לא.

  25. 06:58

    אם כן אנחנו רוצים לצרף אותו לפריימס. בשביל לדעת האם הוא ראשוני כן או לא בואו נבנה לנו איזושהי פונקציית עזר פרייבט כזאת בוליאן is prime.

  26. 07:09

    הפונקציה תקבל איזשהו number אינטג'ר כמובן ותבדוק האם הוא ראשוני. איך אנחנו בודקים האם מספר הוא ראשוני כן או לא? קודם כל אם הnumber יותר קטן או שווה ל-1 אז המספר הוא לא ראשוני נבנה אותה עבור המקרה הכללי.

  27. 07:35

    לאחר מכן אנחנו רוצים לרוץ עד ה-num לא כולל ה-num כמובן מ-2 ולבדוק האם יש איזשהו מספר שמחלק אותו. אם נע"ם מודול הוא i הוא 0 אז אנחנו רוצים גם כאן להחזיר false מפני שהוא לא יהיה ראשוני כי יש מישהו שמחלק אותו בלי שארית.

  28. 07:54

    הגענו לסוף, return true כי בוודאות המספר הזה הוא מספר ראשוני וכאן אנחנו יכולים לבצע איזושהי בדיקה ולראות האם ה-i שאנחנו סורקים כרגע הוא ראשוני כן או לא.

  29. 08:08

    אם כן בואו נצרף אותו לקולקשן הזה שיש לנו. בסוף הלולאה אנחנו יכולים להחזיר את פריימס. השתכפל לי פה שורה והנה יש לנו פה את הקוד המלא של המחלקה שפשוט מכילה פונקציה היא מקבלת איזשהו מספר נניח היא קיבלה 100 אז היא תחזיר את כל המספרים הראשוניים שיש עד 100 מ-2 כמובן ואנחנו נוכל להציג את המספרים האלה בלי בעיה אבל אני רוצה להוסיף עוד משהו במחלקה הזאת בכדי להבהיר

  30. 08:50

    את הנקודה של למה אנחנו צריכים קודם כל כאשר אנחנו רוצים לחפש מספרים ראשונים שהם יחסית נמוכים כמו 100 כמו 1000 אולי כמו 100,000 אז זה יהיה די מהר עבור המחשב לראות האם מספר הוא מספר ראשוני כאשר הוא לא כל כך גבוה.

  31. 09:12

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

  32. 09:36

    אבל אנחנו כן רוצים לבצע איזושהי הדמיה כאילו שכל הסיפור הזה אכן לוקח הרבה זמן כי במציאות אם נותנים לי פה מספר גבוה זה אכן ייקח הרבה זמן אז איך אנחנו נבצע הדמיה שכל הדבר הזה לוקח הרבה זמן פשוט פקודת delay יש לנו פקודה שנקראת thread.slip בואו ניתן לה רק 100 מילי שניות והפקודה הזאת היא בעצם מבצעת delay של 100

  33. 10:05

    מילי סקנדס הפקודה הזאת דורשת רק שנעטוף אותה באיזשהו try-catch כללי כזה בואו ניקח פה איזשהו exception כללי כזה כי הפקודה רק דורשת מאיתנו את זה ומה שאנחנו רוצים לעשות זה לבדוק את המחלקה הזאת אוקיי אז אנחנו רוצים שיהיה לנו פה איזשהו לחצן שפשוט מפעיל אותה.

  34. 10:36

    הנה לחצן, בואו נקרא לו find primes. ניקח את הפונקציה find primes. כאן אנחנו רוצים למצוא את כל המספרים הראשוניים ולהציג אותם.

  35. 10:54

    איך אנחנו מוצאים את המספרים האלה? בואו ניקח primes manager, צור אובייקט מטיפוס primes manager, ניקח איזשהו arraylist integer, ונקרא לפריימס מנאג'ר, get primes, נגיד לו הנה יש לנו פה מספר 50 ותביא לי את כל המספרים הראשונים עד 50. כמובן שאנחנו עושים הדמיה שזה ייקח טיפה

  36. 11:20

    זמן, למעשה זה אמור לקחת פחות או יותר 5 שניות. אוקיי, עכשיו אנחנו רוצים להציג את המספרים האלה ולכן בואו ניגש ונבנה כאן איזשהו טקסט view, ניתן לו id text view primes, נגדיר לו טקסט סייז, נגדיר לו גראביטי כדי שהוא יהיה כזה יפה באמצע אנחנו רוצים לקחת

  37. 11:57

    ולאתר את הרכיב הזה טקסט view טקסט view primes הנה הוא פיינד view iid טקסט view primes יש לנו אותו ועכשיו אנחנו יכולים פשוט לרוץ עליו בלולאה למעשה לרוץ על כל המספרים הראשונים primes.size וכל אחד מהם לגשת לטקסט view primes ופשוט לבצע איזשהו append ולהוסיף אליו את כל המספרים

  38. 12:37

    אנחנו רוצים להוסיף אליו את primes get תביא לי בבקשה את הפריט ה-i תעשה פה איזשהו שרשור של רווח אולי לפני שנעשה את זה ניגש ונבצע סט טקסט ונרוקן אותו מתוכן קודם ואפילו בואו נעשה עוד איזשהו שירות ניגש לאקטיביטי ובאון קליק על האקטיביטי נבצע קליר דיספליי.

  39. 13:06

    זאת אומרת אם היוזר גם לוחץ קליק על האקטיביטי אז אנחנו רוצים לאפס ולרוקן את הטקסט view primes הזה. סט טקסט אז בעצם כשלוחצים על הלחצן find primes אנחנו ניגשים למחלקה יוצרים ממנה אובייקט קוראים לפונקציה get primes אומרים לה הנה מספר תביא

  40. 13:30

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

  41. 13:52

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

  42. 14:09

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

  43. 14:22

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

  44. 14:42

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

לקורס המלא: קורס פיתוח אפליקציות לאנדרואיד

ההרצאה הקודמת: Application Context

ההרצאה הבאה: Dynamic Registration

הרצאות קשורות מקורסים אחרים

  • Pattern - Request-Reply
  • Angular4 - Validator Pattern
  • פייטון design pattern factory

קורסים נוספים במסלול

  • קורס יסודות התכנות - שפת JAVA - קורס ג'אווה למתחילים
  • קורס פיתוח אפליקציות - לאייפון iPhone iOS - ב-Swift-Xcode
  • קורס פלאטר - Flutter - שפת DART של גוגל למובייל - פיתוח אפליקציות לאנדרואיד ואייפון
  • קורס PhoneGap - פיתוח אפליקציות לאנדרואיד ואייפון