چندین APK بسازید

احتیاط: از آگوست 2021، همه برنامه‌های جدید باید به‌عنوان App Bundle منتشر شوند. اگر برنامه خود را در Google Play منتشر می‌کنید، یک Android App Bundle بسازید و آپلود کنید. وقتی این کار را انجام می‌دهید، Google Play به‌طور خودکار فایل‌های APK بهینه‌شده را برای پیکربندی دستگاه هر کاربر تولید و ارائه می‌کند، بنابراین آنها فقط کد و منابعی را که برای اجرای برنامه شما نیاز دارند دانلود می‌کنند. اگر در فروشگاهی منتشر می کنید که قالب AAB را پشتیبانی نمی کند، انتشار چندین APK مفید است. در این صورت، باید هر APK را خودتان بسازید، امضا کنید و مدیریت کنید.

اگرچه بهتر است هر زمان که امکان دارد یک APK واحد بسازید تا از همه دستگاه‌های مورد نظر خود پشتیبانی کند، اما به دلیل پشتیبانی فایل‌هایی که از چند تراکم صفحه یا رابط‌های باینری برنامه (ABI) پشتیبانی می‌کنند، ممکن است یک APK بسیار بزرگ ایجاد کنید. یکی از راه‌های کاهش اندازه APK، ایجاد چندین APK است که حاوی فایل‌هایی برای تراکم صفحه نمایش یا ABI‌های خاص هستند.

Gradle می‌تواند فایل‌های APK جداگانه ایجاد کند که فقط حاوی کد و منابع خاص برای هر تراکم یا ABI هستند. این صفحه نحوه پیکربندی ساخت خود را برای تولید چندین APK توضیح می‌دهد. اگر می‌خواهید نسخه‌های مختلفی از برنامه خود را ایجاد کنید که بر اساس تراکم صفحه یا ABI نیستند، به جای آن از انواع ساخت استفاده کنید.

ساخت خود را برای چندین APK پیکربندی کنید

برای پیکربندی ساخت خود برای چندین APK، یک بلوک splits به فایل build.gradle در سطح ماژول خود اضافه کنید. در بلوک splits ، یک بلوک density ارائه کنید که مشخص می‌کند چگونه می‌خواهید Gradle فایل‌های APK با تراکم تولید کند یا یک بلوک abi که مشخص می‌کند چگونه می‌خواهید Gradle فایل‌های APK در هر ABI را تولید کند. شما می توانید هر دو بلوک تراکم و ABI را ارائه دهید و سیستم ساخت یک APK برای هر تراکم و ترکیب ABI ایجاد می کند.

چندین APK را برای تراکم صفحه پیکربندی کنید

برای ایجاد فایل‌های APK مجزا برای تراکم‌های صفحه‌نمایش مختلف، یک بلوک density داخل بلوک splits خود اضافه کنید. در بلوک density خود، فهرستی از تراکم صفحه نمایش دلخواه و اندازه صفحه نمایش سازگار را ارائه دهید. فقط در صورتی از فهرست اندازه‌های صفحه سازگار استفاده کنید که به عناصر <compatible-screens> خاصی در مانیفست هر APK نیاز دارید.

گزینه های Gradle DSL زیر برای پیکربندی چندین APK برای تراکم صفحه استفاده می شود:

enable برای Groovy، برای اسکریپت Kotlin isEnable
اگر این عنصر را روی true تنظیم کنید، Gradle چندین APK را بر اساس تراکم صفحه‌ای که شما تعریف می‌کنید تولید می‌کند. مقدار پیش فرض false است.
exclude
فهرستی از تراکم‌های جدا شده با کاما را مشخص می‌کند که نمی‌خواهید Gradle فایل‌های APK جداگانه برای آن‌ها ایجاد کند. اگر می‌خواهید برای بیشتر تراکم‌ها فایل‌های APK ایجاد کنید، اما باید چند تراکم را که برنامه شما پشتیبانی نمی‌کند حذف کنید، از exclude استفاده کنید.
reset()

