מבוא ל- RxJava

1. סקירה כללית

במאמר זה אנו נתמקד בשימוש בתוספות תגובתיות (Rx) ב- Java כדי להרכיב ולצרוך רצפי נתונים.

במבט חטוף, ה- API אולי נראה דומה ל- Java 8 Streams, אך למעשה הוא הרבה יותר גמיש וזורם, מה שהופך אותו לפרדיגמת תכנות חזקה.

אם אתה רוצה לקרוא עוד על RxJava, עיין בכתיבה זו.

2. התקנה

כדי להשתמש ב- RxJava בפרויקט Maven שלנו, נצטרך להוסיף את התלות הבאה שלנו pom.xml:

 io.reactivex rxjava $ {rx.java.version} 

או, עבור פרויקט Gradle:

הידור 'io.reactivex.rxjava: rxjava: x.y.z'

3. מושגים תגובתי פונקציונליים

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

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

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

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

3.1. מניפסט תגובתי

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

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

4. נצפים

ישנם שני סוגים עיקריים שיש להבין כאשר עובדים איתם Rx:

  • נצפה מייצג כל אובייקט שיכול לקבל נתונים ממקור נתונים ושמצבו עשוי לעניין באופן שאובייקטים אחרים עשויים לרשום עניין
  • An מַשׁקִיף הוא כל אובייקט שרוצה לקבל הודעה כאשר המצב של אובייקט אחר משתנה

An מַשׁקִיף מנוי ל- נצפה סדר פעולות. הרצף שולח פריטים אל ה- מַשׁקִיף אחד בכל פעם.

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

ב Rx, מַשׁקִיף לעולם לא יתקשר עם פריט שאינו בסדר או יתקשר לפני שההתקשרות חוזרת לפריט הקודם.

4.1. סוגים של נצפה

ישנם שני סוגים:

  • ללא חסימה - ביצוע אסינכרוני נתמך ומותר לבטל את הרישום בכל נקודה בזרם האירוע. במאמר זה נתמקד בעיקר בסוג מסוג זה
  • חסימה - את כל בהבא שיחות צופה יהיו סינכרוניות ולא ניתן לבטל את הרישום באמצע זרם האירוע. אנחנו תמיד יכולים להמיר נצפה לתוך חוסם נצפה, בשיטה toBlocking:
BlockingObservable blockingObservable = נצפה.לבלוקינג ();

4.2. מפעילים

An מַפעִיל היא פונקציה שלוקחת אחת אושמיש (המקור) כטיעון הראשון ומחזיר אחר נצפה (היעד). ואז עבור כל פריט שהמקור הנצפה פולט, הוא יחיל פונקציה על פריט זה ואז יפיץ את התוצאה ביעד נצפה.

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

לא קשה להגיע למצב בו נצפה פולט פריטים מהר יותר מ- מַפעִיל אוֹ מַשׁקִיף יכול לצרוך אותם. תוכלו לקרוא עוד על לחץ אחורי כאן.

4.3. צור נצפה

המפעיל הבסיסי רַק מייצר נצפה הפולט מופע כללי יחיד לפני השלמתו, המחרוזת "שלום". כשאנחנו רוצים לקבל מידע מתוך נצפה, אנו מיישמים מַשׁקִיף ממשק ואז התקשר כמנוי על הרצוי נצפה:

נצפה נצפה = Observable.just ("שלום"); לצפייה. מנוי (s -> תוצאה = s); assertTrue (result.equals ("שלום"));

4.4. OnNext, OnError, ו On הושלם

ישנן שלוש שיטות בנושא מַשׁקִיף ממשק שאנחנו רוצים לדעת עליו:

  1. בהבא נקרא על שלנו מַשׁקִיף בכל פעם שמתפרסם אירוע חדש המצורף נצפה. זו השיטה שבה נבצע פעולה בכל אירוע
  2. On הושלם נקרא כאשר רצף האירועים המשויך ל- נצפה הוא שלם, מה שמעיד שלא עלינו לצפות ליותר בהבא קורא לצופה שלנו
  3. OnError נקרא כאשר מושלך חריג שלא מטופל במהלך RxJava קוד מסגרת או קוד הטיפול באירועים שלנו

