הגדרת וריאנטים של build

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

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

וריאנטים של build הם התוצאה של שימוש של Gradle בקבוצה ספציפית של כללים כדי לשלב הגדרות, קוד ומשאבים שהוגדרו בסוגי ה-build ובגרסאות המוצר. אתם לא מגדירים ישירות את וריאנט ה-build, אלא את סוגי ה-build וגרסאות המוצר שמרכיבים אותם.

לדוגמה, גרסת מוצר מסוג "הדגמה" עשויה לציין תכונות מסוימות ודרישות מכשיר, כמו קוד מקור מותאם אישית, משאבים ורמות API מינימליות, בעוד שסוג ה-build "ניפוי באגים" מחיל הגדרות build ואריזה שונות, כמו אפשרויות ניפוי באגים ומפתחות חתימה. וריאנט ה-build שמשלב את שניהם הוא גרסת ה-"demoDebug" של האפליקציה, והוא כולל שילוב של ההגדרות והמשאבים שנכללים בגרסת המוצר "demo", בסוג ה-build‏ "debug" ובקבוצת המקורות main/.

הגדרת סוגי גרסאות build

אתם יכולים ליצור ולהגדיר סוגי build בתוך הבלוק android של הקובץ build.gradle.kts ברמת המודול. כשיוצרים מודול חדש, Android Studio יוצר באופן אוטומטי את סוגי ה-build לניפוי באגים וגרסת build להפצה. למרות שגרסת build לניפוי באגים לא מופיעה בקובץ של תצורת ה-build, ‏Android Studio מגדיר אותה עם debuggable true. כך תוכלו לנפות באגים באפליקציה במכשירי Android מאובטחים ולהגדיר חתימת אפליקציה באמצעות מאגר מפתחות כללי לניפוי באגים.

אם רוצים להוסיף או לשנות הגדרות מסוימות, אפשר להוסיף את סוג ה-build לניפוי באגים להגדרות האישיות. בדוגמה הבאה מצוין applicationIdSuffix לסוג ה-build לניפוי באגים, ומוגדר סוג build של "סביבת Staging" שמאותחל באמצעות הגדרות מסוג ה-build לניפוי באגים:

Kotlin