لیست پیش فرض تراکم صفحه نمایش را پاک می کند. فقط زمانی استفاده کنید که با عنصر include ترکیب شود تا چگالی هایی را که می خواهید اضافه کنید مشخص کنید.

قطعه زیر لیست چگالی ها را با فراخوانی reset() برای پاک کردن لیست و سپس با استفاده از include روی ldpi و xxhdpi تنظیم می کند:

reset()                  // Clears the default list from all densities
                         // to no densities.
include "ldpi", "xxhdpi" // Specifies the two densities to generate APKs
                         // for.
include
فهرستی از تراکم‌های جدا شده با کاما را مشخص می‌کند که می‌خواهید Gradle فایل‌های APK را برای آن‌ها تولید کند. فقط در ترکیب با reset() برای مشخص کردن لیست دقیقی از چگالی استفاده کنید.
compatibleScreens

فهرستی از اندازه های صفحه سازگار با کاما را مشخص می کند. این یک گره <compatible-screens> منطبق را در مانیفست برای هر APK تزریق می کند.

این تنظیم راهی مناسب برای مدیریت تراکم و اندازه صفحه نمایش در یک بخش build.gradle فراهم می کند. با این حال، استفاده از <compatible-screens> می تواند انواع دستگاهی را که برنامه شما با آن کار می کند محدود کند. برای روش‌های جایگزین برای پشتیبانی از اندازه‌های مختلف صفحه، به نمای کلی سازگاری صفحه‌نمایش مراجعه کنید.

از آنجا که هر APK مبتنی بر تراکم صفحه شامل یک برچسب <compatible-screens> با محدودیت‌های خاص در مورد نوع صفحه‌نمایش پشتیبانی‌شده APK است—حتی اگر چندین APK منتشر کنید—برخی دستگاه‌های جدید با فیلترهای APK متعدد شما مطابقت ندارند. به این ترتیب، Gradle همیشه یک APK جهانی اضافی ایجاد می‌کند که حاوی دارایی‌هایی برای همه تراکم‌های صفحه است و شامل برچسب <compatible-screens> نمی‌شود. این APK جهانی را به همراه فایل‌های APK با تراکم خود منتشر کنید تا برای دستگاه‌هایی که با برچسب <compatible-screens> با APK مطابقت ندارند، نسخه‌ای جایگزین ارائه کنید.

مثال زیر یک APK جداگانه برای هر تراکم صفحه به جز ldpi ، xxhdpi و xxxhdpi ایجاد می‌کند. این کار با استفاده از exclude برای حذف آن سه تراکم از لیست پیش‌فرض همه تراکم‌ها انجام می‌شود.

شیار

android {
  ...
  splits {

    // Configures multiple APKs based on screen density.
    density {

      // Configures multiple APKs based on screen density.
      enable true

      // Specifies a list of screen densities you don't want Gradle to create multiple APKs for.
      exclude "ldpi", "xxhdpi", "xxxhdpi"

      // Specifies a list of compatible screen size settings for the manifest.
      compatibleScreens 'small', 'normal', 'large', 'xlarge'
    }
  }
}

کاتلین

android {
    ...
    splits {

        // Configures multiple APKs based on screen density.
        density {

            // Configures multiple APKs based on screen density.
            isEnable = true

            // Specifies a list of screen densities you don't want Gradle to create multiple APKs for.
            exclude("ldpi", "xxhdpi", "xxxhdpi")

            // Specifies a list of compatible screen size settings for the manifest.
            compatibleScreens("small", "normal", "large", "xlarge")
        }
    }
}

برای جزئیات بیشتر در مورد سفارشی کردن انواع مختلف ساخت برنامه خود برای انواع صفحه نمایش و دستگاه های خاص، به اعلام پشتیبانی صفحه نمایش محدود شده مراجعه کنید.

چندین APK را برای ABI پیکربندی کنید

