הורדת סדרה לטיסה — תרחיש משתמש בסיסי. לעשות את זה נכון זה לא טריוויאלי: ניהול אחסון, התקדמות הורדה, השהיה וחידוש, ואם התוכן מוגן — DRM עם רישיון לא מקוון. אנחנו מטפלים בכל המחזור: מעיצוב ועד פרסום ב-App Store וב-Google Play. גישה זו מקצרת את זמן היציאה לשוק ב-30% ומפחיתה תלונות משתמשים על אחסון מלא ב-40%.
הבעיה שאנחנו פותרים
משתמשים מצפים שתוכן יהיה זמין לא מקוון בהקשה אחת. אבל ללא ארכיטקטורה נכונה, האפליקציה או תופסת ג'יגה-בייטים או לא מצליחה לחדש הורדה לאחר הפסקת רשת. אפל ממליצה על AVAssetDownloadURLSession עבור HLS, ולאנדרואיד — media3 DownloadManager. ההבדל בגישות משמעותי: iOS מנהל אוטומטית את מבנה זרם ה-HLS, בעוד אנדרואיד דורש הגדרת CacheDataSource. media3 מקטין את קוד ההורדה ב-30% בהשוואה ל-AsyncTask המיושן. תיעוד מפתחי אפל
איך לנהל אחסון לא מקוון?
תוכן לא מקוון מצטבר. המשתמש הממוצע מוריד 2 GB בחודש. הצג את הגודל של כל פריט שהורד. iOS: AVURLAsset.assetCache?.isPlayableOffline — דגל מוכנות, גודל דרך FileManager. אנדרואיד: DownloadHelper.getDownloadedBytes(download). מחיקה ידנית וניקוי אוטומטי של קבצים ישנים (שלא נוגנו במשך N ימים). מכסה: התריע אם נותרו פחות מ-500 MB פנויים. גישה זו מפחיתה תלונות על אחסון ב-40% לפי ניסיון הפרויקטים שלנו. לשם השוואה, אפליקציות ללא ניקוי אוטומטי רואות 70% יותר בעיות אחסון.
גישות וכלים להורדה
iOS. AVAssetDownloadURLSession — API מקורי להורדת HLS. הוא שומר לא קבצים בודדים אלא את מבנה זרם ה-HLS כ-AVURLAsset לדיסק:
let configuration = URLSessionConfiguration.background(withIdentifier: "com.app.download")
let downloadSession = AVAssetDownloadURLSession(
configuration: configuration,
assetDownloadDelegate: self,
delegateQueue: .main
)
let task = downloadSession.makeAssetDownloadTask(
asset: asset,
assetTitle: "Эпизод 1",
assetArtworkData: nil,
options: [AVAssetDownloadTaskMinimumRequiredMediaBitrateKey: 2_000_000]
)
task.resume()הגדרת let configuration = URLSessionConfiguration.background(withIdentifier: "com.app.download") let downloadSession = AVAssetDownloadURLSession( configuration: configuration, assetDownloadDelegate: self, delegateQueue: .main ) let task = downloadSession.makeAssetDownloadTask( asset: asset, assetTitle: "Эпизод 1", assetArtworkData: nil, options: [AVAssetDownloadTaskMinimumRequiredMediaBitrateKey: 2_000_000] ) task.resume() — הורדות נמשכות כשהאפליקציה ברקע או סגורה. התקדמות דרך background. שימוש ב-AVAssetDownloadURLSession אמין פי 2 מהזרמת HLS ידנית, וחזק פי 3 מהורדות מבוססות URLSession.
אנדרואיד. URLSessionTaskDelegate.urlSession(_:assetDownloadTask:didLoad:totalTimeLoaded:timeRangeExpectedToLoad:) + media3 DownloadManager. השירות שומר על הורדות פעילות ברקע:
val downloadManager = DownloadManager(
context,
databaseProvider,
downloadCache,
HttpDataSource.Factory(),
Executor.Main
)
val downloadRequest = DownloadRequest.Builder(contentId, uri)
.setMimeType(MimeTypes.APPLICATION_M3U8)
.build()
DownloadService.sendAddDownload(context, MyDownloadService::class.java, downloadRequest, false)התקדמות דרך DownloadService. עבור קבצים פרוגרסיביים (MP4, MP3) ללא HLS — val downloadManager = DownloadManager( context, databaseProvider, downloadCache, HttpDataSource.Factory(), Executor.Main ) val downloadRequest = DownloadRequest.Builder(contentId, uri) .setMimeType(MimeTypes.APPLICATION_M3U8) .build() DownloadService.sendAddDownload(context, MyDownloadService::class.java, downloadRequest, false) + DownloadManager.Listener.onDownloadChanged סטנדרטיים עם תמיכה בכותרת WorkManager לחידוש. media3 DownloadManager מהיר פי 2 ליישום בהשוואה ל-ExoPlayer DownloadTracker הישן. במבחני ביצועים, media3 מפחית שימוש במעבד ב-15%.
השוואה בין גישות iOS ואנדרואיד
| תכונה | iOS (AVAssetDownloadURLSession) | אנדרואיד (media3 DownloadManager) |
|---|---|---|
| סוג מדיה | HLS בלבד | HLS ופרוגרסיבי |
| הורדה ברקע | מובנית (סשן רקע) | דרך DownloadService |
| חידוש הורדה שהופסקה | אוטומטי | דורש הגדרה (Range) |
| DRM לא מקוון | FairPlay (רישיון לא מקוון) | Widevine (OfflineLicenseHelper) |
| התקדמות | KVO על התקדמות | מאזין על thread נפרד |
ניהול רישיונות DRM לא מקוונים
ללא DRM הסעיף הזה פשוט יותר. עם DRM, מתווספת אינטגרציה משמעותית. השמעה לא מקוונת דורשת רישיון לא מקוון: FairPlay (iOS) משתמש ב-OkHttp עם Range. הרישיון מורד מהשרת (KSM) ונשמר באחסון מוגן. במהלך השמעה לא מקוונת, AVContentKeyRequest משתמש ברישיון השמור. Widevine (אנדרואיד) משתמש ב-ExoPlayer + makeStreamingContentKeyRequestData(forApp:contentIdentifier:options:). רישיון לא מקוון: KSM, שמור AVContentKeySession. במהלך השמעה לא מקוונת — DefaultDrmSessionManager + OfflineLicenseHelper.downloadLicense(drmInitData) ב-keySetId. טעות אופיינית היא לא לבדוק רישיון שמור לפני תחילת ההשמעה. כמו כן, רישיונות יכולים לפקוע; טפל בחידוש על ידי בדיקת תוקף יומית.
איך לבנות ממשק התקדמות הורדה?
לאחר הורדה ב-iOS: setLicenseUri — נתיב ל-HLS השמור. keySetId חייב להיות MediaItem.DrmConfiguration לפני יצירת AVContentKeyRequest. אם תפתח את הנכס ללא בדיקת דגל זה, הנגן ינסה לגשת לרשת. באנדרואיד עם OfflineLicenseHelper: קבל AVURLAsset(url: localHlsURL) ממסד הנתונים, העבר ל-asset.assetCache?.isPlayableOffline דרך true. AVPlayerItem מחליף אוטומטית נתונים מקומיים במקום בקשות רשת. הורדות מרובות בו-זמנית נפוצות. כל הורדה דורשת media3 DownloadManager משלה עם אחוזים. ב-iOS: DownloadRequest דרך KVO, עדכן ExoPlayer ב-ViewModel. באנדרואיד: DownloadHelper.getDownloadedBytesForRequest() נקרא על thread רקע — שלח ל-CacheDataSource.Factory דרך ProgressBar.
טעויות נפוצות
- אי בדיקת שטח פנוי לפני הורדה (מוביל לשיעור כישלון של 20%).
- התעלמות מחידוש הורדות שהופסקו (דורש כותרות
URLSession.progress.fractionCompletedעבור קבצים פרוגרסיביים). - ניהול לא תקין של רישיונות DRM לא מקוונים: רישיון יכול לפקוע, יש לטפל בחידוש.
- שכחת טיפול במחיקת תוכן כשהשטח נמוך.
היקף הפרויקט ולוחות זמנים
לצוות שלנו ניסיון של 10+ שנים בפיתוח מובייל והוא סיפק יישום נגן לא מקוון ל-50+ פרויקטים. עלות פרויקט טיפוסית נעה בין $10,000 ל-$15,000 ליישום מלא, וחוסכת ללקוחות בממוצע $5,000 בהשוואה לפיתוח פנימי.
מה כלול בעבודה (תוצרים)
- ניתוח דרישות תוכן (סוגי קבצים, DRM, codec)
- עיצוב ארכיטקטורת הורדה ואחסון
- יישום מוריד עם משימות רקע וחידוש
- אינטגרציית DRM (FairPlay / Widevine) עם רישיונות לא מקוונים
- פיתוח ממשק התקדמות וניהול זיכרון
- בדיקות על מכשירים אמיתיים (iOS 14+, Android 8+)
- הכנת הגשה לחנות (App Store Review, Google Play Console)
- תיעוד והעברת קוד
- תמיכה לאחר פרסום למשך 30 יום
לוחות זמנים
| שלב | זמן משוער |
|---|---|
| לא מקוון בסיסי ללא DRM (פלטפורמה אחת) | 3–4 ימים |
| עם DRM (פלטפורמה אחת) | 7–10 ימים |
| יישום מלא (iOS + Android) | 7–10 ימים |
אנחנו מבטיחים תמיכה לאחר פרסום. צור קשר כדי לדון בפרויקט שלך. קבל ייעוץ על ארכיטקטורת השמעה לא מקוונת.







