כיווץ APK

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

שימוש רק בתלות הנדרשת

להסתמך רק על מודולי הספרייה שבאמת נחוצים. לדוגמה, הקוד הבא יוסיף תלות במודולים של ספריות ExoPlayer,‏ DASH ו-UI, כפי שנדרש באפליקציה שמפעילה רק תוכן DASH:

Kotlin

implementation("androidx.media3:media3-exoplayer:1.10.0")
implementation("androidx.media3:media3-exoplayer-dash:1.10.0")
implementation("androidx.media3:media3-ui:1.10.0")

Groovy

implementation "androidx.media3:media3-exoplayer:1.10.0"
implementation "androidx.media3:media3-exoplayer-dash:1.10.0"
implementation "androidx.media3:media3-ui:1.10.0"

הפעלה של כיווץ קוד ומקורות המידע

מומלץ להפעיל כיווץ קוד וכיווץ מקורות המידע בגרסאות ה-build של האפליקציה שמוכנות להפצה. המבנה של ExoPlayer מאפשר כיווץ קוד כדי להסיר ביעילות פונקציונליות שלא נמצאת בשימוש. לדוגמה, באפליקציה שמפעילה תוכן DASH, אפשר להקטין את התרומה של ExoPlayer לגודל ה-APK בכ-40% על ידי הפעלת כיווץ קוד.

קראו את המאמר כיווץ, ערפול ואופטימיזציה של האפליקציה כדי ללמוד איך להפעיל כיווץ של קוד וכיווץ מקורות המידע.

ציון הרכיבים לעיבוד שהאפליקציה צריכה

כברירת מחדל, מעבדי הנגן נוצרים באמצעות DefaultRenderersFactory. ‫DefaultRenderersFactory תלוי בכל ההטמעות של Renderer שמופיעות בספריית ExoPlayer, ולכן אף אחת מהן לא תוסר על ידי כיווץ קוד. אם אתם יודעים שהאפליקציה שלכם צריכה רק קבוצת משנה של רכיבי עיבוד, אתם יכולים לציין רכיב עיבוד משלכם במקום זאת.RenderersFactory לדוגמה, אפליקציה שמפעילה רק אודיו יכולה להגדיר מפעל כמו זה כשיוצרים מופעים של ExoPlayer:

Kotlin

val audioOnlyRenderersFactory =
  RenderersFactory {
    handler: Handler,
    videoListener: VideoRendererEventListener,
    audioListener: AudioRendererEventListener,
    textOutput: TextOutput,
    metadataOutput: MetadataOutput ->
    arrayOf<Renderer>(
      MediaCodecAudioRenderer(context, MediaCodecSelector.DEFAULT, handler, audioListener)
    )
  }
val player = ExoPlayer.Builder(context, audioOnlyRenderersFactory).build()

Java

RenderersFactory audioOnlyRenderersFactory =
    (handler, videoListener, audioListener, textOutput, metadataOutput) ->
        new Renderer[] {
          new MediaCodecAudioRenderer(
              context, MediaCodecSelector.DEFAULT, handler, audioListener)
        };
ExoPlayer player = new ExoPlayer.Builder(context, audioOnlyRenderersFactory).build();

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

מציינים אילו מחלצי נתונים נדרשים לאפליקציה

כברירת מחדל, הנגן יוצר מופעים של Extractor כדי להפעיל מדיה מתקדמת באמצעות DefaultExtractorsFactory. ‫DefaultExtractorsFactory תלוי בכל ההטמעות של Extractor שמופיעות בספריית ExoPlayer, ולכן אף אחת מהן לא תוסר על ידי כיווץ קוד. אם אתם יודעים שהאפליקציה שלכם צריכה להפעיל רק מספר קטן של פורמטים של קונטיינרים, או שהיא לא מפעילה מדיה מתקדמת בכלל, אתם יכולים לציין במקום זאת את ExtractorsFactory שלכם. לדוגמה, אפליקציה שצריכה רק להפעיל קובצי mp4 יכולה לספק מפעל כמו:

Kotlin

val mp4ExtractorFactory = ExtractorsFactory {
  arrayOf<Extractor>(Mp4Extractor(DefaultSubtitleParserFactory()))
}
val player =
  ExoPlayer.Builder(context, DefaultMediaSourceFactory(context, mp4ExtractorFactory)).build()

Java

ExtractorsFactory mp4ExtractorFactory =
    () -> new Extractor[] {new Mp4Extractor(new DefaultSubtitleParserFactory())};
ExoPlayer player =
    new ExoPlayer.Builder(context, new DefaultMediaSourceFactory(context, mp4ExtractorFactory))
        .build();

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

אם האפליקציה לא מציגה תוכן מתקדם בכלל, צריך להעביר את הערך ExtractorsFactory.EMPTY אל בנאי המחלקה DefaultMediaSourceFactory, ואז להעביר את הערך mediaSourceFactory אל בנאי המחלקה ExoPlayer.Builder.

Kotlin

val player =
  ExoPlayer.Builder(context, DefaultMediaSourceFactory(context, ExtractorsFactory.EMPTY))
    .build()

Java

ExoPlayer player =
    new ExoPlayer.Builder(
            context, new DefaultMediaSourceFactory(context, ExtractorsFactory.EMPTY))
        .build();

יצירת מופע של MediaSource בהתאמה אישית

אם האפליקציה משתמשת ב-MediaSource.Factory מותאם אישית ואתם רוצים ש-DefaultMediaSourceFactory יוסר באמצעות הסרת קוד, אתם צריכים להעביר את MediaSource.Factory ישירות אל ה-constructor של ExoPlayer.Builder.

Kotlin

val player = ExoPlayer.Builder(context, customMediaSourceFactory).build()

Java

ExoPlayer player = new ExoPlayer.Builder(context, mediaSourceFactory).build();

אם האפליקציה שלכם משתמשת ישירות ב-MediaSource במקום ב-MediaItem, אתם צריכים להעביר את MediaSource.Factory.UNSUPPORTED אל ה-constructor של ExoPlayer.Builder כדי לוודא שאפשר להסיר את DefaultMediaSourceFactory ואת DefaultExtractorsFactory באמצעות צמצום קוד.

Kotlin

val player = ExoPlayer.Builder(context, MediaSource.Factory.UNSUPPORTED).build()
val mediaSource =
  ProgressiveMediaSource.Factory(dataSourceFactory, customExtractorsFactory)
    .createMediaSource(MediaItem.fromUri(uri))

Java

ExoPlayer player = new ExoPlayer.Builder(context, MediaSource.Factory.UNSUPPORTED).build();
ProgressiveMediaSource mediaSource =
    new ProgressiveMediaSource.Factory(dataSourceFactory, customExtractorsFactory)
        .createMediaSource(MediaItem.fromUri(uri));