android {
    defaultConfig {
        manifestPlaceholders["hostName"] = "www.example.com"
        ...
    }
    buildTypes {
        getByName("release") {
            isMinifyEnabled = true
            proguardFiles(getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro")
        }

        getByName("debug") {
            applicationIdSuffix = ".debug"
            isDebuggable = true
        }

        /**
         * The `initWith` property lets you copy configurations from other build types,
         * then configure only the settings you want to change. This one copies the debug build
         * type, and then changes the manifest placeholder and application ID.
         */
        create("staging") {
            initWith(getByName("debug"))
            manifestPlaceholders["hostName"] = "internal.example.com"
            applicationIdSuffix = ".debugStaging"
        }
    }
}

מגניב

android {
    defaultConfig {
        manifestPlaceholders = [hostName:"www.example.com"]
        ...
    }
    buildTypes {
        release {
            minifyEnabled true
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }

        debug {
            applicationIdSuffix ".debug"
            debuggable true
        }

        /**
         * The `initWith` property lets you copy configurations from other build types,
         * then configure only the settings you want to change. This one copies the debug build
         * type, and then changes the manifest placeholder and application ID.
         */
        staging {
            initWith debug
            manifestPlaceholders = [hostName:"internal.example.com"]
            applicationIdSuffix ".debugStaging"
        }
    }
}

הערה: כשעורכים שינויים בקובץ תצורת build, צריך לסנכרן את הפרויקט עם התצורה החדשה ב-Android Studio. כדי לסנכרן את הפרויקט, לוחצים על Sync Now בסרגל ההתראות שמופיע כשמבצעים שינוי, או לוחצים על Sync Project בסרגל הכלים. אם ב-Android Studio מזוהות שגיאות בהגדרה, מופיע החלון Messages עם תיאור הבעיה.

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

הגדרת גרסאות המוצר

יצירת גרסאות מוצר דומה ליצירת סוגי build. מוסיפים גרסאות מוצר לבלוק productFlavors בתצורת ה-build וכוללים את ההגדרות הרצויות. גרסאות המוצרים תומכות באותם מאפיינים כמו defaultConfig, כי defaultConfig למעשה שייך למחלקה ProductFlavor. כלומר, אתם יכולים לספק את ההגדרות הבסיסיות לכל הגרסאות בבלוק defaultConfig, וכל גרסה יכולה לשנות כל אחד מערכי ברירת המחדל האלה, כמו applicationId. למידע נוסף על מזהה האפליקציה, קרא את הגדרת מזהה האפליקציה.

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

כל הגרסאות חייבות להשתייך לקבוצת גרסאות עם שם, שהיא קבוצה של גרסאות מוצר. צריך להקצות את כל הגרסאות לקבוצת גרסאות, אחרת תופיע שגיאת ה-build הבאה:

  Error: All flavors must now belong to a named flavor dimension.
  The flavor 'flavor_name' is not assigned to a flavor dimension.

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

בדוגמת הקוד הבאה יוצרים קבוצת גרסאות בשם version ומוסיפים את גרסאות המוצר demo ו-full. לגרסאות האלה יש applicationIdSuffix ו-versionNameSuffix משלהן:

Kotlin

android {
    ...
    defaultConfig {...}
    buildTypes {
        getByName("debug"){...}
        getByName("release"){...}
    }
    // Specifies one flavor dimension.
    flavorDimensions += "version"
    productFlavors {
        create("demo") {
            // Assigns this product flavor to the "version" flavor dimension.
            // If you are using only one dimension, this property is optional,
            // and the plugin automatically assigns all the module's flavors to
            // that dimension.
            dimension = "version"
            applicationIdSuffix = ".demo"
            versionNameSuffix = "-demo"
        }
        create("full") {
            dimension = "version"
            applicationIdSuffix = ".full"
            versionNameSuffix = "-full"
        }
    }
}

מגניב

android {
    ...
    defaultConfig {...}
    buildTypes {
        debug{...}
        release{...}
    }
    // Specifies one flavor dimension.
    flavorDimensions "version"
    productFlavors {
        demo {
            // Assigns this product flavor to the "version" flavor dimension.
            // If you are using only one dimension, this property is optional,
            // and the plugin automatically assigns all the module's flavors to
            // that dimension.
            dimension "version"
            applicationIdSuffix ".demo"
            versionNameSuffix "-demo"
        }
        full {
            dimension "version"
            applicationIdSuffix ".full"
            versionNameSuffix "-full"
        }
    }
}

הערה: אם יש לכם אפליקציה מדור קודם (שנוצרה לפני אוגוסט 2021) שאתם מפיצים באמצעות קובצי APK ב-Google Play, כדי להפיץ את האפליקציה באמצעות תמיכה בכמה קובצי APK ב-Google Play, הקצו את אותו ערך applicationId לכל הווריאציות ותנו לכל וריאציה versionCode שונה. כדי להפיץ גרסאות שונות של האפליקציה כאפליקציות נפרדות ב-Google Play, צריך להקצות applicationId שונה לכל גרסה.

אחרי שיוצרים ומגדירים את גרסאות המוצר, לוחצים על Sync Now (סנכרון עכשיו) בסרגל ההתראות. אחרי שהסנכרון מסתיים, Gradle יוצר באופן אוטומטי וריאנטים של build על סמך סוגי ה-build וגרסאות המוצר, ונותן להם שמות לפי <product-flavor><Build-Type>. לדוגמה, אם יצרתם את גרסאות המוצר "demo" ו-"full", והשארתם את סוגי ה-build שמוגדרים כברירת מחדל, "debug" ו-"release",‏ Gradle יוצר את וריאנט ה-build הבאים:

  • demoDebug
  • demoRelease
  • fullDebug
  • fullRelease

כדי לבחור את וריאנט ה-build שרוצים ליצור ולהריץ, עוברים אל Build‏ > Select Build Variant ובוחרים וריאנט build מהתפריט. כדי להתחיל להתאים אישית כל וריאנט build עם תכונות ומשאבים משלו, צריך ליצור ולנהל קבוצות מקורות, כפי שמתואר בדף הזה.

שינוי מזהה האפליקציה של וריאנטים של build

כשיוצרים קובץ APK או AAB לאפליקציה, כלי ה-build מתייגים את האפליקציה עם מזהה האפליקציה שמוגדר בבלוק defaultConfig מהקובץ build.gradle.kts, כמו בדוגמה הבאה. עם זאת, אם אתם רוצים ליצור גרסאות שונות של האפליקציה שיופיעו כדפי אפליקציה נפרדים בחנות Google Play, כמו גרסה "חינמית" וגרסת "פרו", אתם צריכים ליצור וריאנטים נפרדים של build, שלכל אחד מהם יש מזהה אפליקציה שונה.

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

Kotlin

android {
    defaultConfig {
        applicationId = "com.example.myapp"
    }
    productFlavors {
        create("free") {
            applicationIdSuffix = ".free"
        }
        create("pro") {
            applicationIdSuffix = ".pro"
        }
    }
}

מגניב

android {
    defaultConfig {
        applicationId "com.example.myapp"
    }
    productFlavors {
        free {
            applicationIdSuffix ".free"
        }
        pro {
            applicationIdSuffix ".pro"
        }
    }
}

כך, מזהה האפליקציה של גרסת המוצר "free" הוא "com.example.myapp.free".

אפשר גם להשתמש ב-applicationIdSuffix כדי להוסיף פלח על סמך סוג ה-build, כמו שמוצג כאן:

Kotlin

android {
    ...
    buildTypes {
        getByName("debug") {
            applicationIdSuffix = ".debug"
        }
    }
}

מגניב

android {
    ...
    buildTypes {
        debug {
            applicationIdSuffix ".debug"
        }
    }
}

מכיוון ש-Gradle מחיל את הגדרת סוג ה-build אחרי גרסת המוצר, מזהה האפליקציה של וריאנט ה-build‏ "free debug" הוא "com.example.myapp.free.debug". זה שימושי כשרוצים שגם גרסת ה-build לניפוי באגים וגם גרסת ה-build להפצה יהיו באותו מכשיר, כי לא יכולות להיות שתי אפליקציות עם אותו מזהה אפליקציה.

אם יש לכם אפליקציה מדור קודם (שנוצרה לפני אוגוסט 2021) שאתם מפיצים באמצעות קובצי APK ב-Google Play, ואתם רוצים להשתמש באותו דף אפליקציה כדי להפיץ כמה קובצי APK שכל אחד מהם מטרגט תצורת מכשיר שונה, כמו רמת ה-API, אתם צריכים להשתמש באותו מזהה אפליקציה לכל וריאנט build, אבל לתת לכל קובץ APK ערך versionCode שונה. מידע נוסף זמין במאמר בנושא תמיכה בכמה חבילות APK. הפרסום באמצעות קובצי AAB לא מושפע, כי הוא משתמש בארטיפקט אחד שמשתמש בקוד גרסה אחד ובמזהה אפליקציה אחד כברירת מחדל.

טיפ: אם אתם צריכים להפנות למזהה האפליקציה בקובץ המניפסט, אתם יכולים להשתמש בפלייסהולדר ${applicationId} בכל מאפיין של המניפסט. במהלך ה-build, ‏Gradle מחליף את התג הזה במזהה האפליקציה בפועל. מידע נוסף זמין במאמר הוספת משתני build למניפסט.

שילוב של כמה גרסאות מוצר עם קבוצות גרסאות

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

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

בדוגמת הקוד הבאה נעשה שימוש במאפיין flavorDimensions כדי ליצור קבוצת גרסאות בשם mode, שכוללת את גרסאות המוצר full ו-demo, וקבוצת גרסאות בשם api, שכוללת הגדרות של גרסאות מוצר על סמך רמת ה-API:

Kotlin

android {
  ...
  buildTypes {
    getByName("debug") {...}
    getByName("release") {...}
  }

  // Specifies the flavor dimensions you want to use. The order in which you
  // list the dimensions determines their priority, from highest to lowest,
  // when Gradle merges variant sources and configurations. You must assign
  // each product flavor you configure to one of the flavor dimensions.
  flavorDimensions += listOf("api", "mode")

  productFlavors {
    create("demo") {
      // Assigns this product flavor to the "mode" flavor dimension.
      dimension = "mode"
      ...
    }

    create("full") {
      dimension = "mode"
      ...
    }

    // Configurations in the "api" product flavors override those in "mode"
    // flavors and the defaultConfig block. Gradle determines the priority
    // between flavor dimensions based on the order in which they appear next
    // to the flavorDimensions property, with the first dimension having a higher
    // priority than the second, and so on.
    create("minApi24") {
      dimension = "api"
      minSdk = 24
      // To ensure the target device receives the version of the app with
      // the highest compatible API level, assign version codes in increasing
      // value with API level.
      versionCode = 30000 + (android.defaultConfig.versionCode ?: 0)
      versionNameSuffix = "-minApi24"
      ...
    }

    create("minApi23") {
      dimension = "api"
      minSdk = 23
      versionCode = 20000  + (android.defaultConfig.versionCode ?: 0)
      versionNameSuffix = "-minApi23"
      ...
    }

    create("minApi21") {
      dimension = "api"
      minSdk = 21
      versionCode = 10000  + (android.defaultConfig.versionCode ?: 0)
      versionNameSuffix = "-minApi21"
      ...
    }
  }
}
...

מגניב

android {
  ...
  buildTypes {
    debug {...}
    release {...}
  }

  // Specifies the flavor dimensions you want to use. The order in which you
  // list the dimensions determines their priority, from highest to lowest,
  // when Gradle merges variant sources and configurations. You must assign
  // each product flavor you configure to one of the flavor dimensions.
  flavorDimensions "api", "mode"

  productFlavors {
    demo {
      // Assigns this product flavor to the "mode" flavor dimension.
      dimension "mode"
      ...
    }

    full {
      dimension "mode"
      ...
    }

    // Configurations in the "api" product flavors override those in "mode"
    // flavors and the defaultConfig block. Gradle determines the priority
    // between flavor dimensions based on the order in which they appear next
    // to the flavorDimensions property, with the first dimension having a higher
    // priority than the second, and so on.
    minApi24 {
      dimension "api"
      minSdkVersion 24
      // To ensure the target device receives the version of the app with
      // the highest compatible API level, assign version codes in increasing
      // value with API level.

      versionCode 30000 + android.defaultConfig.versionCode
      versionNameSuffix "-minApi24"
      ...
    }

    minApi23 {
      dimension "api"
      minSdkVersion 23
      versionCode 20000  + android.defaultConfig.versionCode
      versionNameSuffix "-minApi23"
      ...
    }

    minApi21 {
      dimension "api"
      minSdkVersion 21
      versionCode 10000  + android.defaultConfig.versionCode
      versionNameSuffix "-minApi21"
      ...
    }
  }
}
...

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

לפי תצורת ה-build הקודמת, Gradle יוצרת 12 וריאנטים של build עם סכימת השמות הבאה:

  • וריאנט build: ‏[minApi24, minApi23, minApi21][Demo, Full][Debug, Release]
  • קובץ ה-APK התואם: app-[minApi24, minApi23, minApi21]-[demo, full]-[debug, release].apk
  • לדוגמה,
    וריאנט build: minApi24DemoDebug
    קובץ APK תואם: app-minApi24-demo-debug.apk

בנוסף לספריות של קבוצות המקורות שאפשר ליצור לכל גרסת מוצר ווריאנט build, אפשר גם ליצור ספריות של קבוצות מקורות לכל שילוב של גרסאות מוצר. לדוגמה, אתם יכולים ליצור ולהוסיף מקורות Java לספרייה src/demoMinApi24/java/, ו-Gradle ישתמש במקורות האלה רק כשהוא יוצר וריאנט שמשלב את שתי גרסאות המוצר האלה.

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

סינון וריאציות

‫Gradle יוצר וריאנט build לכל שילוב אפשרי של גרסאות המוצר וסוגי ה-build שהגדרתם. עם זאת, יכול להיות שיהיו וריאנטים מסוימים של build שלא תצטרכו או שלא יהיו הגיוניים בהקשר של הפרויקט שלכם. כדי להסיר הגדרות מסוימות של וריאנט build, יוצרים מסנן וריאנטים בקובץ build.gradle.kts ברמת המודול.

נניח שאתם מתכננים לתמוך רק ברמות API‏ 23 ומעלה בגרסת ההדגמה של האפליקציה, ומשתמשים בתצורת ה-build מהקטע הקודם כדוגמה. אתם יכולים להשתמש בבלוק variantFilter כדי לסנן את כל תצורות וריאנט ה-build שמשלבות את גרסאות המוצר "minApi21" ו-"demo":

Kotlin

android {
  ...
  buildTypes {...}

  flavorDimensions += listOf("api", "mode")
  productFlavors {
    create("demo") {...}
    create("full") {...}
    create("minApi24") {...}
    create("minApi23") {...}
    create("minApi21") {...}
  }
}

androidComponents {
    beforeVariants { variantBuilder ->
        // To check for a certain build type, use variantBuilder.buildType == "<buildType>"
        if (variantBuilder.productFlavors.containsAll(listOf("api" to "minApi21", "mode" to "demo"))) {
            // Gradle ignores any variants that satisfy the conditions above.
            variantBuilder.enable = false
        }
    }
}
...

מגניב

android {
  ...
  buildTypes {...}

  flavorDimensions "api", "mode"
  productFlavors {
    demo {...}
    full {...}
    minApi24 {...}
    minApi23 {...}
    minApi21 {...}
  }

  variantFilter { variant ->
      def names = variant.flavors*.name
      // To check for a certain build type, use variant.buildType.name == "<buildType>"
      if (names.contains("minApi21") && names.contains("demo")) {
          // Gradle ignores any variants that satisfy the conditions above.
          setIgnore(true)
      }
  }
}
...

אחרי שמוסיפים מסנן וריאנטים לתצורת ה-build ולוחצים על Sync Now בסרגל ההתראות, Gradle מתעלם מכל וריאנט build שעומד בתנאים שציינתם. הווריאציות של ה-build לא מופיעות יותר בתפריט כשלוחצים על Build > Select Build Variant בסרגל התפריטים או על Build Variants בסרגל של חלון הכלי.

יצירת קבוצות מקורות

כברירת מחדל, Android Studio יוצר את main/ קבוצת המקורות והספריות לכל מה שרוצים לשתף בין כל וריאנט ה-build. עם זאת, אתם יכולים ליצור קבוצות מקורות חדשות כדי לקבוע בדיוק אילו קבצים Gradle יקמפל ויארוז לסוגי build ספציפיים, לגרסאות מוצר, לשילובים של גרסאות מוצר (כשמשתמשים בקבוצות גרסאות) ולווריאנטים של build.

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

‫Gradle מצפה שהקבצים והספריות של קבוצת המקורות יהיו מאורגנים בצורה מסוימת, בדומה לקבוצת המקורות main/. לדוגמה, Gradle מצפה שקובצי Kotlin או Java שספציפיים לסוג ה-build של "ניפוי באגים" יהיו בספריות src/debug/kotlin/ או src/debug/java/.

הפלאגין של Android Gradle מספק משימה שימושית של Gradle שמראה איך לארגן את הקבצים לכל אחד מסוגי ה-build, גרסאות המוצר והווריאנטים של ה-build. לדוגמה, הקטע הבא מהפלט של המשימה מתאר איפה Gradle מצפה למצוא קבצים מסוימים לסוג ה-build‏ debug:

------------------------------------------------------------
Project :app
------------------------------------------------------------

...

debug
----
Compile configuration: debugCompile
build.gradle name: android.sourceSets.debug
Java sources: [app/src/debug/java]
Kotlin sources: [app/src/debug/kotlin, app/src/debug/java]
Manifest file: app/src/debug/AndroidManifest.xml
Android resources: [app/src/debug/res]
Assets: [app/src/debug/assets]
AIDL sources: [app/src/debug/aidl]
RenderScript sources: [app/src/debug/rs]
JNI sources: [app/src/debug/jni]
JNI libraries: [app/src/debug/jniLibs]
Java-style resources: [app/src/debug/resources]

כדי לראות את הפלט הזה:

  1. לוחצים על Gradle בסרגל של חלון הכלי.
  2. עוברים אל MyApplication > Tasks > android ולוחצים לחיצה כפולה על sourceSets.

    כדי לראות את התיקייה Tasks, צריך לאפשר ל-Gradle ליצור את רשימת המשימות במהלך הסנכרון. לשם כך, בצע את הצעדים הבאים:

    1. לוחצים על File > Settings > Experimental (Android Studio > Settings > Experimental ב-macOS).
    2. מבטלים את הבחירה באפשרות Do not build Gradle task list during Gradle sync.
  3. אחרי ש-Gradle מריץ את המשימה, החלון Run נפתח ומוצג בו הפלט.

הערה: הפלט של המשימה גם מראה איך לארגן קבוצות מקורות לקבצים שבהם אתם רוצים להשתמש כדי להריץ בדיקות לאפליקציה, כמו קבוצות המקורות לבדיקה test/ ו-androidTest/.

כשיוצרים וריאנט build חדש, Android Studio לא יוצר את הספריות של קבוצת המקורות, אבל הוא נותן כמה אפשרויות שיעזרו לכם. לדוגמה, כדי ליצור רק את הספרייה java/ לסוג ה-build של "debug":

  1. פותחים את החלונית Project ובוחרים בתצוגה Project מהתפריט שבחלק העליון של החלונית.
  2. עוברים אל MyProject/app/src/.
  3. לוחצים לחיצה ימנית על הספרייה src ובוחרים באפשרות New > Directory.
  4. בתפריט שמתחת ל-Gradle Source Sets, בוחרים באפשרות full/java.
  5. מקישים על Enter.

‫Android Studio יוצר ספריית קבוצת מקורות לסוג ה-build לניפוי באגים, ואז יוצר את הספרייה java/ בתוכה. לחלופין, Android Studio יכול ליצור את הספריות בשבילכם כשאתם מוסיפים קובץ חדש לפרויקט בשביל וריאנט build ספציפי.

לדוגמה, כדי ליצור קובץ XML של ערכים לסוג ה-build של "ניפוי באגים":

  1. בחלונית Project, לוחצים לחיצה ימנית על הספרייה src ובוחרים באפשרות New > XML > Values XML File.
  2. מזינים את השם של קובץ ה-XML או משאירים את שם ברירת המחדל.
  3. בתפריט שלצד Target Source Set, בוחרים באפשרות debug.
  4. לוחצים על סיום.

מכיוון שסוג ה-build "ניפוי באגים" צוין כקבוצת המקורות לטירגוט, Android Studio יוצר באופן אוטומטי את הספריות הנדרשות כשהוא יוצר את קובץ ה-XML. מבנה הספרייה שנוצר נראה כמו באיור 1.

איור 1. ספריות חדשות של קבוצות מקורות לסוג ה-build "ניפוי באגים".

לקבוצות מקורות פעילות יש אינדיקטור ירוק בסמל שלהן שמראה שהן פעילות. קבוצת המקורות debug מסתיימת ב-[main] כדי לציין שהיא תמוזג עם קבוצת המקורות main.

באותו אופן, אפשר גם ליצור ספריות של קבוצות מקורות לגרסאות מוצר, כמו src/demo/, ולווריאנטים של build, כמו src/demoDebug/. בנוסף, אתם יכולים ליצור קבוצות מקורות לבדיקה שמטרגטות וריאנטים ספציפיים של build, כמו src/androidTestDemoDebug/. מידע נוסף זמין במאמר בדיקת קבוצות מקורות.

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

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

הבלוק sourceSets חייב להיות בתוך הבלוק android. אין צורך להעביר את קובצי המקור. צריך רק לספק ל-Gradle את הנתיבים, ביחס לקובץ build.gradle.kts ברמת המודול, שבהם Gradle יכול למצוא קבצים לכל רכיב של קבוצת מקורות. כדי לדעת אילו רכיבים אפשר להגדיר ואם אפשר למפות אותם לכמה קבצים או ספריות, עיינו במאמרי העזרה של ה-API של הפלאגין של Android Gradle.

דוגמת הקוד הבאה ממפה מקורות מהספרייה app/other/ לרכיבים מסוימים של קבוצת המקורות main ומשנה את תיקיית השורש של קבוצת המקורות androidTest:

Kotlin

android {
  ...
  // Encapsulates configurations for the main source set.
  sourceSets.getByName("main") {
    // Changes the directory for Java sources. The default directory is
    // 'src/main/java'.
    java.setSrcDirs(listOf("other/java"))

    // If you list multiple directories, Gradle uses all of them to collect
    // sources. Because Gradle gives these directories equal priority, if
    // you define the same resource in more than one directory, you receive an
    // error when merging resources. The default directory is 'src/main/res'.
    res.setSrcDirs(listOf("other/res1", "other/res2"))

    // Note: Avoid specifying a directory that is a parent to one
    // or more other directories you specify. For example, avoid the following:
    // res.srcDirs = ['other/res1', 'other/res1/layouts', 'other/res1/strings']
    // Specify either only the root 'other/res1' directory or only the
    // nested 'other/res1/layouts' and 'other/res1/strings' directories.

    // For each source set, you can specify only one Android manifest.
    // By default, Android Studio creates a manifest for your main source
    // set in the src/main/ directory.
    manifest.srcFile("other/AndroidManifest.xml")
    ...
  }

  // Create additional blocks to configure other source sets.
  sourceSets.getByName("androidTest") {
      // If all the files for a source set are located under a single root
      // directory, you can specify that directory using the setRoot property.
      // When gathering sources for the source set, Gradle looks only in locations
      // relative to the root directory you specify. For example, after applying the
      // configuration below for the androidTest source set, Gradle looks for Java
      // sources only in the src/tests/java/ directory.
      setRoot("src/tests")
      ...
  }
}
...

מגניב

android {
  ...
  sourceSets {
    // Encapsulates configurations for the main source set.
    main {
      // Changes the directory for Java sources. The default directory is
      // 'src/main/java'.
      java.srcDirs = ['other/java']

      // If you list multiple directories, Gradle uses all of them to collect
      // sources. Because Gradle gives these directories equal priority, if
      // you define the same resource in more than one directory, you receive an
      // error when merging resources. The default directory is 'src/main/res'.
      res.srcDirs = ['other/res1', 'other/res2']

      // Note: Avoid specifying a directory that is a parent to one
      // or more other directories you specify. For example, avoid the following:
      // res.srcDirs = ['other/res1', 'other/res1/layouts', 'other/res1/strings']
      // Specify either only the root 'other/res1' directory or only the
      // nested 'other/res1/layouts' and 'other/res1/strings' directories.

      // For each source set, you can specify only one Android manifest.
      // By default, Android Studio creates a manifest for your main source
      // set in the src/main/ directory.
      manifest.srcFile 'other/AndroidManifest.xml'
      ...
    }

    // Create additional blocks to configure other source sets.
    androidTest {

      // If all the files for a source set are located under a single root
      // directory, you can specify that directory using the setRoot property.
      // When gathering sources for the source set, Gradle looks only in locations
      // relative to the root directory you specify. For example, after applying the
      // configuration below for the androidTest source set, Gradle looks for Java
      // sources only in the src/tests/java/ directory.
      setRoot 'src/tests'
      ...
    }
  }
}
...

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

פיתוח באמצעות קבוצות מקורות

אפשר להשתמש בספריות של קבוצות מקורות כדי להכיל את הקוד והמשאבים שרוצים לארוז רק עם הגדרות מסוימות. לדוגמה, אם אתם יוצרים את וריאנט ה-build‏ "demoDebug", שהוא המכפלה הקרטזית של גרסת המוצר "demo" וסוג ה-build‏ "debug",‏ Gradle בודק את הספריות האלה ונותן להן את העדיפות הבאה:

  1. ‫src/demoDebug/ (קבוצת מקורות של וריאנט build)
  2. ‫src/debug/ (קבוצת מקורות של סוג ה-build)
  3. src/demo/ (קבוצת מקורות של גרסת המוצר)
  4. ‫src/main/ (קבוצת המקורות הראשית)

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

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

סדר העדיפויות קובע לאיזו קבוצת מקורות יש עדיפות גבוהה יותר כש-Gradle משלב קוד ומשאבים. סביר להניח שספריית קבוצת המקורות demoDebug/ מכילה קבצים שספציפיים לווריאנט ה-build הזה. לכן, אם demoDebug/ כוללת קובץ שמוגדר גם ב-debug/, Gradle משתמש בקובץ שבקבוצת המקורות demoDebug/. באופן דומה, Gradle נותן לקבצים בקבוצות המקור של סוג ה-build וגרסת המוצר עדיפות גבוהה יותר מאשר לאותם קבצים ב-main/. ‫Gradle מתייחס לסדר העדיפויות הזה כשהוא מחיל את כללי ה-build הבאים:

  • כל קוד המקור בספריות kotlin/ או java/ עובר קומפילציה ביחד כדי ליצור פלט אחד.

    הערה: עבור וריאנט build נתון, Gradle מציג שגיאת build אם הוא נתקל בשתי ספריות או יותר של קבוצות מקורות שהוגדרה בהן אותה מחלקה של Kotlin או Java. לדוגמה, כשיוצרים אפליקציה לניפוי באגים, אי אפשר להגדיר גם את src/debug/Utility.kt וגם את src/main/Utility.kt, כי Gradle בודק את שתי הספריות האלה במהלך תהליך היצירה ומציג את השגיאה "duplicate class". אם רוצים גרסאות שונות של Utility.kt לסוגים שונים של גרסאות build, צריך להגדיר בכל סוג גרסת build את הגרסה של הקובץ ולא לכלול אותו בקבוצת המקורות main/.

  • המניפסטים ממוזגים למניפסט אחד. העדיפות ניתנת באותו סדר כמו ברשימה שבדוגמה הקודמת. לדוגמה, הגדרות המניפסט של סוג גרסה מבטלות את הגדרות המניפסט של גרסת המוצר. מידע נוסף זמין במאמר בנושא מיזוג מניפסטים.
  • הקבצים בספריות values/ מוזגו. אם לשני קבצים יש אותו שם, למשל שני קובצי strings.xml, העדיפות ניתנת באותו סדר כמו ברשימה בדוגמה הקודמת. לדוגמה, ערכים שמוגדרים בקובץ בקבוצת המקורות של סוג ה-build מבטלים את הערכים שמוגדרים באותו קובץ בגרסת המוצר.
  • המשאבים בספריות res/ ו-asset/ נארזים יחד. אם יש משאבים עם אותו שם שמוגדרים בשתי קבוצות מקורות או יותר, העדיפות ניתנת באותו סדר כמו ברשימה שבדוגמה הקודמת.
  • ‫Gradle נותן למשאבים ולמניפסטים שנכללים ביחסי התלות של מודול הספרייה את העדיפות הנמוכה ביותר בזמן פיתוח האפליקציה.

הצהרה על יחסי תלות

כדי להגדיר תלות לווריאנט build ספציפי או לקבוצת מקורות לבדיקה, מוסיפים את השם של וריאנט ה-build או של קבוצת המקורות לבדיקה לפני מילת המפתח Implementation, כמו בדוגמה הבאה:

Kotlin

dependencies {
    // Adds the local "mylibrary" module as a dependency to the "free" flavor.
    "freeImplementation"(project(":mylibrary"))

    // Adds a remote binary dependency only for local tests.
    testImplementation("junit:junit:4.12")

    // Adds a remote binary dependency only for the instrumented test APK.
    androidTestImplementation("com.android.support.test.espresso:espresso-core:3.6.1")
}

מגניב

dependencies {
    // Adds the local "mylibrary" module as a dependency to the "free" flavor.
    freeImplementation project(":mylibrary")

    // Adds a remote binary dependency only for local tests.
    testImplementation 'junit:junit:4.12'

    // Adds a remote binary dependency only for the instrumented test APK.
    androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.6.1'
}

מידע נוסף על הגדרת יחסי תלות זמין במאמר הוספת יחסי תלות ב-build.

שימוש בניהול יחסי תלות שמותאם לווריאנטים

הפלאגין של Android Gradle בגרסה 3.0.0 ואילך כולל מנגנון תלות חדש שמתאים באופן אוטומטי וריאציות כשמשתמשים בספרייה. לדוגמה, גרסת debug של אפליקציה צורכת באופן אוטומטי גרסת debug של ספרייה. הוא פועל גם כשמשתמשים ב-flavors: וריאנט freeDebug של אפליקציה יצרוך וריאנט freeDebug של ספרייה.

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

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

Error:Failed to resolve: Could not resolve project :mylibrary.
Required by:
    project :app

פתרון שגיאות build שקשורות להתאמת וריאנטים

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

בהמשך מופיעה רשימה של בעיות שקשורות להתאמת תלויות לפי וריאנטים, ואיך לפתור אותן באמצעות מאפייני DSL:

  • האפליקציה כוללת סוג build שלא קיים ביחסי התלות של הספרייה.

    לדוגמה, האפליקציה כוללת סוג build של "staging", אבל יחסי התלות כוללים רק סוגי build של "debug" ו-"release".

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

    משתמשים ב-matchingFallbacks כדי לציין התאמות חלופיות לסוג build נתון, כמו שמוצג כאן:

    Kotlin

    // In the app's build.gradle.kts file.
    android {
        buildTypes {
            getByName("debug") {}
            getByName("release") {}
            create("staging") {
                // Specifies a sorted list of fallback build types that the
                // plugin can try to use when a dependency does not include a
                // "staging" build type. You may specify as many fallbacks as you
                // like, and the plugin selects the first build type that's
                // available in the dependency.
                matchingFallbacks += listOf("debug", "qa", "release")
            }
        }
    }

    מגניב

    // In the app's build.gradle file.
    android {
        buildTypes {
            debug {}
            release {}
            staging {
                // Specifies a sorted list of fallback build types that the
                // plugin can try to use when a dependency does not include a
                // "staging" build type. You may specify as many fallbacks as you
                // like, and the plugin selects the first build type that's
                // available in the dependency.
                matchingFallbacks = ['debug', 'qa', 'release']
            }
        }
    }
  • בקבוצת גרסאות מסוימת שקיימת גם באפליקציה וגם בספרייה שהאפליקציה תלויה בה, האפליקציה כוללת גרסאות שלא קיימות בספרייה.

    לדוגמה, גם האפליקציה וגם התלויות בספרייה שלה כוללות קבוצת גרסאות בשם "tier". עם זאת, המאפיין "tier" באפליקציה כולל את הטעמים "free" ו-"paid", אבל תלות כוללת רק את הטעמים "demo" ו-"paid" לאותו מאפיין.

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

    משתמשים ב-matchingFallbacks כדי לציין התאמות חלופיות לגרסת המוצר "החינמית" של האפליקציה, כמו שמוצג כאן:

    Kotlin

    // In the app's build.gradle.kts file.
    android {
        defaultConfig{
        // Don't configure matchingFallbacks in the defaultConfig block.
        // Instead, specify fallbacks for a given product flavor in the
        // productFlavors block, as shown below.
      }
        flavorDimensions += "tier"
        productFlavors {
            create("paid") {
                dimension = "tier"
                // Because the dependency already includes a "paid" flavor in its
                // "tier" dimension, you don't need to provide a list of fallbacks
                // for the "paid" flavor.
            }
            create("free") {
                dimension = "tier"
                // Specifies a sorted list of fallback flavors that the plugin
                // can try to use when a dependency's matching dimension does
                // not include a "free" flavor. Specify as many
                // fallbacks as you like; the plugin selects the first flavor
                // that's available in the dependency's "tier" dimension.
                matchingFallbacks += listOf("demo", "trial")
            }
        }
    }

    מגניב

    // In the app's build.gradle file.
    android {
        defaultConfig{
        // Don't configure matchingFallbacks in the defaultConfig block.
        // Instead, specify fallbacks for a given product flavor in the
        // productFlavors block, as shown below.
      }
        flavorDimensions 'tier'
        productFlavors {
            paid {
                dimension 'tier'
                // Because the dependency already includes a "paid" flavor in its
                // "tier" dimension, you don't need to provide a list of fallbacks
                // for the "paid" flavor.
            }
            free {
                dimension 'tier'
                // Specifies a sorted list of fallback flavors that the plugin
                // can try to use when a dependency's matching dimension does
                // not include a "free" flavor. Specify as many
                // fallbacks as you like; the plugin selects the first flavor
                // that's available in the dependency's "tier" dimension.
                matchingFallbacks = ['demo', 'trial']
            }
        }
    }
  • יחס תלות של ספרייה כולל קבוצת גרסאות שלא קיימת באפליקציה.

    לדוגמה, יחסי תלות של ספרייה כוללים וריאציות למאפיין minApi, אבל האפליקציה כוללת וריאציות רק למאפיין tier. כשרוצים ליצור את הגרסה "freeDebug" של האפליקציה, הפלאגין לא יודע אם להשתמש בגרסה "minApi23Debug" או בגרסה "minApi18Debug" של התלות.

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

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

    Kotlin

    // In the app's build.gradle.kts file.
    android {
        defaultConfig{
        // Specifies a sorted list of flavors that the plugin can try to use from
        // a given dimension. This tells the plugin to select the "minApi18" flavor
        // when encountering a dependency that includes a "minApi" dimension.
        // You can include additional flavor names to provide a
        // sorted list of fallbacks for the dimension.
        missingDimensionStrategy("minApi", "minApi18", "minApi23")
        // Specify a missingDimensionStrategy property for each
        // dimension that exists in a local dependency but not in your app.
        missingDimensionStrategy("abi", "x86", "arm64")
        }
        flavorDimensions += "tier"
        productFlavors {
            create("free") {
                dimension = "tier"
                // You can override the default selection at the product flavor
                // level by configuring another missingDimensionStrategy property
                // for the "minApi" dimension.
                missingDimensionStrategy("minApi", "minApi23", "minApi18")
            }
            create("paid") {}
        }
    }

    מגניב

    // In the app's build.gradle file.
    android {
        defaultConfig{
        // Specifies a sorted list of flavors that the plugin can try to use from
        // a given dimension. This tells the plugin to select the "minApi18" flavor
        // when encountering a dependency that includes a "minApi" dimension.
        // You can include additional flavor names to provide a
        // sorted list of fallbacks for the dimension.
        missingDimensionStrategy 'minApi', 'minApi18', 'minApi23'
        // Specify a missingDimensionStrategy property for each
        // dimension that exists in a local dependency but not in your app.
        missingDimensionStrategy 'abi', 'x86', 'arm64'
        }
        flavorDimensions 'tier'
        productFlavors {
            free {
                dimension 'tier'
                // You can override the default selection at the product flavor
                // level by configuring another missingDimensionStrategy property
                // for the 'minApi' dimension.
                missingDimensionStrategy 'minApi', 'minApi23', 'minApi18'
            }
            paid {}
        }
    }

מידע נוסף זמין במאמרים matchingFallbacks ו-missingDimensionStrategy במאמרי העזרה של DSL לפלאגין של Android Gradle.

קביעת הגדרות החתימה

‫Gradle לא חותם על ה-APK או ה-AAB של גרסת ה-build להפצה, אלא אם מגדירים במפורש הגדרת חתימה לגרסת ה-build הזו. אם עדיין אין לכם מפתח חתימה, צרו מפתח העלאה ומאגר מפתחות באמצעות Android Studio.

כדי להגדיר ידנית את הגדרות החתימה של סוג ה-build להפצה באמצעות הגדרות ה-build של Gradle:

  1. יוצרים מאגר מפתחות. מאגר מפתחות הוא קובץ בינארי שמכיל קבוצה של מפתחות פרטיים. אתם צריכים לשמור את מאגר המפתחות במקום בטוח ומאובטח.
  2. יוצרים מפתח פרטי. מפתח פרטי משמש לחתימה על האפליקציה לצורך הפצה, והוא אף פעם לא נכלל באפליקציה או נחשף לצדדים שלישיים לא מורשים.
  3. מוסיפים את הגדרת החתימה לקובץ build.gradle.kts ברמת המודול:

    Kotlin

    ...
    android {
        ...
        defaultConfig {...}
        signingConfigs {
            create("release") {
                storeFile = file("myreleasekey.keystore")
                storePassword = "password"
                keyAlias = "MyReleaseKey"
                keyPassword = "password"
            }
        }
        buildTypes {
            getByName("release") {
                ...
                signingConfig = signingConfigs.getByName("release")
            }
        }
    }

    מגניב

    ...
    android {
        ...
        defaultConfig {...}
        signingConfigs {
            release {
                storeFile file("myreleasekey.keystore")
                storePassword "password"
                keyAlias "MyReleaseKey"
                keyPassword "password"
            }
        }
        buildTypes {
            release {
                ...
                signingConfig signingConfigs.release
            }
        }
    }

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

כדי לקבל את הסיסמאות האלה ממשתני סביבה:

Kotlin

storePassword = System.getenv("KSTOREPWD")
keyPassword = System.getenv("KEYPWD")

מגניב

storePassword System.getenv("KSTOREPWD")
keyPassword System.getenv("KEYPWD")

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

אחרי שתסיימו את התהליך, תוכלו להפיץ את האפליקציה ולפרסם אותה ב-Google Play.

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

חתימה על אפליקציות ל-Wear OS

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