עיצוב שמאפשר מעבר חלק

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

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

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

דוגמה נוספת לבעיה שקשורה לחוסר רציפות היא מצב שבו פעילות מאבדת בטעות את הסטטוס או את נתוני המשתמשים שלה כי לא מיושמת בה בצורה נכונה הפונקציה onPause()‎ ופונקציות אחרות של מחזור החיים. לחלופין, אם האפליקציה חושפת נתונים שמיועדים לשימוש באפליקציות אחרות, צריך לחשוף אותם באמצעות ContentProvider, ולא (לדוגמה) באמצעות קובץ או מסד נתונים גולמיים שניתן לקרוא אותם בכל מקום.

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

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

לא לאבד נתונים

חשוב לזכור תמיד ש-Android היא פלטפורמה לניידים. יכול להיות שזה ברור מאליו, אבל חשוב לזכור שפעילות אחרת (כמו האפליקציה 'שיחה נכנסת') יכולה להופיע מעל הפעילות שלכם בכל רגע. הפעולה הזו תפעיל את השיטות onSaveInstanceState()‎ ו-onPause()‎, וסביר להניח שהיא תגרום להפסקת הפעולה של האפליקציה.

אם המשתמש ערך נתונים באפליקציה שלכם כשהפעילות השנייה הופיעה, סביר להניח שהאפליקציה שלכם תאבד את הנתונים האלה כשהיא תופסק. אלא אם שומרים את העבודה קודם. הדרך המומלצת ב-Android היא לעשות בדיוק את זה: אפליקציות ל-Android שמקבלות או עורכות קלט צריכות לבטל את ה-method‏ onSaveInstanceState() ולשמור את המצב שלהן בצורה מתאימה. כשהמשתמשת תחזור לאפליקציה, היא תוכל לאחזר את הנתונים שלה.

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

לא לחשוף נתונים גולמיים

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

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

לא להפריע למשתמש

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

כלומר, אל תקראו לפונקציה startActivity()‎ מ-BroadcastReceivers או מ-Services שפועלים ברקע. הפעולה הזו תפריע לאפליקציה שפועלת כרגע, ותגרום למשתמש להיות מתוסכל. יכול להיות שגרוע מכך, הפעילות שלכם תהפוך ל'גנב הקשות' ותקבל חלק מהקלט שהמשתמש היה באמצע ההזנה שלו לפעילות הקודמת. בהתאם לפעולות שהאפליקציה מבצעת, יכול להיות שמדובר בבעיה.

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

(שימו לב: כל זה לא רלוונטי למקרים שבהם הפעילות שלכם כבר ברקע. במקרה כזה, המשתמש מצפה לראות את הפעילות הבאה שלכם בתגובה לקלט).

יש לכם הרבה דברים לעשות? איך עושים את זה בשרשור

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

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

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

לא להעמיס על מסך פעילות יחיד

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

בהתאם לרקע שלכם בפיתוח, יכול להיות שתפרשו Activity כמשהו שדומה ל-Java Applet, כי הוא נקודת הכניסה לאפליקציה. עם זאת, ההגדרה הזו לא מדויקת לגמרי: אם מחלקת משנה של Applet היא נקודת הכניסה היחידה לאפלט Java, אז Activity היא אחת מנקודות הכניסה האפשריות לאפליקציה. ההבדל היחיד בין הפעילות 'הראשית' לבין כל פעילות אחרת שיש לכם הוא שהפעילות 'הראשית' היא הפעילות היחידה שהביעה עניין בפעולה android.intent.action.MAIN בקובץ AndroidManifest.xml.

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

הרחבת ערכות הצבעים של המערכת

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

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

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

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

הנחה שהרשת איטית

מכשירי Android מגיעים עם מגוון אפשרויות לקישור לרשת. לכולם תהיה הקצאת גישה לנתונים, אבל חלקם יהיו מהירים יותר מאחרים. אבל המכנה המשותף הנמוך ביותר הוא GPRS, שירות הנתונים שאינו 3G לרשתות GSM. גם מכשירים עם יכולת 3G יבלו הרבה זמן ברשתות שאינן 3G, ולכן רשתות איטיות יישארו מציאות די הרבה זמן.

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

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

לא להניח שמדובר במסך מגע או במקלדת

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

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

חשוב לחסוך בצריכת הסוללה של המכשיר

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

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

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