برای ایجاد APK جداگانه برای ABI های مختلف، یک بلوک abi را در بلوک splits خود اضافه کنید. در بلوک abi خود، فهرستی از ABI های مورد نظر را ارائه دهید.

گزینه های Gradle DSL زیر برای پیکربندی چندین APK در هر ABI استفاده می شود:

برای Groovy یا برای اسکریپت کاتلین isEnable enable
اگر این عنصر را روی true تنظیم کنید، Gradle چندین APK را بر اساس ABI هایی که شما تعریف کرده اید تولید می کند. مقدار پیش فرض false است.
exclude
فهرستی از ABI‌های جدا شده با کاما را مشخص می‌کند که نمی‌خواهید Gradle فایل‌های APK جداگانه برای آن‌ها ایجاد کند. اگر می‌خواهید برای بیشتر ABI‌ها APK ایجاد کنید، exclude استفاده کنید، اما باید چند ABI را که برنامه شما پشتیبانی نمی‌کند حذف کنید.
reset()

لیست پیش فرض ABI ها را پاک می کند. فقط زمانی استفاده کنید که با عنصر include ترکیب شود تا ABI هایی را که می خواهید اضافه کنید مشخص کنید.

قطعه زیر با فراخوانی reset() برای پاک کردن لیست، لیست ABI ها را فقط به x86 و x86_64 تنظیم می کند و سپس از include استفاده می کند:

reset()                 // Clears the default list from all ABIs to no ABIs.
include "x86", "x86_64" // Specifies the two ABIs we want to generate APKs for.
include
فهرستی از ABI های جدا شده با کاما را مشخص می کند که می خواهید Gradle برای آنها APK ایجاد کند. فقط در ترکیب با reset() برای تعیین لیست دقیق ABI ها استفاده کنید.
universalApk برای Groovy یا isUniversalApk برای اسکریپت Kotlin

اگر true ، Gradle یک APK جهانی علاوه بر APKهای هر ABI ایجاد می‌کند. یک APK جهانی حاوی کد و منابع برای همه ABI ها در یک APK واحد است. مقدار پیش فرض false است.

توجه داشته باشید که این گزینه فقط در بلوک splits.abi موجود است. هنگام ساخت چندین APK بر اساس تراکم صفحه، Gradle همیشه یک APK جهانی تولید می‌کند که حاوی کد و منابع برای همه تراکم‌های صفحه است.

مثال زیر یک APK جداگانه برای هر ABI ایجاد می کند: x86 و x86_64 . این کار با استفاده از reset() برای شروع با یک لیست خالی از ABI ها، به دنبال include لیستی از ABI ها که هر کدام یک APK دریافت می کنند، انجام می شود.

شیار

android {
  ...
  splits {

    // Configures multiple APKs based on ABI.
    abi {

      // Enables building multiple APKs per ABI.
      enable true

      // By default all ABIs are included, so use reset() and include to specify that you only
      // want APKs for x86 and x86_64.

      // Resets the list of ABIs for Gradle to create APKs for to none.
      reset()

      // Specifies a list of ABIs for Gradle to create APKs for.
      include "x86", "x86_64"

      // Specifies that you don't want to also generate a universal APK that includes all ABIs.
      universalApk false
    }
  }
}

کاتلین

android {
  ...
  splits {

    // Configures multiple APKs based on ABI.
    abi {

      // Enables building multiple APKs per ABI.
      isEnable = true

      // By default all ABIs are included, so use reset() and include to specify that you only
      // want APKs for x86 and x86_64.

      // Resets the list of ABIs for Gradle to create APKs for to none.
      reset()

      // Specifies a list of ABIs for Gradle to create APKs for.
      include("x86", "x86_64")

      // Specifies that you don't want to also generate a universal APK that includes all ABIs.
      isUniversalApk = false
    }
  }
}

برای فهرستی از ABI های پشتیبانی شده، ABI های پشتیبانی شده را ببینید.

