ב-Google Play, ה-APK הדחוס שמשתמשים מורידים לא יעלה על 100MB. עבור רוב האפליקציות, זה מספיק מקום לכל הקוד והנכסים של האפליקציה. עם זאת, באפליקציות מסוימות נדרש יותר מקום לשמירת גרפיקה באיכות גבוהה, קובצי מדיה או נכסים גדולים אחרים. בעבר, אם הגודל של קובץ ההורדה הדחוס של האפליקציה חרג מ-100MB, הייתם צריכים לארח ולהוריד את הקובץ משאבים נוספים בעצמכם כשהמשתמש פותח את האפליקציה. אירוח והצגה של הקבצים הנוספים יכולים להיות יקרות, וחוויית המשתמש לרוב לא אידיאלית. כדי להקל עליכם את התהליך ונעים יותר למשתמשים, Google Play מאפשר לצרף שני קובצי הרחבה גדולים להשלים את ה-APK.
מערכת Google Play מארחת את קובצי ההרחבה של האפליקציה שלכם ומציגה אותם במכשיר בכתובת ללא עלות. קובצי ההרחבה נשמרים במיקום האחסון המשותף של המכשיר ( כרטיס SD או מחיצה שניתנת לטעינת USB; נקרא גם "חיצוני" אחסון) שדרכם האפליקציה יכולה לגשת אותם. ברוב המכשירים, Google Play מוריד את קובצי ההרחבה באותו זמן יוריד את ה-APK, כך שלאפליקציה יש את כל מה שצריך כשהמשתמש פותח אותה עבור בפעם הראשונה. עם זאת, במקרים מסוימים, האפליקציה חייבת להוריד את הקבצים מ-Google Play כשהאפליקציה מופעלת.
אם רוצים להימנע משימוש בקובצי הרחבה וההורדה הדחוסה של האפליקציה גדולה יותר מעל 100MB, עליך להעלות את האפליקציה שלך באמצעות אפליקציה ל-Android חבילות שמאפשרות הורדה דחוסה של עד 200MB. בנוסף, מכיוון שהשתמשנו ב- קובצי App Bundle מונעים יצירת APK וחתימה ל-Google Play, משתמשים מורידים חבילות APK שעברו אופטימיזציה באמצעות רק את הקוד והמשאבים הדרושים להם כדי להפעיל את האפליקציה. לא צריך לבנות, לחתום מנהלים כמה חבילות APK או קובצי הרחבה, והמשתמשים מקבלים הורדות קטנות ומשופרות.
סקירה כללית
בכל פעם שמעלים APK באמצעות Google Play Console, אפשר: מוסיפים קובץ הרחבה אחד או שניים ל-APK. כל קובץ יכול להיות בגודל של עד 2GB ויכול להיות בכל פורמט אבל מומלץ להשתמש בקובץ דחוס כדי לחסוך ברוחב פס במהלך ההורדה. בעיקרון, לכל קובץ הרחבה יש תפקיד שונה:
- קובץ ההרחבות הראשי הוא קובץ הרחבה ראשי למשאבים נוספים שנדרשים על ידי האפליקציה.
- קובץ ההרחבה patch הוא אופציונלי ומיועד לעדכונים קטנים של את קובץ ההרחבות הראשי.
אפשר להשתמש בשני קובצי ההרחבה בכל שלב, אבל מומלץ מספק את הנכסים הראשיים. הוא אמור להתבצע רק לעיתים רחוקות, אם הוא יעודכן אי פעם. הרחבת התיקון צריך להיות קטן יותר ולשמש כ"ספק תיקונים", שמתעדכן עם כל לשחרר או לפי הצורך.
עם זאת, גם אם עדכון האפליקציה דורש רק קובץ הרחבת תיקון חדש, עדיין
להעלות APK חדש עם קובץ versionCode
מעודכן במניפסט. (ה
ב-Play Console אין אפשרות להעלות קובץ הרחבה ל-APK קיים).
הערה: קובץ הרחבת התיקון זהה מבחינה סמנטית את קובץ ההרחבה הראשי — אפשר להשתמש בכל קובץ בכל דרך שרוצים.
הפורמט של שם הקובץ
כל קובץ הרחבה שתעלו יכול להיות בכל פורמט שתבחרו (ZIP, PDF, MP4 וכו'). אפשר גם משתמשים בכלי JOBB כדי לבצע הצפנה ולהצפין קבוצה של קובצי משאבים ותיקונים נוספים לקבוצה הזו. ללא קשר לסוג הקובץ, Google Play רואה אותם blobs בינאריים אטומים ומשנה את שמות הקבצים באמצעות הסכמה הבאה:
[main|patch].<expansion-version>.<package-name>.obb
הסכמה הזו מורכבת משלושה רכיבים:
main
אוpatch
- מציין אם הקובץ הוא קובץ ההרחבה הראשי או קובץ התיקון. יכולות להיות רק קובץ ראשי אחד וקובץ תיקון אחד לכל APK.
<expansion-version>
- זהו מספר שלם שתואם לקוד הגרסה של ה-APK שבו ההרחבה
הראשון שמשויך לאפליקציה (הוא תואם ל-
android:versionCode
של האפליקציה )."First" על אף ש-Play Console מאפשר משתמשים שוב בקובץ הרחבה שהועלה עם APK חדש, השם של קובץ ההרחבה לא משתנה — הוא שומרת את הגרסה שהוחלה עליה כשהעלית את הקובץ בפעם הראשונה.
<package-name>
- שם החבילה של האפליקציה בסגנון Java.
לדוגמה, נניח שגרסת ה-APK היא 314159 ושם החבילה הוא com.example.app. אם מעלים קובץ הרחבות ראשי, שם הקובץ משתנה ל:
main.314159.com.example.app.obb
מיקום אחסון
כש-Google Play מורידה את קובצי ההרחבה למכשיר, הם נשמרים מקום האחסון המשותף. כדי להבטיח התנהגות תקינה, אסור למחוק, להעביר או לשנות את השם קובצי הרחבות. במקרה שהאפליקציה שלך חייבת לבצע את ההורדה מ-Google Play עצמו, עליכם לשמור את הקבצים באותו מיקום.
השיטה getObbDir()
מחזירה את המיקום הספציפי
לקובצי ההרחבה שלכם:
<shared-storage>/Android/obb/<package-name>/
<shared-storage>
הוא הנתיב לנפח האחסון המשותף, שזמין דרךgetExternalStorageDirectory()
.<package-name>
הוא שם החבילה של האפליקציה בסגנון Java, שזמין החל מ-getPackageName()
.
לכל אפליקציה אין יותר משני קובצי הרחבה בספרייה הזו.
אחד הוא קובץ ההרחבה הראשי והשני הוא קובץ הרחבת התיקונים (במקרה הצורך). לתמונה הקודמת
הגרסאות מוחלפות כאשר מעדכנים את האפליקציה בקובצי הרחבה חדשים. מאז Android
4.4 (רמת API 19), אפליקציות יכולות לקרוא קובצי הרחבה של OBB
ללא אחסון חיצוני
הרשאה. עם זאת, בחלק מההטמעות של Android 6.0 (רמת API 23) ואילך עדיין נדרש
לכן תצטרכו להצהיר על
הרשאה ל-READ_EXTERNAL_STORAGE
בקובץ המניפסט של האפליקציה ויש לבקש הרשאה בכתובת
ככה:
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
ל-Android בגרסה 6 ואילך, צריך לבקש הרשאה לאחסון חיצוני בזמן הריצה. עם זאת, יישומים מסוימים של Android לא דורשות הרשאה כדי לקרוא קובצי OBB. קטע הקוד הבא מראה איך לבדוק אם יש גישת קריאה לפני שמבקשים אחסון חיצוני הרשאה:
Kotlin
val obb = File(obb_filename) var open_failed = false try { BufferedReader(FileReader(obb)).also { br -> ReadObbFile(br) } } catch (e: IOException) { open_failed = true } if (open_failed) { // request READ_EXTERNAL_STORAGE permission before reading OBB file ReadObbFileWithPermission() }
Java
File obb = new File(obb_filename); boolean open_failed = false; try { BufferedReader br = new BufferedReader(new FileReader(obb)); open_failed = false; ReadObbFile(br); } catch (IOException e) { open_failed = true; } if (open_failed) { // request READ_EXTERNAL_STORAGE permission before reading OBB file ReadObbFileWithPermission(); }
אם אתם חייבים לפרק את התוכן של קובצי ההרחבה, אל תמחקו את
OBB
קובצי הרחבה לאחר מכן ולא שומרים את הנתונים שאינם ארוזים
באותה ספרייה. עליך לשמור את הקבצים שלא ארוזים בספרייה
צוין על ידי getExternalFilesDir()
. אבל, לפעמים
אם ניתן, מומלץ להשתמש בפורמט של קובץ הרחבה שמאפשר לקרוא ישירות
את הקובץ במקום לדרוש מכם לפרק את הנתונים. לדוגמה, סיפקנו ספרייה
פרויקט שנקרא ספריית ZIP להרחבה של APK שקורא את הנתונים ישירות
מקובץ ה-ZIP.
זהירות: בניגוד לקובצי APK, כל הקבצים שנשמרו באחסון המשותף, המשתמש ואפליקציות אחרות יכולים לקרוא את הנתונים.
טיפ: אם אורזים קובצי מדיה בקובץ ZIP, אפשר להשתמש במדיה
הפעלת שיחות בקבצים עם פקדי היסט ואורך (כמו MediaPlayer.setDataSource()
SoundPool.load()
) בלי התג
צריך לפתוח את קובץ ה-ZIP. כדי שזה יעבוד, אסור לבצע דחיסה נוספת על
קובצי המדיה במהלך יצירת חבילות ה-ZIP. לדוגמה, כשמשתמשים בכלי zip
,
צריך להשתמש באפשרות -n
כדי לציין את סיומות הקבצים שלא צריכות להיות
דחוס:
zip -n .mp4;.ogg main_expansion media_files
תהליך ההורדה
ברוב המקרים, Google Play מוריד ושומר את קובצי ההרחבה שלכם בו-זמנית יוריד את ה-APK למכשיר. אבל, במקרים מסוימים, Google Play לא יכול להוריד את קובצי ההרחבה, או שהמשתמש מחק את ההרחבה שכבר הורדת בעבר . כדי להתמודד עם מצבים כאלה, לאפליקציה שלך צריכה להיות אפשרות להוריד את הקבצים עצמו כשהפעילות העיקרית מתחילה, באמצעות כתובת URL שסופקה על ידי Google Play.
תהליך ההורדה ברמה גבוהה ייראה כך:
- המשתמש בוחר להתקין את האפליקציה מ-Google Play.
- אם ל-Google Play יש אפשרות להוריד את קובצי ההרחבה (במקרה של רוב
מכשירים), הוא מוריד אותם יחד עם ה-APK.
אם לא תהיה ל-Google Play אפשרות להוריד את קובצי ההרחבה, תתבצע הורדה של APK בלבד.
- כשהמשתמש מפעיל את האפליקציה, צריך לבדוק אם קובצי ההרחבה פועלים
כבר שמורות במכשיר.
- אם כן, האפליקציה מוכנה להפעלה.
- אם לא, האפליקציה חייבת להוריד את קובצי ההרחבה ב-HTTP מ-Google Play. האפליקציה שלך חייבים לשלוח בקשה ללקוח Google Play באמצעות שירות רישוי האפליקציות של Google Play, ש התגובה כוללת את השם, גודל הקובץ וכתובת ה-URL של כל קובץ הרחבה. המידע הזה מאפשר לכם להוריד את הקבצים ולשמור אותם במיקום האחסון הנכון.
זהירות: חשוב מאוד לכלול את הקוד הדרוש כדי להוריד את קובצי ההרחבה מ-Google Play אם הקבצים עדיין לא נמצאים במכשיר כשהאפליקציה מופעלת. כמו שנאמר בקטע הבא לגבי הורדת קובצי ההרחבה, הכנו לכם ספרייה זמינה מפשט מאוד את התהליך ומבצע את ההורדה משירות עם כמות מינימלית של לקבל ממך קוד.
רשימת משימות לפיתוח
הנה סיכום של המשימות שצריך לבצע כדי להשתמש בקובצי הרחבה יחד עם app:
- קודם כול צריך לבדוק אם גודל ההורדה הדחוס של האפליקציה צריך להיות גדול מ-100MB. השטח יקר, ועליך לשמור על גודל הורדה כולל קטן ככל האפשר. אם האפליקציה משתמשת ביותר מ-100MB כדי לספק כמה גרסאות של הנכסים הגרפיים לכמה מסכים צפיפות, במקום זאת כדאי לפרסם מספר חבילות APK שבהן כל APK מכילה רק את הנכסים הנדרשים למסכים שאליהם הוא מטרגט. לקבלת התוצאות הטובות ביותר, לפרסם ב-Google Play, להעלות קובץ Android App Bundle, כולל את כל הקוד והמשאבים שנאספו באפליקציה, אבל הוא מונע יצירת APK וכניסה ל-Google הפעלה.
- קובעים אילו משאבי אפליקציה צריך להפריד מה-APK ואריזתם אותם בקובץ
שישמש כקובץ ההרחבה הראשי.
בדרך כלל צריך להשתמש בקובץ הרחבת התיקון השני רק כשמבצעים עדכונים כדי בקובץ ההרחבות הראשי. עם זאת, אם המשאבים שלך חורגים ממגבלת 2GB עבור תוכלו להשתמש בקובץ התיקון לשאר הנכסים.
- פיתחו את האפליקציה כך שתשתמש במשאבים מקובצי ההרחבה שלכם
מיקום האחסון המשותף של המכשיר.
חשוב לזכור שאסור למחוק, להעביר או לשנות את השם של קובצי ההרחבה.
אם באפליקציה שלך אין דרישה לפורמט מסוים, מומלץ ליצור קובצי ZIP את קובצי ההרחבה שלך, ולאחר מכן לקרוא אותם באמצעות קובץ ZIP של הרחבת APK ספרייה.
- צריך להוסיף לוגיקה לפעילות הראשית של האפליקציה, שבודקת אם קובצי ההרחבה
נמצאים במכשיר בזמן ההפעלה. אם הקבצים לא נמצאים במכשיר, יש להשתמש בשירות רישוי אפליקציות של Google Play כדי לבקש כתובות URL
של קובצי ההרחבות, ואז להוריד ולשמור אותם.
כדי לצמצם משמעותית את כמות הקוד שצריך לכתוב ולהבטיח חוויית משתמש טובה במהלך ההורדה, מומלץ להשתמש בכלי ההורדה ספרייה כדי להטמיע את התנהגות ההורדה.
אם אתם יוצרים שירות הורדות משלכם במקום להשתמש בספרייה, חשוב לדעת אסור לשנות את השמות של קובצי ההרחבה, והם צריכים לשמור אותם מיקום האחסון.
לאחר שמסיימים לפתח את האפליקציה, פועלים לפי המדריך בדיקה קובצי ההרחבות
כללים ומגבלות
הוספת קובצי הרחבה של APK היא תכונה שזמינה כשמעלים את האפליקציה באמצעות ה Play Console. כשמעלים את האפליקציה בפעם הראשונה או כשמעדכנים אפליקציה אפליקציה שמשתמשת בקובצי הרחבה, עליכם להיות מודעים לכללים ולמגבלות הבאים:
- כל קובץ הרחבה יכול להיות בנפח של עד 2GB.
- כדי להוריד את קובצי ההרחבה שלך מ-Google Play, המשתמש חייב צירפתם את האפליקציה מ-Google Play. Google Play לא לספק את כתובות ה-URL של קובצי ההרחבה שלך, אם האפליקציה הותקנה באמצעים אחרים.
- בעת ביצוע ההורדה מתוך האפליקציה, כתובת ה-URL ש-Google Play מספקת לכל קובץ באופן ייחודי, וכל הורדה פגה זמן קצר לאחר שהיא ניתנה לאפליקציה.
- אם מעדכנים את האפליקציה עם APK חדש או מעלים מספר חבילות APK לאותו APK של האפליקציה, תוכלו לבחור קובצי הרחבה שהעליתם לחבילת APK קודמת. שם קובץ ההרחבה לא משתנה – הוא נשאר בגרסה שהתקבלה ב-APK למשך שאליו הקובץ שויך במקור.
- אם אתם משתמשים בקובצי הרחבה בשילוב עם מספר חבילות APK כדי
מספקים קובצי הרחבה שונים למכשירים שונים, אבל עדיין צריך להעלות חבילות APK נפרדות
לכל מכשיר כדי לספק
versionCode
ייחודי ולהצהיר על מסננים שונים כל APK. - לא ניתן לבצע עדכון לאפליקציה באמצעות שינוי קובצי ההרחבה
בלבד — תצטרכו להעלות APK חדש כדי לעדכן את האפליקציה. אם רק השינויים שביצעתם
בנוגע לנכסים בקובצי ההרחבה, ניתן לעדכן את ה-APK פשוט על ידי שינוי
versionCode
(ו אולי גםversionName
). - אין לשמור נתונים אחרים ב-
obb/
שלנו. אם צריך לפרוק חלק מהנתונים, צריך לשמור אותם במיקום שצוין על ידיgetExternalFilesDir()
. - אין למחוק את קובץ ההרחבה
.obb
או לשנות את שמו (אלא אם ביצוע עדכון). הפעולה הזו תגרום ל-Google Play (או לאפליקציה עצמה) לבצע שוב ושוב מורידים את קובץ ההרחבה. - כשמעדכנים קובץ הרחבה באופן ידני, צריך למחוק את קובץ ההרחבה הקודם.
מתבצעת הורדה של קובצי ההרחבה
ברוב המקרים, מערכת Google Play מורידה את קובצי ההרחבה ושומרת אותם במכשיר באותו זמן בכל פעם שתתבצע התקנה או עדכון של ה-APK. באופן כזה, קובצי ההרחבה זמינים מופעלת בפעם הראשונה. עם זאת, במקרים מסוימים האפליקציה חייבת להוריד את את קובצי ההרחבה עצמה על-ידי בקשתם מכתובת ה-URL שסופקה לך בתגובה משירות רישוי האפליקציות של Google Play.
הלוגיקה הבסיסית שנדרשת להורדת קובצי ההרחבה היא:
- כשהאפליקציה מופעלת, צריך לחפש את קובצי ההרחבה במיקום האחסון המשותף (בשדה
Android/obb/<package-name>/
).- אם קובצי ההרחבה מופיעים, הכול מוכן והאפליקציה יכולה להמשיך.
- אם קובצי ההרחבה לא מופיעים שם:
- עליך לבצע בקשה באמצעות רישוי האפליקציות של Google Play כדי לקבל את את השמות, הגדלים וכתובות ה-URL של קובצי ההרחבה של האפליקציה.
- משתמשים בכתובות ה-URL שסופקו על ידי Google Play כדי להוריד את קובצי ההרחבה ולשמור אותם
את קובצי ההרחבה. חובה לשמור את הקבצים במיקום האחסון המשותף
(
Android/obb/<package-name>/
) ולהשתמש בשם הקובץ המדויק שסופק מהתשובה של Google Play.הערה: כתובת ה-URL שמערכת Google Play מספקת עבור קובצי הרחבה הם ייחודיים לכל הורדה, והתוקף של כל הורדה פג זמן קצר לאחר שהוא באפליקציה שלך.
אם האפליקציה חינמית (ולא אפליקציה בתשלום), כנראה שלא השתמשתם בשירות רישוי אפליקציות. זה בעיקר שתוכלו לאכוף מדיניות הרישוי של האפליקציה ולוודא שיש למשתמש את הזכות להשתמש באפליקציה (הוא שילם עליה בצדק ב-Google Play). כדי להקל על פונקציונליות של קובץ הרחבה, שירות הרישוי שופר כדי לספק תגובה לאפליקציה שלכם שכוללת את כתובת ה-URL של קובצי ההרחבה של האפליקציה שמתארחים ב-Google Play. לכן, גם אם האפליקציה זמינה בחינם למשתמשים, עליך לכלול את ספריית אימות רישיונות (LVL) כדי להשתמש בקובצי הרחבה של APK. כמובן, אם האפליקציה הוא בחינם, אתם לא צריכים לאכוף אימות רישיון – פשוט צריך כדי לבצע את הבקשה שתחזיר את כתובת ה-URL של קובצי ההרחבה.
הערה: בין אם האפליקציה זמינה בחינם ובין אם לא, Google Play מחזירה את כתובות ה-URL של קובץ ההרחבה רק אם המשתמש צירף את האפליקציה מ-Google Play.
בנוסף ל-LVL, נדרשת קבוצת קוד שמורידה את קובצי ההרחבה בחיבור HTTP ושומר אותן במיקום הנכון באחסון המשותף של המכשיר. במהלך פיתוח התהליך הזה באפליקציה, יש כמה בעיות שצריך לטפל בהן התעניינות ברכישה:
- יכול להיות שאין במכשיר מספיק מקום לקובצי ההרחבה, לכן כדאי לבדוק לפני התחלת ההורדה, ולהזהיר את המשתמש אם אין מספיק מקום.
- הורדות קבצים צריכות להתבצע בשירות ברקע כדי למנוע את חסימת המשתמש אינטראקציה ומאפשרים למשתמש לצאת מהאפליקציה במהלך ההורדה.
- עשויות להתרחש שגיאות שונות במהלך הבקשה וההורדה שתצטרכו לטפל באלגנטיות.
- קישוריות הרשת עשויה להשתנות במהלך ההורדה, לכן עליך לטפל בשינויים כאלה אם ההורדה הופסקה, ממשיכים את ההורדה כשאפשר.
- בזמן שההורדה מתבצעת ברקע, עליך לספק התראה מציין את התקדמות ההורדה, מודיע למשתמש כשהסתיימה ומחזיר את המשתמש באפליקציה שנבחרה.
כדי לפשט את התהליך הזה, יצרנו את ספריית ההורדות, שמבקש את כתובות ה-URL של קובץ ההרחבה דרך שירות הרישוי, מוריד את קובצי ההרחבה, מבצע את כל המשימות שצוינו למעלה, ואפילו מאפשר לפעילות שלכם להשהות ולהמשיך להוריד. על ידי הוספה של ספריית ההורדות וכמה קטעי קוד לאפליקציה, כמעט כל להוריד את קובצי ההרחבה כבר מקודדים בשבילכם. לכן, כדי לספק חוויית משתמש מינימלית עבורך, מומלץ להשתמש בספריית ההורדות כדי מורידים את קובצי ההרחבות. בקטעים הבאים מוסבר איך לשלב את בספרייה בתוך האפליקציה.
אם אתם מעדיפים לפתח פתרון משלכם כדי להוריד את קובצי ההרחבה, באמצעות
כתובות URL של Play, חייבות לפעול בהתאם לאפליקציה
מסמכי רישוי לביצוע בקשת רישיון, ולאחר מכן אחזור השמות של קובצי ההרחבה,
גדלים וכתובות URL מהתוספות של התשובות. עליך להשתמש במחלקה APKExpansionPolicy
(כלולה בספריית אימות הרישיונות) בתור הרישוי
המדיניות הזו, שמתעדת את השמות, הגדלים וכתובות ה-URL של קובצי ההרחבה משירות הרישוי.
מידע על ספריית ההורדות
כדי להשתמש בקובצי הרחבה של APK באפליקציה שלך ולספק את חוויית המשתמש הטובה ביותר מאמץ מינימלי בשמך, מומלץ להשתמש בספריית ההורדות שכלולה חבילת ספריית ההרחבה של APK ב-Google Play. הספרייה הזו מורידה את קובצי ההרחבה במסגרת שירות ברקע, מציג התראה למשתמש עם סטטוס ההורדה, מטפל ברשת אובדן קישוריות, המשך ההורדה כשהדבר יתאפשר ועוד.
כדי להטמיע הורדות של קובצי הרחבות באמצעות ספריית ההורדות, צריך רק:
- להרחיב מחלקה מיוחדת של
Service
ומחלקה משניתBroadcastReceiver
, שכל אחת מהן דורשת רק כמה דוגמאות שורות קוד ממך. - מוסיפים לוגיקה לפעילות העיקרית, שבודקת אם יש לקובצי ההרחבה שכבר הורד, ואם לא, מפעיל את תהליך ההורדה ומציג ממשק המשתמש של ההתקדמות.
- להטמיע ממשק קריאה חוזרת (callback) שכולל מספר שיטות בפעילות העיקרית, מקבל עדכונים לגבי התקדמות ההורדה.
בקטעים הבאים מוסבר איך להגדיר את האפליקציה באמצעות ספריית ההורדות.
בהכנה לשימוש בספריית ההורדות
כדי להשתמש בספריית ההורדות, צריך להוריד שתי חבילות מ'מנהל ה-SDK' ולהוסיף את הספריות המתאימות אפליקציה.
קודם כול, פותחים את מנהל ה-SDK של Android (כלים > SDK Manager), ובקטע מראה ו התנהגות > הגדרות מערכת > Android SDK, יש לבחור כדי לבחור ולהוריד את הכרטיסייה SDK Tools:
- חבילה של ספריית הרישיונות ב-Google Play
- חבילת ספריית הרחבה של APK ל-Google Play
יצירת מודול ספרייה חדש ל'ספרייה לאימות רישיונות' ול'כלי ההורדה' ספרייה. לכל ספרייה:
- בוחרים באפשרות קובץ > חדש > מודול חדש.
- בחלון יצירת מודול חדש בוחרים באפשרות ספריית Android. ובוחרים באפשרות הבא.
- מציינים שם אפליקציה/ספרייה, כמו 'ספריית הרישיונות של Google Play' וב-Google Play Downloader Library, בוחרים באפשרות Minimum SDK level (רמת SDK מינימלית). סיום.
- בוחרים באפשרות קובץ > מבנה הפרויקט.
- בוחרים בכרטיסייה מאפיינים ובספרייה
מאגר, יש להזין את הספרייה מהספרייה
<sdk>/extras/google/
(play_licensing/
לספריית אימות הרישיון אוplay_apk_expansion/downloader_library/
לספריית ההורדות). - לוחצים על OK כדי ליצור את המודול החדש.
הערה: ספריית ההורדות תלויה ברישיון ספריית האימות. חשוב להוסיף את הרישיון ספריית אימות לנכסי הפרויקט של ספריית ההורדות.
לחלופין, משורת הפקודה, מעדכנים את הפרויקט כך שיכלול את הספריות:
- שינוי ספריות לספרייה
<sdk>/tools/
. - מפעילים את
android update project
עם האפשרות--library
כדי להוסיף גם את LVL וספריית הורדות של הפרויקט. מוצרים לדוגמה:android update project --path ~/Android/MyApp \ --library ~/android_sdk/extras/google/market_licensing \ --library ~/android_sdk/extras/google/market_apk_expansion/downloader_library
באמצעות הוספת ספריית אימות הרישיונות וספריית ההורדות תוכלו לשלב במהירות את היכולת להוריד קובצי הרחבה Google Play. הפורמט שבחרתם לקובצי ההרחבה ואופן הקריאה שלהם מנפח האחסון המשותף הוא הטמעה נפרדת שצריך לשקול בהתבסס על לצרכים של האפליקציה.
טיפ: חבילת ההרחבה Apk כוללת דוגמה יישום שמראה איך להשתמש בספריית ההורדות באפליקציה. הדוגמה משתמשת בספרייה שלישית זמינה בחבילת ה-APK תוספים בשם 'ספריית ZIP של תוספים ל-APK'. אם המיקום שאתם מתכננים באמצעות קובצי ZIP לקובצי הרחבה, מומלץ להוסיף גם את ספריית ה-ZIP להרחבת APK באפליקציה שלך. מידע נוסף זמין בקטע הבא על שימוש בספריית Zip להרחבת APK.
מצהיר על הרשאות המשתמש
כדי להוריד את קובצי ההרחבה, צריך להיכנס לספריית ההורדות נדרשות כמה הרשאות שעליך להצהיר בקובץ המניפסט של האפליקציה. הם הן:
<manifest ...> <!-- Required to access Google Play Licensing --> <uses-permission android:name="com.android.vending.CHECK_LICENSE" /> <!-- Required to download files from Google Play --> <uses-permission android:name="android.permission.INTERNET" /> <!-- Required to keep CPU alive while downloading files (NOT to keep screen awake) --> <uses-permission android:name="android.permission.WAKE_LOCK" /> <!-- Required to poll the state of the network connection and respond to changes --> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <!-- Required to check whether Wi-Fi is enabled --> <uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/> <!-- Required to read and write the expansion files on shared storage --> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> ... </manifest>
הערה: כברירת מחדל, נדרש API לספריית ההורדות רמה 4, אבל לספריית Zip להרחבה של APK נדרשת רמת API 5.
הטמעת שירות ההורדה
כדי לבצע הורדות ברקע, ספריית ההורדות מספקת
הבעלים של מחלקה משנית אחת (Service
) בשם DownloaderService
שאותה צריך להרחיב. לחשבון
בנוסף להורדת קובצי ההרחבה, DownloaderService
:
- רושם
BroadcastReceiver
שמאזינים לשינויים ב- קישוריות הרשת של המכשיר (ה-CONNECTIVITY_ACTION
שידור) כדי להשהות את ההורדה במקרה הצורך (למשל בגלל אובדן קישוריות) להמשיך את ההורדה כשזה אפשרי (קישוריות נרכשת). - תזמון התראה של
RTC_WAKEUP
כדי לנסות להוריד שוב עבור מקרים שבהם השירות נהרג. - יוצר
Notification
בהתאמה אישית שמציג את התקדמות ההורדה שגיאות או שינויים במצב. - מאפשרת לאפליקציה להשהות ולהמשיך את ההורדה באופן ידני.
- מאשרת שנפח האחסון המשותף נטען וזמין, שהקבצים לא קיימים כבר, ושיש מספיק מקום, וכל זה לפני הורדת קובצי ההרחבה. לאחר מכן המשתמש שולח הודעה אם אחד מהתנאים האלה אינו נכון.
כל מה שצריך לעשות הוא ליצור באפליקציה מחלקה שתרחיב את הכיתה DownloaderService
ולשנות שלוש שיטות כדי לספק פרטים ספציפיים לגבי האפליקציה:
getPublicKey()
- הפעולה הזו חייבת להחזיר מחרוזת שהיא המפתח הציבורי של ה-RSA בקידוד Base64 של בעל התוכן הדיגיטלי חשבון, זמין בדף הפרופיל ב-Play Console (מידע נוסף זמין בקטע הגדרת רישוי).
getSALT()
- הפעולה הזו חייבת להחזיר מערך של בייטים אקראיים שבהם נעשה שימוש ברישוי
Policy
כדי יוצריםObfuscator
. ה-salt מבטיח שה-SharedPreferences
המעורפל (obfuscation) שבו שמורים נתוני הרישוי, יהיה ייחודי ולא ניתן יהיה לגלות. getAlarmReceiverClassName()
- פעולה זו חייבת להחזיר את שם הכיתה של
BroadcastReceiver
ב האפליקציה שאמורה לקבל את ההתראה, שמציינת שההורדה צריכה הופעלה מחדש (דבר שעשוי להתרחש אם שירות ההורדה מפסיק באופן בלתי צפוי).
לדוגמה, כך הטמעה מלאה של DownloaderService
:
Kotlin
// You must use the public key belonging to your publisher account const val BASE64_PUBLIC_KEY = "YourLVLKey" // You should also modify this salt val SALT = byteArrayOf( 1, 42, -12, -1, 54, 98, -100, -12, 43, 2, -8, -4, 9, 5, -106, -107, -33, 45, -1, 84 ) class SampleDownloaderService : DownloaderService() { override fun getPublicKey(): String = BASE64_PUBLIC_KEY override fun getSALT(): ByteArray = SALT override fun getAlarmReceiverClassName(): String = SampleAlarmReceiver::class.java.name }
Java
public class SampleDownloaderService extends DownloaderService { // You must use the public key belonging to your publisher account public static final String BASE64_PUBLIC_KEY = "YourLVLKey"; // You should also modify this salt public static final byte[] SALT = new byte[] { 1, 42, -12, -1, 54, 98, -100, -12, 43, 2, -8, -4, 9, 5, -106, -107, -33, 45, -1, 84 }; @Override public String getPublicKey() { return BASE64_PUBLIC_KEY; } @Override public byte[] getSALT() { return SALT; } @Override public String getAlarmReceiverClassName() { return SampleAlarmReceiver.class.getName(); } }
הערה: צריך לעדכן את הערך של BASE64_PUBLIC_KEY
יהיה המפתח הציבורי ששייך לחשבון המוציא לאור שלכם. אפשר למצוא את המפתח בכלי למפתחים
מסוף מתחת לפרטי הפרופיל. צריך לעשות את זה גם כשמבצעים בדיקות
את ההורדות שלך.
חשוב לזכור להצהיר על השירות בקובץ המניפסט:
<app ...> <service android:name=".SampleDownloaderService" /> ... </app>
הטמעה של מקלט האזעקה
כדי לעקוב אחר התקדמות ההורדות של הקבצים ולהפעיל מחדש את ההורדה במקרה הצורך,
DownloaderService
מתזמנת התראה של RTC_WAKEUP
ש
משלוח Intent
ל-BroadcastReceiver
אפליקציה. עליך להגדיר את BroadcastReceiver
כדי לקרוא ל-API
מספריית ההורדות, שבודקת את סטטוס ההורדה ומפעילים מחדש
במקרה הצורך.
צריך רק לעקוף את השיטה onReceive()
כדי לקרוא ל-DownloaderClientMarshaller.startDownloadServiceIfRequired()
.
לדוגמה:
Kotlin
class SampleAlarmReceiver : BroadcastReceiver() { override fun onReceive(context: Context, intent: Intent) { try { DownloaderClientMarshaller.startDownloadServiceIfRequired( context, intent, SampleDownloaderService::class.java ) } catch (e: PackageManager.NameNotFoundException) { e.printStackTrace() } } }
Java
public class SampleAlarmReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { try { DownloaderClientMarshaller.startDownloadServiceIfRequired(context, intent, SampleDownloaderService.class); } catch (NameNotFoundException e) { e.printStackTrace(); } } }
שימו לב שזו המחלקה שעבורה צריך להחזיר את השם
בשיטה getAlarmReceiverClassName()
של השירות (עיינו בקטע הקודם).
חשוב להצהיר על הנמען בקובץ המניפסט:
<app ...> <receiver android:name=".SampleAlarmReceiver" /> ... </app>
התחלת ההורדה
הפעילות העיקרית באפליקציה שלך (זו שהתחילה על ידי סמל מרכז האפליקציות) היא אחראי לבדוק אם קובצי ההרחבה כבר נמצאים במכשיר ולהתחיל ליזום ההורדה, אם לא.
כדי להתחיל את ההורדה באמצעות ספריית ההורדות, נדרשים הדברים הבאים הליכים:
- בודקים אם הקבצים הורדו.
ספריית ההורדות כוללת מספר ממשקי API במחלקה
Helper
, עזרה בתהליך הזה:getExpansionAPKFileName(Context, c, boolean mainFile, int versionCode)
doesFileExist(Context c, String fileName, long fileSize)
לדוגמה, האפליקציה לדוגמה שסופקה בחבילת הרחבת ה-APK מפעילה את הבאה בשיטה
onCreate()
של הפעילות כדי לבדוק אם קובצי ההרחבה כבר קיימים במכשיר:Kotlin
fun expansionFilesDelivered(): Boolean { xAPKS.forEach { xf -> Helpers.getExpansionAPKFileName(this, xf.isBase, xf.fileVersion).also { fileName -> if (!Helpers.doesFileExist(this, fileName, xf.fileSize, false)) return false } } return true }
Java
boolean expansionFilesDelivered() { for (XAPKFile xf : xAPKS) { String fileName = Helpers.getExpansionAPKFileName(this, xf.isBase, xf.fileVersion); if (!Helpers.doesFileExist(this, fileName, xf.fileSize, false)) return false; } return true; }
במקרה הזה, כל אובייקט
XAPKFile
מכיל את מספר הגרסה וגודל הקובץ של או קובץ בוליאני שמציין אם זהו קובץ ההרחבה הראשי. (לצפייה בדוגמה פרטים נוספים על הכיתהSampleDownloaderActivity
של האפליקציה).אם השיטה הזו מחזירה את הערך False, האפליקציה חייבת להתחיל את ההורדה.
- כדי להתחיל את ההורדה, קוראים לשיטה הסטטית
DownloaderClientMarshaller.startDownloadServiceIfRequired(Context c, PendingIntent notificationClient, Class<?> serviceClass)
.השיטה משתמשת בפרמטרים הבאים:
context
:Context
של האפליקציה.notificationClient
:PendingIntent
לפתיחת המסלול הראשי פעילות. השדה הזה משמש בNotification
שבוDownloaderService
יוצר כדי להציג את התקדמות ההורדה. כשהמשתמש בוחר בהתראה, המערכת מפעיל את ה-PendingIntent
שסיפקת כאן ואמור לפתוח את הפעילות שמציג את התקדמות ההורדה (בדרך כלל אותה פעילות שבה התחילה ההורדה).serviceClass
: האובייקטClass
להטמעה שלDownloaderService
, נדרש כדי להפעיל את השירות ולהתחיל את ההורדה במקרה הצורך.
השיטה מחזירה מספר שלם שמציין האם נדרשת הורדה או לא. הערכים האפשריים הם:
NO_DOWNLOAD_REQUIRED
: מוחזר אם הקבצים כבר או שמתבצעת הורדה כבר.LVL_CHECK_REQUIRED
: מוחזר אם אימות הרישיון שנדרשים כדי לקבל את כתובות ה-URL של קובץ ההרחבה.DOWNLOAD_REQUIRED
: מוחזר אם כתובות ה-URL של קובץ ההרחבה כבר ידועות, אבל לא הורדו.
ההתנהגות של המוצרים
LVL_CHECK_REQUIRED
ו-DOWNLOAD_REQUIRED
היא בעצם ובדרך כלל אין צורך לדאוג לגביהם. בפעילות הראשית שלך שמתקשרת אלstartDownloadServiceIfRequired()
, אפשר פשוט לבדוק אם התגובה היאNO_DOWNLOAD_REQUIRED
. אם התגובה היא כל דבר חוץ מ-NO_DOWNLOAD_REQUIRED
, ספריית ההורדות מתחילה את ההורדה וצריך לעדכן את ממשק המשתמש של הפעילות כדי להציג את התקדמות ההורדה (ראו בשלב הבא). אם התשובה היאNO_DOWNLOAD_REQUIRED
, הקבצים יהיו זמינים והאפליקציה תוכל להתחיל לפעול.לדוגמה:
Kotlin
override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) // Check if expansion files are available before going any further if (!expansionFilesDelivered()) { val pendingIntent = // Build an Intent to start this activity from the Notification Intent(this, MainActivity::class.java).apply { flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP }.let { notifierIntent -> PendingIntent.getActivity( this, 0, notifierIntent, PendingIntent.FLAG_UPDATE_CURRENT ) } // Start the download service (if required) val startResult: Int = DownloaderClientMarshaller.startDownloadServiceIfRequired( this, pendingIntent, SampleDownloaderService::class.java ) // If download has started, initialize this activity to show // download progress if (startResult != DownloaderClientMarshaller.NO_DOWNLOAD_REQUIRED) { // This is where you do set up to display the download // progress (next step) ... return } // If the download wasn't necessary, fall through to start the app } startApp() // Expansion files are available, start the app }
Java
@Override public void onCreate(Bundle savedInstanceState) { // Check if expansion files are available before going any further if (!expansionFilesDelivered()) { // Build an Intent to start this activity from the Notification Intent notifierIntent = new Intent(this, MainActivity.getClass()); notifierIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP); ... PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notifierIntent, PendingIntent.FLAG_UPDATE_CURRENT); // Start the download service (if required) int startResult = DownloaderClientMarshaller.startDownloadServiceIfRequired(this, pendingIntent, SampleDownloaderService.class); // If download has started, initialize this activity to show // download progress if (startResult != DownloaderClientMarshaller.NO_DOWNLOAD_REQUIRED) { // This is where you do set up to display the download // progress (next step) ... return; } // If the download wasn't necessary, fall through to start the app } startApp(); // Expansion files are available, start the app }
- כאשר השיטה
startDownloadServiceIfRequired()
מחזירה ערך אחר מאשרNO_DOWNLOAD_REQUIRED
, יוצרים מופע שלIStub
באמצעות מתקשרת אלDownloaderClientMarshaller.CreateStub(IDownloaderClient client, Class<?> downloaderService)
.IStub
מספק קישור בין הפעילות שלך לבין כלי ההורדה כך שהפעילות שלך תקבל התקשרות חזרה לגבי התקדמות ההורדה.כדי ליצור מופע של
IStub
באמצעות קריאה ל-CreateStub()
, צריך להעביר אותו הטמעה של הממשקIDownloaderClient
ו-DownloaderService
יישום בפועל. הקטע הבא שעוסק בקבלת התקדמות ההורדה הממשקIDownloaderClient
, שאותו בדרך כלל צריך להטמיע במחלקהActivity
, כדי לעדכן את ממשק המשתמש של הפעילות כשמצב ההורדה משתנה.מומלץ להתקשר אל
CreateStub()
כדי ליצור יצירה שלIStub
במהלך ה-methodonCreate()
של הפעילות, אחריstartDownloadServiceIfRequired()
מפעיל את ההורדה.למשל, בדוגמת הקוד הקודמת של
onCreate()
אפשר להגיב לתוצאה שלstartDownloadServiceIfRequired()
כך:Kotlin
// Start the download service (if required) val startResult = DownloaderClientMarshaller.startDownloadServiceIfRequired( this@MainActivity, pendingIntent, SampleDownloaderService::class.java ) // If download has started, initialize activity to show progress if (startResult != DownloaderClientMarshaller.NO_DOWNLOAD_REQUIRED) { // Instantiate a member instance of IStub downloaderClientStub = DownloaderClientMarshaller.CreateStub(this, SampleDownloaderService::class.java) // Inflate layout that shows download progress setContentView(R.layout.downloader_ui) return }
Java
// Start the download service (if required) int startResult = DownloaderClientMarshaller.startDownloadServiceIfRequired(this, pendingIntent, SampleDownloaderService.class); // If download has started, initialize activity to show progress if (startResult != DownloaderClientMarshaller.NO_DOWNLOAD_REQUIRED) { // Instantiate a member instance of IStub downloaderClientStub = DownloaderClientMarshaller.CreateStub(this, SampleDownloaderService.class); // Inflate layout that shows download progress setContentView(R.layout.downloader_ui); return; }
לאחר החזרת השיטה
onCreate()
, הפעילות שלך מקבל שיחה אלonResume()
, וכדאי להגיע אליו קוראים למספרconnect()
ב-IStub
ומעבירים אתContext
של האפליקציה. לעומת זאת, צריך לקרואdisconnect()
בקריאה החוזרת (callback) שלonStop()
בפעילות שלך.Kotlin
override fun onResume() { downloaderClientStub?.connect(this) super.onResume() } override fun onStop() { downloaderClientStub?.disconnect(this) super.onStop() }
Java
@Override protected void onResume() { if (null != downloaderClientStub) { downloaderClientStub.connect(this); } super.onResume(); } @Override protected void onStop() { if (null != downloaderClientStub) { downloaderClientStub.disconnect(this); } super.onStop(); }
קריאה ל-
connect()
ב-IStub
מחברת את הפעילות שלך ל-DownloaderService
, כך שהפעילות שלך תקבל קריאות חוזרות לגבי שינויים בהורדה. באמצעות הממשקIDownloaderClient
.
מתקבלת התקדמות ההורדה
כדי לקבל עדכונים לגבי התקדמות ההורדה ולקיים אינטראקציה עם DownloaderService
, עליך להטמיע את הממשק IDownloaderClient
של ספריית ההורדות.
בדרך כלל, הפעילות שבה אתם משתמשים כדי להתחיל את ההורדה צריכה להטמיע את הממשק הזה כדי
להציג את התקדמות ההורדה ולשלוח בקשות לשירות.
שיטות הממשק הנדרשות עבור IDownloaderClient
הן:
onServiceConnected(Messenger m)
- אחרי שיוצרים את ה-
IStub
בפעילות, מקבלים שיחה למספר הזה , שמעביר אובייקטMessenger
שמחובר למכונה. מתוךDownloaderService
. לשלוח בקשות לשירות, למשל להשהות ולהמשיך את התהליך הורדות, עליך לבצע קריאה ל-DownloaderServiceMarshaller.CreateProxy()
כדי לקבל את הממשקIDownloaderService
שמחובר לשירות.הטמעה מומלצת נראית כך:
Kotlin
private var remoteService: IDownloaderService? = null ... override fun onServiceConnected(m: Messenger) { remoteService = DownloaderServiceMarshaller.CreateProxy(m).apply { downloaderClientStub?.messenger?.also { messenger -> onClientUpdated(messenger) } } }
Java
private IDownloaderService remoteService; ... @Override public void onServiceConnected(Messenger m) { remoteService = DownloaderServiceMarshaller.CreateProxy(m); remoteService.onClientUpdated(downloaderClientStub.getMessenger()); }
עם אתחול האובייקט
IDownloaderService
, אפשר לשלוח פקודות אל שירות הורדות, כמו השהיה והמשך של ההורדה (requestPauseDownload()
) ו-requestContinueDownload()
). onDownloadStateChanged(int newState)
- שירות ההורדות קורא לפעולה הזו כשמתרחש שינוי במצב ההורדה, למשל
ההורדה מתחילה או מסתיימת.
הערך
newState
יהיה אחד מהערכים האפשריים שמצוינים ב לפי אחד מקבועיSTATE_*
של המחלקהIDownloaderClient
.כדי להציג מסר מועיל למשתמשים, אפשר לבקש מחרוזת תואמת בשביל כל מדינה, מפעילים את הפונקציה
Helpers.getDownloaderStringResourceIDFromState()
. הזה מחזירה את מזהה המשאב של אחת מהמחרוזות בחבילה עם כלי ההורדה ספרייה. לדוגמה, המחרוזת 'ההורדה הושהתה כי המכשיר שלך בנדידה' תואם ל-STATE_PAUSED_ROAMING
. onDownloadProgress(DownloadProgressInfo progress)
- שירות ההורדות מפעיל אותו כדי לספק אובייקט
DownloadProgressInfo
, שמתאר מידע שונה על התקדמות ההורדה, כולל זמן משוער שנותר, המהירות הנוכחית, ההתקדמות הכוללת וסך הכול, כדי שניתן יהיה לעדכן את ממשק המשתמש של התקדמות ההורדה.
טיפ: דוגמאות של הקריאות החוזרות (callback) האלה לעדכון ההורדה
ממשק המשתמש של ההתקדמות, תוכלו לראות את SampleDownloaderActivity
באפליקציה לדוגמה שסופקה עם
חבילת הרחבת APK.
הנה כמה שיטות ציבוריות שאולי יעזרו לך להשתמש בממשק IDownloaderService
:
requestPauseDownload()
- השהיית ההורדה.
requestContinueDownload()
- המשך הורדה שהושהה.
setDownloadFlags(int flags)
- הגדרת העדפות המשתמש לסוגי הרשתות שבהם אפשר להוריד את הקבצים.
ההטמעה הנוכחית תומכת בדגל אחד,
FLAGS_DOWNLOAD_OVER_CELLULAR
, אבל אפשר להוסיף אחרים. כברירת מחדל, הדגל הזה לא מופעל, כך שהמשתמש חייב להיות מחובר ל-Wi-Fi כדי להוריד קובצי הרחבות. ייתכן שתרצו לספק העדפת משתמש להפעלת הורדות הרשת הסלולרית. במקרה כזה, אפשר לבצע קריאה:Kotlin
remoteService = DownloaderServiceMarshaller.CreateProxy(m).apply { ... setDownloadFlags(IDownloaderService.FLAGS_DOWNLOAD_OVER_CELLULAR) }
Java
remoteService .setDownloadFlags(IDownloaderService.FLAGS_DOWNLOAD_OVER_CELLULAR);
שימוש ב-APKExpansionPolicy
אם תחליטו ליצור שירות הורדות משלכם במקום להשתמש ב-Google Play
בספריית ההורדות, צריך עדיין להשתמש ב-APKExpansionPolicy
שסופקה בספריית אימות הרישיונות. המחלקה APKExpansionPolicy
כמעט זהה למחלקה ServerManagedPolicy
(זמינה
ספריית אימות הרישיונות של Google Play), אבל כוללת טיפול נוסף בהרחבת ה-APK
תוספות לתגובות לקבצים.
הערה: אם אתם משתמשים בספריית ההורדות כמו שמוסבר בקטע הקודם,
מבצעת את כל האינטראקציות עם APKExpansionPolicy
כך שלא צריך להשתמש
ישירות לכיתה הזו.
הקורס כולל שיטות שיעזרו לכם לקבל את המידע הנדרש קובצי הרחבות:
getExpansionURLCount()
getExpansionURL(int index)
getExpansionFileName(int index)
getExpansionFileSize(int index)
למידע נוסף על השימוש בAPKExpansionPolicy
במקרים שלא
באמצעות ספריית ההורדות, עיינו בתיעוד של הוספת רישיונות לאפליקציה.
שמסבירה איך ליישם מדיניות רישיון כמו המדיניות הזו.
קריאת קובץ ההרחבה
אחרי שקובצי הרחבת ה-APK נשמרים במכשיר, אופן קריאת הקבצים
תלוי בסוג הקובץ שבו אתם משתמשים. כפי שצוין בסקירה הכללית,
קובצי הרחבות יכולים להיות כל סוג של קובץ
שרוצים, אבל השמות שלהם משתנים לפי פורמט מסוים של שם הקובץ, והם נשמרים
<shared-storage>/Android/obb/<package-name>/
לא משנה איך אתה קורא את הקבצים שלך, תמיד עליך לבדוק קודם אחסון זמין לקריאה. יש סיכוי שהאחסון של המשתמש מותקן ב מחובר באמצעות USB או למעשה הסיר את כרטיס ה-SD.
הערה: כשהאפליקציה מופעלת, תמיד צריך לבדוק אם
שטח האחסון החיצוני זמין וקריא על ידי שליחת קריאה אל getExternalStorageState()
. הפונקציה מחזירה אחת מתוך כמה מחרוזות אפשריות
שמייצגים את המצב של האחסון החיצוני. כדי שהטקסט יהיה קריא
app, הערך המוחזר חייב להיות MEDIA_MOUNTED
.
קבלת שמות הקבצים
כפי שמתואר בסקירה הכללית, קובצי הרחבת ה-APK נשמרים באמצעות פורמט ספציפי של שם קובץ:
[main|patch].<expansion-version>.<package-name>.obb
כדי לקבל את המיקום והשמות של קובצי ההרחבה, צריך להשתמש ב
getExternalStorageDirectory()
ו-getPackageName()
כדי ליצור את הנתיב לקבצים.
זו שיטה שבה אפשר להשתמש באפליקציה כדי לקבל מערך שמכיל את הנתיב המלא בשני קובצי ההרחבה:
Kotlin
fun getAPKExpansionFiles(ctx: Context, mainVersion: Int, patchVersion: Int): Array<String> { val packageName = ctx.packageName val ret = mutableListOf<String>() if (Environment.getExternalStorageState() == Environment.MEDIA_MOUNTED) { // Build the full path to the app's expansion files val root = Environment.getExternalStorageDirectory() val expPath = File(root.toString() + EXP_PATH + packageName) // Check that expansion file path exists if (expPath.exists()) { if (mainVersion > 0) { val strMainPath = "$expPath${File.separator}main.$mainVersion.$packageName.obb" val main = File(strMainPath) if (main.isFile) { ret += strMainPath } } if (patchVersion > 0) { val strPatchPath = "$expPath${File.separator}patch.$mainVersion.$packageName.obb" val main = File(strPatchPath) if (main.isFile) { ret += strPatchPath } } } } return ret.toTypedArray() }
Java
// The shared path to all app expansion files private final static String EXP_PATH = "/Android/obb/"; static String[] getAPKExpansionFiles(Context ctx, int mainVersion, int patchVersion) { String packageName = ctx.getPackageName(); Vector<String> ret = new Vector<String>(); if (Environment.getExternalStorageState() .equals(Environment.MEDIA_MOUNTED)) { // Build the full path to the app's expansion files File root = Environment.getExternalStorageDirectory(); File expPath = new File(root.toString() + EXP_PATH + packageName); // Check that expansion file path exists if (expPath.exists()) { if ( mainVersion > 0 ) { String strMainPath = expPath + File.separator + "main." + mainVersion + "." + packageName + ".obb"; File main = new File(strMainPath); if ( main.isFile() ) { ret.add(strMainPath); } } if ( patchVersion > 0 ) { String strPatchPath = expPath + File.separator + "patch." + mainVersion + "." + packageName + ".obb"; File main = new File(strPatchPath); if ( main.isFile() ) { ret.add(strPatchPath); } } } } String[] retArray = new String[ret.size()]; ret.toArray(retArray); return retArray; }
כדי לקרוא לשיטה הזו, אפשר להעביר אותה לאפליקציה Context
ואת גרסת קובץ ההרחבה הרצוי.
קיימות דרכים רבות לקביעת מספר הגרסה של קובץ ההרחבה. אחת הדרכים הפשוטות היא
לשמור את הגרסה בקובץ SharedPreferences
כשההורדה מתחילה, עד
שליחת שאילתה על שם קובץ ההרחבה באמצעות השיטה getExpansionFileName(int index)
של המחלקה APKExpansionPolicy
. כדי לקבל את קוד הגרסה, צריך לקרוא את הקובץ SharedPreferences
כשרוצים לגשת לתוסף
חדש.
למידע נוסף על קריאה מהאחסון המשותף, אפשר לעיין באחסון הנתונים התיעוד.
שימוש בספריית ZIP להרחבה של APK
חבילת ה-Apk בקשת Google Market כוללת ספרייה בשם ה-APK
ספריית ZIP להרחבה (נמצאת ב-<sdk>/extras/google/google_market_apk_expansion/zip_file/
). זוהי ספרייה אופציונלית
עוזר לקרוא את ההרחבה
קבצים כשהם נשמרים כקובצי ZIP. השימוש בספרייה הזו מאפשר לך לקרוא בקלות משאבים מ-
את קובצי הרחבת ה-ZIP כמערכת קבצים וירטואלית.
ספריית ה-ZIP של הרחבת ה-APK כוללת את המחלקות וממשקי ה-API הבאים:
APKExpansionSupport
- מספקת כמה שיטות לגישה לשמות של קובצי הרחבה ולקובצי ZIP:
getAPKExpansionFiles()
- אותה שיטה שמוצגת למעלה שמחזירה את נתיב הקובץ המלא לשתי ההרחבה .
getAPKExpansionZipFile(Context ctx, int mainVersion, int patchVersion)
- מחזירה
ZipResourceFile
שמייצגת את הסכום של הקובץ הראשי קובץ תיקונים. כלומר, אם מציינים גם את השדהmainVersion
וגם אתpatchVersion
, הפעולה הזו מחזירהZipResourceFile
שמספק גישת קריאה אל כל הנתונים, והנתונים של קובץ התיקון ימוזגו מעל הקובץ הראשי.
ZipResourceFile
- מייצג קובץ ZIP באחסון המשותף ומבצע את כל העבודה כדי לספק קובץ ZIP וירטואלי
מערכת הקבצים על סמך קובצי ה-ZIP. אפשר לקבל מכונה באמצעות
APKExpansionSupport.getAPKExpansionZipFile()
או באמצעותZipResourceFile
על ידי העברת בנתיב לקובץ ההרחבה. הקורס הזה כולל מגוון שיטות שימושיות, אבל באופן כללי לא צריכות גישה לרובן. יש שתי שיטות חשובות:getInputStream(String assetPath)
- מספק
InputStream
לקריאת קובץ בתוך קובץ ה-ZIP.assetPath
חייב להיות הנתיב לקובץ הרצוי, ביחס ל בסיס התוכן של קובץ ה-ZIP. getAssetFileDescriptor(String assetPath)
- מספק
AssetFileDescriptor
לקובץ בתוך קובץ ZIP. הערךassetPath
חייב להיות הנתיב לקובץ הרצוי, ביחס ל בסיס התוכן של קובץ ה-ZIP. האפשרות הזו שימושית לממשקי API מסוימים של Android שנדרשים להםAssetFileDescriptor
, כמו חלק מממשקי ה-API שלMediaPlayer
.
APEZProvider
- ברוב האפליקציות אין צורך להשתמש בכיתה הזו. בכיתה הזו מוגדר
ContentProvider
שמארגן את הנתונים מקובצי ה-ZIP דרך תוכן ספקUri
כדי לספק גישה לקובץ עבור ממשקי API מסוימים של Android צפויה גישה שלUri
לקובצי מדיה. לדוגמה, האפשרות הזו שימושית אם רוצים הפעלת סרטון עםVideoView.setVideoURI()
.
דילוג על דחיסת קובצי מדיה בפורמט ZIP
אם אתם משתמשים בקובצי הרחבה כדי לאחסן קובצי מדיה, קובץ ZIP עדיין מאפשר לכם:
להשתמש בקריאות להפעלת מדיה ב-Android שמספקות בקרות היסט ומשך זמן (כמו MediaPlayer.setDataSource()
SoundPool.load()
). כדי
זה יעבוד, אסור לבצע דחיסה נוספת על קובצי המדיה בזמן יצירת ה-ZIP
חבילות. לדוגמה, כשמשתמשים בכלי zip
, צריך להשתמש בכלי -n
אפשרות לציין את סיומות הקבצים שאין לדחוס את הקבצים:
zip -n .mp4;.ogg main_expansion media_files
קריאה מקובץ ZIP
כשמשתמשים בספריית Zip להרחבה של APK, בדרך כלל צריך לקרוא קובץ מ-ZIP הבאים:
Kotlin
// Get a ZipResourceFile representing a merger of both the main and patch files val expansionFile = APKExpansionSupport.getAPKExpansionZipFile(appContext, mainVersion, patchVersion) // Get an input stream for a known file inside the expansion file ZIPs expansionFile.getInputStream(pathToFileInsideZip).use { ... }
Java
// Get a ZipResourceFile representing a merger of both the main and patch files ZipResourceFile expansionFile = APKExpansionSupport.getAPKExpansionZipFile(appContext, mainVersion, patchVersion); // Get an input stream for a known file inside the expansion file ZIPs InputStream fileStream = expansionFile.getInputStream(pathToFileInsideZip);
הקוד שלמעלה מספק גישה לכל קובץ שקיים בקובץ ההרחבה הראשי או
תיקון קובץ הרחבות, על ידי קריאה של מפה ממוזגת של כל הקבצים משני הקבצים. נשאר רק אצלך
צריך לספק את ה-method getAPKExpansionFile()
היא האפליקציה android.content.Context
ואת מספר הגרסה של קובץ ההרחבה הראשי והתיקון
קובץ הרחבות.
אם אתם מעדיפים לקרוא מקובץ הרחבה ספציפי, תוכלו להשתמש ב-constructor של ZipResourceFile
בנתיב לקובץ ההרחבה הרצוי:
Kotlin
// Get a ZipResourceFile representing a specific expansion file val expansionFile = ZipResourceFile(filePathToMyZip) // Get an input stream for a known file inside the expansion file ZIPs expansionFile.getInputStream(pathToFileInsideZip).use { ... }
Java
// Get a ZipResourceFile representing a specific expansion file ZipResourceFile expansionFile = new ZipResourceFile(filePathToMyZip); // Get an input stream for a known file inside the expansion file ZIPs InputStream fileStream = expansionFile.getInputStream(pathToFileInsideZip);
למידע נוסף על השימוש בספרייה הזו עבור קובצי ההרחבה שלך, אפשר להיכנס לכתובת
המחלקה SampleDownloaderActivity
של האפליקציה לדוגמה, שכוללת קוד נוסף
לאמת את הקבצים שהורדתם באמצעות CRC. חשוב לזכור שאם משתמשים בדוגמה הזאת כבסיס
בעצמך, יש צורך להצהיר על גודל הבייטים של ההרחבה
קבצים במערך xAPKS
.
בדיקה של קובצי ההרחבה
לפני פרסום האפליקציה, יש שני דברים שכדאי לבדוק: קריאת קובצי הרחבה והורדת הקבצים.
בדיקת קריאות קבצים
לפני שמעלים את האפליקציה ל-Google Play, צריך צריך לבדוק את היכולת של האפליקציה לקרוא את הקבצים מהאחסון המשותף. כל מה שצריך לעשות הוא להוסיף את הקבצים למיקום המתאים באחסון המשותף במכשיר ולהפעיל את app:
- במכשיר שלכם, יוצרים את הספרייה המתאימה באחסון המשותף, שבו Google
הקבצים שלך יישמרו ב-Play.
לדוגמה, אם שם החבילה הוא
com.example.android
, עליך ליצור הספרייהAndroid/obb/com.example.android/
בשטח האחסון המשותף. (חיבור את מכשיר הבדיקה למחשב כדי לטעון את נפח האחסון המשותף וליצור אותו באופן ידני ). - להוסיף באופן ידני את קובצי ההרחבה לספרייה הזו. חשוב לוודא ששינית את שמות הקבצים ל-
תואם לפורמט שם הקובץ שישמש את Google Play.
לדוגמה, בלי קשר לסוג הקובץ, קובץ ההרחבה הראשי של אפליקציית
com.example.android
צריך להיותmain.0300110.com.example.android.obb
. קוד הגרסה יכול להיות כל ערך שרוצים. רק חשוב לזכור:- קובץ ההרחבה הראשי תמיד מתחיל ב-
main
וקובץ התיקון מתחיל ב-patch
. - שם החבילה תמיד תואם לשם ה-APK שאליו הקובץ מצורף Google Play.
- קובץ ההרחבה הראשי תמיד מתחיל ב-
- עכשיו כשקובצי ההרחבה נמצאים במכשיר, אפשר להתקין ולהפעיל את האפליקציה לבדוק את קובצי ההרחבה.
הנה כמה תזכורות לגבי הטיפול בקובצי ההרחבות:
- אין למחוק או לשנות את השם של קובצי ההרחבה
.obb
(גם אם פורקים את החבילה) את הנתונים למיקום אחר). הפעולה הזו תגרום ל-Google Play (או לאפליקציה עצמה) להוריד את קובץ ההרחבה שוב ושוב. - אין לשמור נתונים אחרים ב-
obb/
שלנו. אם צריך לפרוק חלק מהנתונים, צריך לשמור אותם במיקום שצוין על ידיgetExternalFilesDir()
.
בדיקה של הורדות קבצים
כי לפעמים האפליקציה צריכה להוריד באופן ידני את קובצי ההרחבה בפעם הראשונה. נפתחת, חשוב לבדוק את התהליך כדי לוודא שהאפליקציה יכולה לשלוח שאילתות של כתובות ה-URL, מורידים את הקבצים ושומרים אותם במכשיר.
כדי לבדוק את היישום של תהליך ההורדה הידנית באפליקציה: אפשר לפרסם אותו במסלול הבדיקה הפנימית, כך שהוא יהיה זמין רק בודקים מורשים. אם הכול פועל כצפוי, האפליקציה אמורה לפעול להתחיל להוריד את קובצי ההרחבה ברגע שהפעילות הראשית מתחילה.
הערה: בעבר אפשר היה לבדוק אפליקציה לפי העלאת "טיוטה" שלא פורסמה . הפונקציונליות הזו כבר לא בתוקף נתמך. במקום זאת, צריך לפרסם אותה לבדיקה פנימית, בקבוצה מוגדרת או לבדיקה פתוחה לציבור טראק. מידע נוסף זמין במאמר הבא: אפליקציות טיוטה לא תמיכה ארוכה יותר.
האפליקציה מתעדכנת
אחד היתרונות הנהדרים של שימוש בקובצי הרחבה ב-Google Play הוא היכולת לעדכן את האפליקציה בלי להוריד מחדש את כל הנכסים המקוריים. כי Google Play מאפשר לספק שני קובצי הרחבות לכל APK, אפשר להשתמש בקובץ השני כ-patch שמספק עדכונים ונכסים חדשים. פעולה זו מונעת תצטרכו להוריד מחדש את קובץ ההרחבה הראשי, שעשוי להיות גדול ויקר למשתמשים.
מבחינה טכנית קובץ הרחבת התיקון זהה לקובץ ההרחבה הראשי, מערכת Android או Google Play מבצעים תיקונים אמיתיים בין הרחבת התיקון . קוד האפליקציה חייב לבצע את התיקונים הדרושים בעצמו.
אם משתמשים בקובצי ZIP כקובצי הרחבה, קובץ ZIP של הרחבת ה-APK ספרייה שכלולה בחבילת ההרחבה Apk כוללת את האפשרות למזג על תיקון בקובץ ההרחבה הראשי.
הערה: גם אם צריך לערוך שינויים רק בתיקון
עליך לעדכן את ה-APK כדי ש-Google Play יבצע עדכון, בכל זאת.
אם לא צריך לבצע שינויים בקוד באפליקציה, צריך פשוט לעדכן את versionCode
.
כל עוד לא משנים את קובץ ההרחבה הראשי שמשויך ל-APK ב-Play Console, משתמשים שהתקינו את האפליקציה שלך בעבר לא מורידים את קובץ ההרחבה הראשי. משתמשים קיימים מקבלים רק את ה-APK המעודכן ואת התיקון החדש קובץ הרחבות (ששומר על קובץ ההרחבה הראשי הקודם).
ריכזנו כאן כמה בעיות שכדאי לזכור לגבי עדכונים בקובצי הרחבה:
- ניתן להפעיל רק שני קובצי הרחבה לאפליקציה שלכם בו-זמנית. הרחבה ראשית אחת קובץ וקובץ הרחבות אחד של תיקונים. במהלך עדכון קובץ, Google Play מוחקת את הגרסה הקודמת (וכך גם האפליקציה בעת ביצוע עדכונים ידניים).
- בעת הוספת קובץ הרחבת תיקון, מערכת Android לא באמת מתקן את האפליקציה או קובץ ההרחבה הראשי. צריך לעצב את האפליקציה כך שתתמוך בנתוני התיקונים. עם זאת, חבילת ההרחבה Apk כוללת ספרייה לשימוש בקובצי ZIP כקובצי הרחבות, שממזגים את הנתונים מקובץ התיקון אל קובץ ההרחבה הראשי, תוכלו לקרוא בקלות את כל הנתונים של קובץ ההרחבה.