ערך ההחזר עבור נצפיםהירשם כמנוי השיטה היא א הירשם כמנוי מִמְשָׁק:

מחרוזת [] אותיות = {"a", "b", "c", "d", "e", "f", "g"}; נצפה נצפה = Observable.from (אותיות); observerable.subscribe (i -> result + = i, // OnNext Throwable :: printStackTrace, // OnError () -> result + = "_Completed" // OnCompleted); assertTrue (result.equals ("abcdefg_Completed"));

5. טרנספורמציות נצפות ומפעילים מותנים

5.1. מַפָּה

הממפעיל ap הופך פריטים הנפלטים על ידי נצפה על ידי יישום פונקציה לכל פריט.

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

Observable.from (אותיות) .map (String :: toUpperCase). Subscribe (letter -> result + = letter); assertTrue (result.equals ("ABCDEFG"));

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

פרטים נוספים על ההבדל בין מַפָּה ו flatMap ניתן למצוא כאן.

בהנחה שיש לנו שיטה שמחזירה an נצפה מרשימת מיתרים. כעת נדפיס עבור כל מחרוזת מחדש נצפה את רשימת הכותרות על סמך מה מָנוּי רואה:

GetTitle נצפה () {return Observable.from (titleList); } Observable.just ("book1", "book2") .flatMap (s -> getTitle ()). מנוי (l -> תוצאה + = l); assertTrue (result.equals ("כותרת כותרת"));

5.2. לִסְרוֹק

ה מפעיל סריקה אממלא פונקציה לכל פריט שנפלט על ידי נצפה ברצף ופולט כל ערך עוקב.

זה מאפשר לנו להמשיך את המצב מאירוע לאירוע:

מחרוזת [] אותיות = {"a", "b", "c"}; Observable.from (אותיות) .scan (StringBuilder חדש (), StringBuilder :: להוסיף). מנוי (סה"כ -> תוצאה + = total.toString ()); assertTrue (result.equals ("aababc"));

5.3. GroupBy

קבוצה לפי המפעיל מאפשר לנו לסווג את האירועים בקלט נצפה לקטגוריות תפוקה.

בואו נניח שיצרנו מערך של מספרים שלמים בין 0 ל -10 ואז להחיל קבוצה לפי שיחלק אותם לקטגוריות אֲפִילוּ ו מוזר:

Observable.from (numbers) .groupBy (i -> 0 == (i% 2)? "EVEN": "ODD"). Subscribe (group -> group.subscribe ((number) -> {if (group.getKey () .toString (). שווה ("EVEN")) {EVEN [0] + = number;} אחר {ODD [0] + = number;}})); assertTrue (EVEN [0] .equals ("0246810")); assertTrue (ODD [0] .equals ("13579"));

5.4. לְסַנֵן

המפעיל לְסַנֵן פולט רק את הפריטים האלה מתוך נצפה שעוברים א לְבַסֵס מִבְחָן.

אז בואו נסנן במערך שלם את המספרים האי-זוגיים:

Observable.from (numbers) .filter (i -> (i% 2 == 1)). מנוי (i -> result + = i); assertTrue (result.equals ("13579"));

5.5. מפעילים מותנים

DefaultIfEmpty פולט פריט מהמקור נצפה, או פריט ברירת מחדל אם המקור נצפה זה ריק:

Observable.empty () .defaultIfEmpty ("נצפה ריק"). מנוי (s -> תוצאה + = s); assertTrue (result.equals ("נצפה ריק"));

הקוד הבא פולט את האות הראשונה של האלף-בית 'א' כי המערך אותיות אינו ריק וזה מה שהוא מכיל בעמדה הראשונה:

Observable.from (אותיות). DefaultIfEmpty ("נצפה ריק"). First (). Subscribe (s -> result + = s); assertTrue (result.equals ("a"));

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

Observable.from (numbers) .takeWhile (i -> i sum [0] + = s); assertTrue (סכום [0] == 10);

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

6. תצפיות הניתנות לחיבור

א ניתן לחיבור ניתן לצפייה דומה לרגיל נצפהאלא שהיא לא מתחילה להוציא פריטים כשהם מנויים, אלא רק כאשר ה- לְחַבֵּר המפעיל מוחל עליו.

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

מחרוזת [] תוצאה = {""}; ConnectableObservable connectable = Observable.interval (200, TimeUnit.MILLISECONDS) .publish (); connectable.subscribe (i -> תוצאה [0] + = i); assertFalse (תוצאה [0]. שווה ("01")); connectable.connect (); Thread.sleep (500); assertTrue (תוצאה [0]. שווה ("01"));

7. רווק

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

בעזרת מקור נתונים זה אנו יכולים להשתמש בשתי שיטות בלבד כדי להירשם כמנוי:

  • OnSuccess מחזירה א יחיד המכנה גם שיטה שאנו מציינים
  • OnError מחזיר גם א יחיד שמיידע את המנויים באופן מיידי על שגיאה
מחרוזת [] תוצאה = {""}; סינגל יחיד = Observable.just ("שלום") .toSingle () .doOnSuccess (i -> תוצאה [0] + = i) .doOnError (שגיאה -> {זרוק RuntimeException חדש (error.getMessage ());}); single.subscribe (); assertTrue (תוצאה [0] .equals ("שלום"));

8. נושאים

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

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

בדוגמה הבאה נבדוק כיצד הצופים יוכלו לראות את האירועים המתרחשים לאחר הרשמתם:

מנוי שלם 1 = 0; מנוי שלם 2 = 0; Observer getFirstObserver () {להחזיר Observer new () {@Override public void onNext (Integer value) {subscriber1 + = value; } @ עקירה בטלנית ציבורית על שגיאה (הניתן לזריקה) {System.out.println ("שגיאה"); } @ ביטול ציבורי בטל ב- OnCompleted () {System.out.println ("המנוי 1 הושלם"); }}; } Observer getSecondObserver () {להחזיר Observer חדש () {@Override public void onNext (Integer value) {subscriber2 + = value; } @ עקירה בטלנית ציבורית על שגיאה (הניתן לזריקה) {System.out.println ("שגיאה"); } @ ביטול הריק הציבורי onCompleted () {System.out.println ("המנוי 2 הושלם"); }}; } נושא PublishSubject = PublishSubject.create (); subject.subscribe (getFirstObserver ()); subject.onNext (1); subject.onNext (2); subject.onNext (3); subject.subscribe (getSecondObserver ()); subject.onNext (4); subject.onCompleted (); assertTrue (מנוי 1 + מנוי 2 == 14)

9. ניהול משאבים

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

כאן אנו מציגים בפרשנויות את הצעדים שעלינו לעשות כדי להשיג מטרה זו וגם דוגמה ליישום:

מחרוזת [] תוצאה = {""}; ערכים נצפים = Observable.using (() -> "MyResource", r -> {return Observable.create (o -> {for (Character c: r.toCharArray ()) {o.onNext (c);} o. onCompleted ();});}, r -> System.out.println ("מסולק:" + r)); ערכים.מנוי (v -> תוצאה [0] + = v, e -> תוצאה [0] + = e); assertTrue (תוצאה [0] .equals ("MyResource"));

10. מסקנה

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

קוד המקור המלא של הפרויקט כולל כל דגימות הקוד המשמשות כאן ניתן למצוא באתר Github.


$config[zx-auto] not found$config[zx-overlay] not found