پروژه های بدون کد Native/C++

برای پروژه‌های بدون کد Native/C++، پنل Build Variants دارای دو ستون است: Module و Active Build Variant ، همانطور که در شکل 1 نشان داده شده است.

پنل Build variants
شکل 1. پانل Build Variants دارای دو ستون برای پروژه های بدون کد Native/C++ است.

مقدار Active Build Variant برای ماژول، نوع ساختی را که در ویرایشگر مستقر و قابل مشاهده است، تعیین می کند. برای جابجایی بین انواع، روی سلول Active Build Variant برای یک ماژول کلیک کنید و نوع مورد نظر را از قسمت لیست انتخاب کنید.

پروژه هایی با کد Native/C++

برای پروژه‌هایی با کد Native/C++، پنل Build Variants دارای سه ستون است: Module ، Active Build Variant و Active ABI ، همانطور که در شکل 2 نشان داده شده است.

شکل 2. پنل Build Variants ستون Active ABI را برای پروژه هایی با کد Native/C++ اضافه می کند.

مقدار Active Build Variant برای ماژول، نوع ساختی را تعیین می کند که مستقر شده و در ویرایشگر قابل مشاهده است. برای ماژول‌های بومی، مقدار Active ABI ABI مورد استفاده ویرایشگر را تعیین می‌کند، اما بر آنچه که مستقر می‌شود تأثیری نمی‌گذارد.

برای تغییر نوع ساخت یا ABI:

  1. روی سلول مربوط به ستون Active Build Variant یا Active ABI کلیک کنید.
  2. نوع مورد نظر یا ABI را از قسمت لیست انتخاب کنید. یک همگام سازی جدید به طور خودکار اجرا می شود.

تغییر هر یک از ستون ها برای یک برنامه یا ماژول کتابخانه، این تغییر را در تمام ردیف های وابسته اعمال می کند.

پیکربندی نسخه

به طور پیش‌فرض، وقتی Gradle چندین APK تولید می‌کند، هر APK همان اطلاعات نسخه را دارد، همانطور که در فایل build.gradle یا build.gradle.kts در سطح ماژول مشخص شده است. از آنجایی که فروشگاه Google Play چندین APK را برای یک برنامه که همه اطلاعات نسخه یکسانی دارند، مجاز نمی‌داند، قبل از آپلود در فروشگاه Play باید مطمئن شوید که هر APK یک versionCode منحصر به فرد دارد.

می‌توانید فایل build.gradle در سطح ماژول خود را پیکربندی کنید تا versionCode برای هر APK لغو کند. با ایجاد نقشه ای که یک مقدار عددی منحصر به فرد را برای هر ABI و چگالی که چندین APK را برای آن پیکربندی می کنید، اختصاص می دهد، می توانید کد نسخه خروجی را با مقداری که کد نسخه تعریف شده در بلوک defaultConfig یا productFlavors را با مقدار عددی اختصاص داده شده به آن ترکیب می کند، لغو کنید. چگالی یا ABI.

در مثال زیر، APK برای x86 ABI versionCode 2004 و x86_64 ABI versionCode کد 3004 دریافت می کند.

تخصیص کدهای نسخه به صورت افزایشی، مانند 1000، به شما این امکان را می دهد که بعداً در صورت نیاز به به روز رسانی برنامه، کدهای نسخه منحصر به فرد را اختصاص دهید. برای مثال، اگر defaultConfig.versionCode در یک به‌روزرسانی بعدی به عدد 5 تکرار شود، Gradle versionCode 2005 را به x86 APK و 3005 را به x86_64 APK اختصاص می‌دهد.

