אופטימיזציה של בקשות רשת: כיצד להפחית את זמן טעינת המסך?
המסך הראשי של אפליקציה מבצע 14 בקשות מקבילות בעת פתיחתו. נראה מהיר—מקביל אומר מהיר, נכון? אבל HTTP/1.1 מגביל ל-6 חיבורים לכל מארח, כך ש-8 בקשות ממתינות בתור. בחיבור LTE חלש עם RTT של 180 אלפיות השנייה, זמן ההמתנה הכולל עולה על 2 שניות. כל שנייה של עיכוב מפחיתה את ההמרה ב-7% ואת השימור ב-20%. הפסדי הכנסה ממסכים איטיים יכולים להגיע לכ-9,000–13,000 דולר בחיסכון חודשי.
מעבר ל-HTTP/2 עם multiplexing או איגום בקשות בשכבת BFF (Backend for Frontend) פותר זאת ללא שינויים בצד הלקוח. HTTP/2 מבצע multiplexing לבקשות על פני חיבור יחיד, ומבטל חסימת head-of-line. המהנדסים שלנו מיישמים את שתי הגישות בהתאם לארכיטקטורה.
היכן הזמן אובד?
בקשות מיותרות. הבעיה הנפוצה ביותר היא היעדר מטמון בצד הלקוח. URLSession ב-iOS מכבד כותרות Cache-Control כברירת מחדל, אך רק אם השרת מגדיר אותן. אם ה-API מחזיר func retry(_ request: Request, for session: Session, dueTo error: Error, completion: @escaping (RetryResult) -> Void) { let delay = min(pow(2.0, Double(request.retryCount)), 30.0) completion(.retryWithDelay(delay)) } "לאמינות," כל קריאה לנתוני ייחוס (קטגוריות, הגדרות, תצורה) עוברת דרך הרשת. URLCache עם מגבלת 50 MB ו-val cache = Cache(context.cacheDir, 50L * 1024 * 1024) val client = OkHttpClient.Builder().cache(cache).build() ידני עבור נקודות קצה לקריאה בלבד פועל כתיקון מהיר.
מטענים כבדים מדי. נקודת קצה REST לרשימת משתמשים מחזירה 40 שדות, אך הממשק משתמש רק ב-4. עם רשימה של 100 פריטים, זה 60–80 KB נוספים של JSON לכל בקשה. GraphQL פותר זאת ברמת הפרוטוקול, אך אם GraphQL אינו אופציה, ?fields=id,name,avatar_url כפרמטר שאילתה לסינון שדות מפחית חלקית את הבעיה.
בקשות מיותרות בסיבוב מסך. באנדרואיד, ViewModel + LiveData/StateFlow מחזיק את התוצאה ואינו מריץ מחדש את הבקשה כאשר Activity נוצר מחדש. אבל אם הבקשה חיה ב-Fragment.onViewCreated ללא בדיקה, כל סיבוב מפעיל קריאת רשת חדשה. אבחן זאת עם Charles Proxy או OkHttp EventListener עם רישום.
כלים ופתרונות: באיזו גישה לבחור?
iOS (URLSession / Alamofire / Moya)
Alamofire RequestInterceptor הוא מקום נוח ללוגיקת ניסיונות חוזרים עם backoff אקספוננציאלי:
func retry(_ request: Request, for session: Session, dueTo error: Error, completion: @escaping (RetryResult) -> Void) {
let delay = min(pow(2.0, Double(request.retryCount)), 30.0)
completion(.retryWithDelay(delay))
}URLSession עם waitsForConnectivity = true גורם לבקשות להמתין אוטומטית לשחזור הרשת במקום להיכשל מיד. קריטי לאפליקציות offline-first.
אנדרואיד (OkHttp / Retrofit)
OkHttp CacheInterceptor מובנה; פשוט העבר Cache בעת יצירת הלקוח:
val cache = Cache(context.cacheDir, 50L * 1024 * 1024) val client = OkHttpClient.Builder().cache(cache).build() Retrofit + suspend fun מבטל בקשות אוטומטית כאשר ה-coroutine scope נהרס. המפתח הוא לקשור את ה-scope ל-viewModelScope, לא ל-GlobalScope.
ביטול כפילויות בקשות
אם מספר רכיבים מבקשים את אותו משאב בו-זמנית, בצע את הבקשה רק פעם אחת. ב-iOS, השתמש באופרטור share() של Combine על Publisher. באנדרואיד, השתמש ב-StateFlow ב-Repository: המנוי הראשון מפעיל את הבקשה, ומנויים עוקבים מקבלים את התוצאה מאותו flow. זה תואם ל-Apple Human Interface Guidelines בנושא היענות.
תיעדוף בקשות
ב-iOS, URLSession תומך ב-URLRequest.networkServiceType: .responsiveData לפעולות משתמש, .background לאנליטיקה וטעינה מוקדמת. המערכת מתעדפת תעבורה בהתאם—אנליטיקה אינה מתחרה על רוחב פס עם בקשות משתמש.
באנדרואיד, WorkManager עם NetworkType.CONNECTED ועדיפות EXPEDITED לעומת רגיל מאפשר סנכרון נתונים ברקע ללא חסימת זרימת הבקשות הראשית.
כיצד להגדיר מטמון: מדריך שלב אחר שלב
- זהה נקודות קצה לקריאה בלבד (ספריות, קטגוריות, תצורה).
- בדוק אם השרת שולח
Cache-Control. אם לא, כפה מטמון בצד הלקוח. - ב-iOS, הגדר URLCache עם מגבלת 50 MB ו-
cachePolicy = .returnCacheDataElseLoad. - באנדרואיד, צור OkHttp Cache של 50 MB והעבר אותו ל-OkHttpClient.
- לנתונים דינמיים, השתמש ב-ETag או Last-Modified: הלקוח שולח
If-None-Match, השרת מגיב עם304 Not Modified, וחוסך רוחב פס.
השוואה בין HTTP/1.1 ו-HTTP/2
| מאפיין | HTTP/1.1 | HTTP/2 |
|---|---|---|
| חיבורים לכל מארח | 6 (אופייני) | 1 (multiplexed) |
| חסימת head-of-line | כן (תור בקשות) | לא (streams בתוך החיבור) |
| Server push | לא | כן (server push) |
| דחיסת כותרות | לא | HPACK |
| זמן טעינה ב-LTE איטי (14 בקשות) | >2 שניות | ~600 אלפיות השנייה |
HTTP/2 מספק שיפור של פי 3–4 ברשתות חלשות על ידי ביטול תורים.
מקרה בוחן: GraphQL N+1 במובייל
מהניסיון שלנו: אפליקציה השתמשה ב-GraphQL, אך השאילתות נבנו "כפי שנוח"—שאילתה נפרדת לכל כרטיס ברשימה בעת צפייה בפרטים. 20 כרטיסים = 20 שאילתות. יישום תבנית DataLoader בצד הלקוח באמצעות ה-directive @defer (נתמך על ידי Apollo iOS / Apollo Android) אפשר איגום בקשות. זמן טעינת מסך הפרטים ירד מ-2.8 שניות ל-0.6 שניות. הניסיון של הצוות שלנו מראה שגישה זו ישימה גם ל-REST דרך BFF.
השוואת אסטרטגיות מטמון
| גישה | iOS | אנדרואיד | הפחתת תעבורה | מורכבות |
|---|---|---|---|---|
| URLCache / OkHttp Cache | URLCache + cachePolicy | OkHttp Cache | עד 70% | נמוכה |
| מבוסס דיסק + זיכרון | + | + | עד 90% | בינונית |
| ETag / Last-Modified | URLSession ברירת מחדל | OkHttp ברירת מחדל | עד 50% עם 304 | נמוכה |
| Pragma / Cache-Control | ניתן להגדרה | Cache-Control | עד 80% | בינונית |
טעויות נפוצות באופטימיזציה של בקשות רשת
- שכחת הגדרת Cache-Control על השרת, מה שהופך את המטמון ללא יעיל. - שימוש בלקוח HTTP יחיד לכל הבקשות ללא התחשבות בעדיפויות. - אי בדיקה ברשתות חלשות—סימולציית LTE היא חובה. - ביצוע בקשות בכל ציור מחדש של המסך במקום ביצוע ביצירת ViewModel.מה כולל העבודה?
- ביקורת של שכבת הרשת הנוכחית עם מדידות זמן וניתוח תעבורה.
- יישום HTTP/2 או איגום ב-BFF.
- הגדרת מטמון (URLCache, OkHttp Cache, ETag).
- ביטול כפילויות ואיגום בקשות.
- אופטימיזציית מטען (GraphQL או סינון שדות).
- לוגיקת ניסיונות חוזרים ותיעדוף.
- תיעוד והמלצות לתחזוקה שוטפת.
צור קשר כדי להזמין ביקורת שכבת רשת—נעריך את הפרויקט שלך ביום אחד.
למה לבחור בנו?
יש לנו ניסיון של 5+ שנים באופטימיזציית אפליקציות מובייל ל-iOS ואנדרואיד. השלמנו מעל 50 פרויקטים המתמקדים במהירות טעינה, מטמון והפחתת תעבורה. לאחר הביקורת, תקבל המלצות קונקרטיות עם מדדים ניתנים למדידה. שלח בקשה לביקורת וקבל דוח מפורט עם מדדים.
לוח זמנים
ביקורת שכבת רשת ואופטימיזציות ממוקדות: 3–5 ימים. יישום מטמון, לוגיקת ניסיונות חוזרים וביטול כפילויות בכל האפליקציה: 1–2 שבועות. קבל ייעוץ—נחשב לוחות זמנים מדויקים לפרויקט שלך.







