ساختن چند APK

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

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

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

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

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

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

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

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

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

قطعه کد زیر فهرست «رابط‌های دودویی کاربردی» را فقط روی x86 و x86_64 تنظیم می‌کند. این کار با فراخوانی reset() برای پاک کردن فهرست و سپس استفاده از 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() برای مشخص کردن فهرست دقیقی از رابط‌های دودویی برنامه استفاده کنید.

universalApk برای Groovy، یا isUniversalApk برای Kotlin script

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

مثال زیر یک APK جداگانه برای هر ABI تولید می‌کند: x86 و x86_64. این کار بااستفاده از reset() برای شروع با فهرست خالی از رابط‌های دودویی نرم‌افزار انجام می‌شود، و به‌دنبال آن include با فهرستی از رابط‌های دودویی نرم‌افزار که هرکدام یک 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
    }
  }
}

برای مشاهده فهرست رابط‌های دودویی نرم‌افزار پشتیبانی‌شده، به رابط‌های دودویی نرم‌افزار پشتیبانی‌شده مراجعه کنید.

پروژه‌های بدون کد بومی/C++‎

برای پروژه‌های بدون کد بومی/C++‎، پانل Build Variants (انواع ساخت) دو ستون دارد: Module (پیمانه) و Active Build Variant (نوع ساخت فعال)، همان‌طور که در شکل ۱ نشان داده شده است.

پانل «تنوع‌های ساخت» دو ستون برای پروژه‌های بدون کد بومی/C++‎ دارد.
شکل ۱. پانل Build Variants دو ستون برای پروژه‌های بدون کد بومی/C++‎ دارد.

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

پروژه‌های دارای کد بومی/C++‎

برای پروژه‌های دارای کد بومی/C++‎، پانل Build Variants (انواع ساخت) سه ستون دارد: Module (مدول)، Active Build Variant (نوع ساخت فعال)، و Active ABI (رابط دودویی برنامه فعال)، همان‌طور که در شکل ۲ نشان داده شده است.

پانل «انواع ساخت» ستون «میانای دودویی فعال» را برای پروژه‌های دارای کد بومی/C++‎ اضافه می‌کند.
شکل ۲. پانل انواع ساخت ستون ABI فعال را برای پروژه‌های دارای کد بومی/C++‎ اضافه می‌کند.

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

برای تغییر نوع ساخت یا «میانای دودویی کاربردی»:

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

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

پیکربندی نسخه‌سازی

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

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

در مثال زیر، APK برای x86 ABI، versionCode ۲۰۰۴ و x86_64 ABI، versionCode ۳۰۰۴ را دریافت می‌کند.

اختصاص دادن کدهای نسخه با افزایش‌های بزرگ، مانند ۱۰۰۰، به شما امکان می‌دهد بعداً کدهای نسخه منحصربه‌فردی را اختصاص دهید اگر نیاز به به‌روزرسانی برنامه خود داشته باشید. برای مثال، اگر defaultConfig.versionCode در به‌روزرسانی بعدی به ۵ تکرار شود، Gradle versionCode ۲۰۰۵ را به APK x86 و ۳۰۰۵ را به APK x86_64 اختصاص می‌دهد.

شیک

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]

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)

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 پیکربندی کردید، روی ساختن > ساختن APK کلیک کنید تا همه APKها برای واحدی که درحال‌حاضر در قاب پروژه انتخاب شده است ساخته شود. Gradle فایل‌های APK را برای هر ABI در فهرست راهنمای build/outputs/apk/ پروژه ایجاد می‌کند.

Gradle برای هر ABI که چند APK برای آن پیکربندی می‌کنید، یک APK می‌سازد.

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

شیک

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

کاتلین

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

خروجی پیکربندی نمونه شامل ۴ فایل APK زیر است:

  • app-X86-release.apk: حاوی کد و منابع برای x86 ABI است.
  • app-X86_64-release.apk: حاوی کد و منابع برای x86_64 ABI است.

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

قالب نام فایل APK

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

modulename-ABI-buildvariant.apk

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

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