نکته: اگر ساخت شما دارای یک APK جهانی است، versionCode را به آن اختصاص دهید که کمتر از هر یک از APK های دیگر شما باشد. از آنجایی که Google Play Store نسخه‌ای از برنامه شما را نصب می‌کند که هم با دستگاه مورد نظر سازگار است و هم دارای بالاترین versionCode است، اختصاص versionCode کمتر نسخه به APK جهانی تضمین می‌کند که فروشگاه Google Play سعی می‌کند یکی از APK‌های شما را قبل از بازگشت به نسخه جهانی نصب کند. APK. کد نمونه زیر با نادیده نگرفتن versionCode پیش‌فرض یک APK جهانی با این مشکل مقابله می‌کند.

شیار

android {
  ...
  defaultConfig {
    ...
    versionCode 4
  }
  splits {
    ...
  }
}

// Map for the version code that gives each ABI a value.
ext.abiCodes = ['armeabi-v7a':1, x86:2, x86_64:3]

// For per-density APKs, create a similar map:
// ext.densityCodes = ['mdpi': 1, 'hdpi': 2, 'xhdpi': 3]

import com.android.build.OutputFile

// For each APK output variant, override versionCode with a combination of
// ext.abiCodes * 1000 + variant.versionCode. In this example, variant.versionCode
// is equal to defaultConfig.versionCode. If you configure product flavors that
// define their own versionCode, variant.versionCode uses that value instead.
android.applicationVariants.all { variant ->

  // Assigns a different version code for each output APK
  // other than the universal APK.
  variant.outputs.each { output ->

    // Stores the value of ext.abiCodes that is associated with the ABI for this variant.
    def baseAbiVersionCode =
            // Determines the ABI for this variant and returns the mapped value.
            project.ext.abiCodes.get(output.getFilter(OutputFile.ABI))

    // Because abiCodes.get() returns null for ABIs that are not mapped by ext.abiCodes,
    // the following code doesn't override the version code for universal APKs.
    // However, because you want universal APKs to have the lowest version code,
    // this outcome is desirable.
    if (baseAbiVersionCode != null) {

      // Assigns the new version code to versionCodeOverride, which changes the
      // version code for only the output APK, not for the variant itself. Skipping
      // this step causes Gradle to use the value of variant.versionCode for the APK.
      output.versionCodeOverride =
              baseAbiVersionCode * 1000 + variant.versionCode
    }
  }
}

کاتلین

android {
  ...
  defaultConfig {
    ...
    versionCode = 4
  }
  splits {
    ...
  }
}

// Map for the version code that gives each ABI a value.
val abiCodes = mapOf("armeabi-v7a" to 1, "x86" to 2, "x86_64" to 3)

// For per-density APKs, create a similar map:
// val densityCodes = mapOf("mdpi" to 1, "hdpi" to 2, "xhdpi" to 3)

import com.android.build.api.variant.FilterConfiguration.FilterType.*

// For each APK output variant, override versionCode with a combination of
// abiCodes * 1000 + variant.versionCode. In this example, variant.versionCode
// is equal to defaultConfig.versionCode. If you configure product flavors that
// define their own versionCode, variant.versionCode uses that value instead.
androidComponents {
    onVariants { variant ->

        // Assigns a different version code for each output APK
        // other than the universal APK.
        variant.outputs.forEach { output ->
            val name = output.filters.find { it.filterType == ABI }?.identifier

            // Stores the value of abiCodes that is associated with the ABI for this variant.
            val baseAbiCode = abiCodes[name]
            // Because abiCodes.get() returns null for ABIs that are not mapped by ext.abiCodes,
            // the following code doesn't override the version code for universal APKs.
            // However, because you want universal APKs to have the lowest version code,
            // this outcome is desirable.
            if (baseAbiCode != null) {
                // Assigns the new version code to output.versionCode, which changes the version code
                // for only the output APK, not for the variant itself.
                output.versionCode.set(baseAbiCode * 1000 + (output.versionCode.get() ?: 0))
            }
        }
    }
}

برای مثال‌های بیشتر از طرح‌های کد نسخه جایگزین، به اختصاص کدهای نسخه مراجعه کنید.

چندین APK بسازید

هنگامی که فایل build.gradle یا build.gradle.kts در سطح ماژول خود را برای ساخت چندین APK پیکربندی کردید، روی Build > Build APK کلیک کنید تا همه APK ها برای ماژول انتخابی فعلی در پنجره پروژه ساخته شوند. Gradle فایل‌های APK را برای هر تراکم یا ABI در دایرکتوری build/outputs/apk/ پروژه ایجاد می‌کند.

Gradle برای هر تراکم یا ABI که چندین APK را برای آن پیکربندی می‌کنید، یک APK می‌سازد. اگر چندین APK را برای تراکم و ABI فعال کنید، Gradle برای هر تراکم و ترکیب ABI یک APK ایجاد می‌کند.

به عنوان مثال، قطعه build.gradle زیر ساخت چندین APK را برای تراکم mdpi و hdpi و همچنین x86 و x86_64 ABI را قادر می‌سازد:

شیار

...
  splits {
    density {
      enable true
      reset()
      include "mdpi", "hdpi"
    }
    abi {
      enable true
      reset()
      include "x86", "x86_64"
    }
  }

کاتلین

...
  splits {
    density {
      isEnable = true
      reset()
      include("mdpi", "hdpi")
    }
    abi {
      isEnable = true
      reset()
      include("x86", "x86_64")
    }
  }

خروجی از پیکربندی مثال شامل 4 APK زیر است:

  • app-hdpiX86-release.apk : حاوی کد و منابعی برای تراکم hdpi و x86 ABI است.
  • app-hdpiX86_64-release.apk : حاوی کد و منابعی برای چگالی hdpi و x86_64 ABI است.
  • app-mdpiX86-release.apk : حاوی کد و منابعی برای تراکم mdpi و x86 ABI است.
  • app-mdpiX86_64-release.apk : حاوی کد و منابعی برای تراکم mdpi و x86_64 ABI است.

هنگام ساخت چندین APK بر اساس تراکم صفحه، Gradle همیشه یک APK جهانی ایجاد می‌کند که شامل کد و منابع برای همه تراکم‌ها، علاوه بر APK‌های هر تراکم است.

هنگام ساختن چندین APK بر اساس ABI، Gradle تنها در صورتی یک APK ایجاد می‌کند که شامل کد و منابع برای همه ABI باشد، در صورتی که universalApk true در بلوک splits.abi در فایل build.gradle خود (برای Groovy) یا isUniversalApk = true در splits.abi بلوک splits.abi در فایل build.gradle.kts شما (برای اسکریپت Kotlin).

فرمت نام فایل APK

هنگام ساخت چندین APK، Gradle نام فایل‌های APK را با استفاده از طرح زیر تولید می‌کند:

modulename - screendensity ABI - buildvariant .apk

اجزای طرح عبارتند از:

modulename
نام ماژول در حال ساخت را مشخص می کند.
screendensity
اگر چند APK برای تراکم صفحه فعال باشد، تراکم صفحه APK را مشخص می‌کند، مانند mdpi .
ABI

اگر چند APK برای ABI فعال باشد، ABI را برای APK مشخص می‌کند، مانند x86 .

اگر چندین APK برای تراکم صفحه و ABI فعال باشد، Gradle نام تراکم را با نام ABI، به عنوان مثال mdpiX86 ، الحاق می‌کند. اگر universalApk برای هر APK ABI فعال باشد، Gradle از universal به عنوان بخش ABI نام فایل APK جهانی استفاده می‌کند.

buildvariant
نوع ساخت در حال ساخت را مشخص می کند، مانند debug .

برای مثال، هنگام ساختن APK با تراکم صفحه نمایش mdpi برای نسخه اشکال زدایی myApp، نام فایل APK myApp-mdpi-debug.apk است. نسخه منتشر شده myApp که برای ساخت چندین APK برای تراکم صفحه mdpi و x86 ABI پیکربندی شده است، نام فایل APK myApp-mdpiX86-release.apk دارد.