বিল্ড ভেরিয়েন্ট কনফিগার করুন

এই পৃষ্ঠায় দেখানো হয়েছে কীভাবে আপনি একটিমাত্র প্রজেক্ট থেকে আপনার অ্যাপের বিভিন্ন সংস্করণ তৈরি করার জন্য বিল্ড ভ্যারিয়েন্ট কনফিগার করতে পারেন এবং কীভাবে আপনার ডিপেন্ডেন্সি ও সাইনিং কনফিগারেশনগুলো সঠিকভাবে পরিচালনা করতে পারেন।

প্রতিটি বিল্ড ভ্যারিয়েন্ট আপনার অ্যাপের একটি ভিন্ন সংস্করণকে বোঝায়, যা আপনি তৈরি করতে পারেন। উদাহরণস্বরূপ, আপনি আপনার অ্যাপের একটি সংস্করণ বিনামূল্যে এবং সীমিত কন্টেন্ট সহ তৈরি করতে চাইতে পারেন, এবং আরেকটি পেইড সংস্করণ তৈরি করতে পারেন যাতে আরও বেশি কিছু অন্তর্ভুক্ত থাকে। এছাড়াও, আপনি এপিআই লেভেল বা ডিভাইসের অন্যান্য ভিন্নতার উপর ভিত্তি করে বিভিন্ন ডিভাইসকে লক্ষ্য করে আপনার অ্যাপের বিভিন্ন সংস্করণ তৈরি করতে পারেন।

আপনার বিল্ড টাইপ এবং প্রোডাক্ট ফ্লেভারে কনফিগার করা সেটিংস, কোড এবং রিসোর্সগুলোকে একত্রিত করার জন্য গ্রেডল (Gradle) কর্তৃক ব্যবহৃত কিছু নির্দিষ্ট নিয়মের ফলস্বরূপ বিল্ড ভ্যারিয়েন্ট তৈরি হয়। যদিও আপনি সরাসরি বিল্ড ভ্যারিয়েন্ট কনফিগার করেন না, তবে যে বিল্ড টাইপ এবং প্রোডাক্ট ফ্লেভারগুলো দিয়ে এগুলো গঠিত হয়, সেগুলো আপনি কনফিগার করে থাকেন।

উদাহরণস্বরূপ, একটি 'ডেমো' প্রোডাক্ট ফ্লেভার নির্দিষ্ট কিছু ফিচার এবং ডিভাইসের প্রয়োজনীয়তা উল্লেখ করতে পারে, যেমন কাস্টম সোর্স কোড, রিসোর্স এবং ন্যূনতম এপিআই লেভেল, অন্যদিকে 'ডিবাগ' বিল্ড টাইপ ভিন্ন বিল্ড ও প্যাকেজিং সেটিংস প্রয়োগ করে, যেমন ডিবাগ অপশন এবং সাইনিং কী। যে বিল্ড ভ্যারিয়েন্টটি এই দুটিকে একত্রিত করে, সেটিই হলো আপনার অ্যাপের 'ডেমোডিবাগ' সংস্করণ, এবং এতে 'ডেমো' প্রোডাক্ট ফ্লেভার, 'ডিবাগ' বিল্ড টাইপ এবং main/ সোর্স সেটে অন্তর্ভুক্ত কনফিগারেশন ও রিসোর্সগুলোর একটি সংমিশ্রণ থাকে।

বিল্ড প্রকার কনফিগার করুন

আপনি মডিউল-স্তরের build.gradle.kts ফাইলের android ব্লকের ভিতরে বিল্ড টাইপ তৈরি এবং কনফিগার করতে পারেন। যখন আপনি একটি নতুন মডিউল তৈরি করেন, অ্যান্ড্রয়েড স্টুডিও স্বয়ংক্রিয়ভাবে ডিবাগ এবং রিলিজ বিল্ড টাইপ তৈরি করে। যদিও ডিবাগ বিল্ড টাইপটি বিল্ড কনফিগারেশন ফাইলে দেখা যায় না, অ্যান্ড্রয়েড স্টুডিও এটিকে debuggable true দিয়ে কনফিগার করে। এটি আপনাকে সুরক্ষিত অ্যান্ড্রয়েড ডিভাইসে অ্যাপটি ডিবাগ করতে দেয় এবং একটি জেনেরিক ডিবাগ কীস্টোর দিয়ে অ্যাপ সাইনিং কনফিগার করে।

আপনি যদি নির্দিষ্ট কিছু সেটিংস যোগ বা পরিবর্তন করতে চান, তাহলে আপনার কনফিগারেশনে ডিবাগ বিল্ড টাইপটি যোগ করতে পারেন। নিম্নলিখিত নমুনাটিতে ডিবাগ বিল্ড টাইপের জন্য একটি applicationIdSuffix নির্দিষ্ট করা হয়েছে এবং একটি "স্টেজিং" বিল্ড টাইপ কনফিগার করা হয়েছে, যা ডিবাগ বিল্ড টাইপের সেটিংস ব্যবহার করে ইনিশিয়ালাইজ করা হয়:

কোটলিন

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"
        }
    }
}

দ্রষ্টব্য: আপনি যখন কোনো বিল্ড কনফিগারেশন ফাইলে পরিবর্তন করেন, তখন অ্যান্ড্রয়েড স্টুডিও চায় যে আপনি আপনার প্রজেক্টটিকে নতুন কনফিগারেশনের সাথে সিঙ্ক করুন। আপনার প্রজেক্ট সিঙ্ক করতে, কোনো পরিবর্তন করার পর যে নোটিফিকেশন বারটি আসে, সেখানে থাকা ‘Sync Now’- তে ক্লিক করুন অথবা ‘Sync Project’-এ ক্লিক করুন। টুলবার থেকে। অ্যান্ড্রয়েড স্টুডিও আপনার কনফিগারেশনে কোনো ত্রুটি লক্ষ্য করলে, সমস্যাটি বর্ণনা করার জন্য মেসেজেস উইন্ডোটি প্রদর্শিত হয়।

বিল্ড টাইপের মাধ্যমে আপনি যেসব প্রোপার্টি কনফিগার করতে পারেন, সে সম্পর্কে আরও জানতে BuildType রেফারেন্সটি পড়ুন।

পণ্যের ফ্লেভার কনফিগার করুন

প্রোডাক্ট ফ্লেভার তৈরি করা বিল্ড টাইপ তৈরির মতোই। আপনার বিল্ড কনফিগারেশনের productFlavors ব্লকে প্রোডাক্ট ফ্লেভার যোগ করুন এবং আপনার পছন্দের সেটিংস অন্তর্ভুক্ত করুন। প্রোডাক্ট ফ্লেভারগুলো defaultConfig মতোই একই প্রোপার্টি সমর্থন করে, কারণ defaultConfig আসলে ProductFlavor ক্লাসের অন্তর্গত। এর মানে হলো, আপনি defaultConfig ব্লকে সমস্ত ফ্লেভারের জন্য বেস কনফিগারেশন প্রদান করতে পারেন, এবং প্রতিটি ফ্লেভার এই ডিফল্ট মানগুলোর যেকোনোটি পরিবর্তন করতে পারে, যেমন applicationId । অ্যাপ্লিকেশন আইডি সম্পর্কে আরও জানতে, "অ্যাপ্লিকেশন আইডি সেট করুন" পড়ুন।

দ্রষ্টব্য: আপনাকে এখনও main/ ম্যানিফেস্ট ফাইলে package অ্যাট্রিবিউট ব্যবহার করে একটি প্যাকেজ নাম নির্দিষ্ট করতে হবে। R ক্লাসকে নির্দেশ করতে বা যেকোনো সম্পর্কিত অ্যাক্টিভিটি বা সার্ভিস রেজিস্ট্রেশন সমাধান করতে আপনাকে অবশ্যই আপনার সোর্স কোডে সেই প্যাকেজ নামটি ব্যবহার করতে হবে। এটি আপনাকে আপনার সোর্স কোড পরিবর্তন না করেই প্যাকেজিং এবং বিতরণের জন্য প্রতিটি প্রোডাক্ট ফ্লেভারকে একটি অনন্য আইডি দিতে applicationId ব্যবহার করার সুযোগ দেয়।

সমস্ত ফ্লেভারকে অবশ্যই একটি নামযুক্ত ফ্লেভার ডাইমেনশনের অন্তর্ভুক্ত হতে হবে, যা হলো প্রোডাক্ট ফ্লেভারগুলোর একটি গ্রুপ। আপনাকে অবশ্যই সমস্ত ফ্লেভারকে একটি ফ্লেভার ডাইমেনশনে অ্যাসাইন করতে হবে; অন্যথায়, আপনি নিম্নলিখিত বিল্ড এররটি পাবেন।

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

যদি কোনো নির্দিষ্ট মডিউল কেবল একটি ফ্লেভার ডাইমেনশন উল্লেখ করে, তাহলে অ্যান্ড্রয়েড গ্রেডল প্লাগইন স্বয়ংক্রিয়ভাবে মডিউলটির সমস্ত ফ্লেভারকে সেই ডাইমেনশনে বরাদ্দ করে দেয়।

নিম্নলিখিত কোড নমুনাটি 'version' নামে একটি ফ্লেভার ডাইমেনশন তৈরি করে এবং 'demo' ও 'full' প্রোডাক্ট ফ্লেভার যোগ করে। এই ফ্লেভারগুলি তাদের নিজস্ব applicationIdSuffix এবং versionNameSuffix প্রদান করে:

কোটলিন

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"
        }
    }
}

দ্রষ্টব্য: আপনার যদি একটি লিগ্যাসি অ্যাপ (আগস্ট ২০২১-এর আগে তৈরি) থাকে যা আপনি গুগল প্লে-তে APK ব্যবহার করে বিতরণ করেন, তাহলে গুগল প্লে-তে একাধিক APK সাপোর্টের মাধ্যমে আপনার অ্যাপটি বিতরণ করতে, সমস্ত ভ্যারিয়েন্টের জন্য একই applicationId ভ্যালু অ্যাসাইন করুন এবং প্রতিটি ভ্যারিয়েন্টকে একটি ভিন্ন versionCode দিন। গুগল প্লে-তে আপনার অ্যাপের বিভিন্ন ভ্যারিয়েন্টকে আলাদা অ্যাপ হিসেবে বিতরণ করতে, আপনাকে প্রতিটি ভ্যারিয়েন্টের জন্য একটি ভিন্ন applicationId অ্যাসাইন করতে হবে।

আপনার প্রোডাক্ট ফ্লেভারগুলো তৈরি ও কনফিগার করার পর, নোটিফিকেশন বারে থাকা ‘সিঙ্ক নাও’ (Sync Now) বোতামে ক্লিক করুন। সিঙ্ক সম্পন্ন হলে, গ্রেডল (Gradle) স্বয়ংক্রিয়ভাবে আপনার বিল্ড টাইপ ও প্রোডাক্ট ফ্লেভারের উপর ভিত্তি করে বিল্ড ভ্যারিয়েন্ট তৈরি করে এবং <product-flavor><Build-Type> অনুযায়ী সেগুলোর নামকরণ করে। উদাহরণস্বরূপ, যদি আপনি "demo" এবং "full" প্রোডাক্ট ফ্লেভার তৈরি করেন এবং ডিফল্ট "debug" ও "release" বিল্ড টাইপগুলো রাখেন, তাহলে গ্রেডল নিম্নলিখিত বিল্ড ভ্যারিয়েন্টগুলো তৈরি করবে:

  • demoDebug
  • demoRelease
  • fullDebug
  • fullRelease

কোন বিল্ড ভ্যারিয়েন্টটি বিল্ড ও রান করবেন তা নির্বাচন করতে, Build > Select Build Variant- এ যান এবং মেনু থেকে একটি বিল্ড ভ্যারিয়েন্ট বেছে নিন। প্রতিটি বিল্ড ভ্যারিয়েন্টকে তার নিজস্ব বৈশিষ্ট্য ও রিসোর্স দিয়ে কাস্টমাইজ করা শুরু করতে, আপনাকে সোর্স সেট তৈরি ও পরিচালনা করতে হবে, যেমনটি এই পৃষ্ঠায় বর্ণনা করা হয়েছে।

বিল্ড ভ্যারিয়েন্টের জন্য অ্যাপ্লিকেশন আইডি পরিবর্তন করুন

যখন আপনি আপনার অ্যাপের জন্য একটি APK বা AAB বিল্ড করেন, তখন বিল্ড টুলগুলো build.gradle.kts ফাইলের defaultConfig ব্লকে সংজ্ঞায়িত অ্যাপ্লিকেশন আইডি দিয়ে অ্যাপটিকে ট্যাগ করে, যেমনটি নিম্নলিখিত উদাহরণে দেখানো হয়েছে। তবে, আপনি যদি গুগল প্লে স্টোরে আলাদা লিস্টিং হিসেবে দেখানোর জন্য আপনার অ্যাপের বিভিন্ন সংস্করণ তৈরি করতে চান, যেমন একটি "ফ্রি" এবং একটি "প্রো" সংস্করণ, তাহলে আপনাকে আলাদা বিল্ড ভ্যারিয়েন্ট তৈরি করতে হবে, যেগুলোর প্রতিটির একটি ভিন্ন অ্যাপ্লিকেশন আইডি থাকবে।

এক্ষেত্রে, প্রতিটি বিল্ড ভ্যারিয়েন্টকে একটি পৃথক প্রোডাক্ট ফ্লেভার হিসেবে সংজ্ঞায়িত করুন। productFlavors ব্লকের ভেতরের প্রতিটি ফ্লেভারের জন্য, আপনি applicationId প্রপার্টিটি পুনরায় সংজ্ঞায়িত করতে পারেন, অথবা এর পরিবর্তে applicationIdSuffix ব্যবহার করে ডিফল্ট অ্যাপ্লিকেশন আইডির সাথে একটি সেগমেন্ট যুক্ত করতে পারেন, যেমনটি এখানে দেখানো হয়েছে:

কোটলিন

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 ব্যবহার করতে পারেন, যেমনটি এখানে দেখানো হয়েছে:

কোটলিন

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

গ্রুভি

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

যেহেতু গ্রেডল প্রোডাক্ট ফ্লেভারের পরে বিল্ড টাইপ কনফিগারেশন প্রয়োগ করে, তাই 'ফ্রি ডিবাগ' বিল্ড ভ্যারিয়েন্টের অ্যাপ্লিকেশন আইডি হলো 'com.example.myapp.free.debug'। এটি তখন কাজে আসে যখন আপনি একই ডিভাইসে ডিবাগ এবং রিলিজ উভয় বিল্ড রাখতে চান, কারণ কোনো দুটি অ্যাপের অ্যাপ্লিকেশন আইডি একই হতে পারে না।

আপনার যদি একটি লিগ্যাসি অ্যাপ (আগস্ট ২০২১-এর আগে তৈরি) থাকে যা আপনি গুগল প্লে-তে APK ব্যবহার করে বিতরণ করেন, এবং আপনি যদি একই অ্যাপ লিস্টিং ব্যবহার করে একাধিক APK বিতরণ করতে চান যেগুলোর প্রতিটি ভিন্ন ভিন্ন ডিভাইস কনফিগারেশন (যেমন API লেভেল) অনুযায়ী তৈরি, তাহলে আপনাকে অবশ্যই প্রতিটি বিল্ড ভ্যারিয়েন্টের জন্য একই অ্যাপ্লিকেশন আইডি ব্যবহার করতে হবে, কিন্তু প্রতিটি APK-কে একটি ভিন্ন versionCode দিতে হবে। আরও তথ্যের জন্য, মাল্টিপল APK সাপোর্ট সম্পর্কে পড়ুন। AAB ব্যবহার করে পাবলিশিং এর ক্ষেত্রে কোনো পরিবর্তন হয় না, কারণ এটি একটি একক আর্টিফ্যাক্ট ব্যবহার করে যা ডিফল্টরূপে একটি একক ভার্সন কোড এবং অ্যাপ্লিকেশন আইডি ব্যবহার করে।

পরামর্শ: আপনার ম্যানিফেস্ট ফাইলে অ্যাপ্লিকেশন আইডি উল্লেখ করার প্রয়োজন হলে, আপনি যেকোনো ম্যানিফেস্ট অ্যাট্রিবিউটে ${applicationId} প্লেসহোল্ডারটি ব্যবহার করতে পারেন। বিল্ডের সময়, গ্রেডল এই ট্যাগটিকে আসল অ্যাপ্লিকেশন আইডি দিয়ে প্রতিস্থাপন করে। আরও তথ্যের জন্য, ম্যানিফেস্টে বিল্ড ভেরিয়েবল ইনজেক্ট করা দেখুন।

স্বাদের মাত্রা অনুযায়ী একাধিক পণ্যের ফ্লেভার একত্রিত করুন।

কিছু ক্ষেত্রে, আপনি একাধিক প্রোডাক্ট ফ্লেভারের কনফিগারেশন একত্রিত করতে চাইতে পারেন। উদাহরণস্বরূপ, আপনি এপিআই লেভেলের উপর ভিত্তি করে 'ফুল' এবং 'ডেমো' প্রোডাক্ট ফ্লেভারের জন্য আলাদা কনফিগারেশন তৈরি করতে চাইতে পারেন। এটি করার জন্য, অ্যান্ড্রয়েড গ্রেডল প্লাগইন আপনাকে ফ্লেভার ডাইমেনশন হিসেবে প্রোডাক্ট ফ্লেভারের একাধিক গ্রুপ তৈরি করার সুযোগ দেয়।

আপনার অ্যাপ বিল্ড করার সময়, Gradle আপনার সংজ্ঞায়িত প্রতিটি ফ্লেভার ডাইমেনশন থেকে একটি প্রোডাক্ট ফ্লেভার কনফিগারেশন এবং একটি বিল্ড টাইপ কনফিগারেশন একত্রিত করে চূড়ান্ত বিল্ড ভ্যারিয়েন্টটি তৈরি করে। একই ফ্লেভার ডাইমেনশনের অন্তর্গত প্রোডাক্ট ফ্লেভারগুলোকে Gradle একত্রিত করে না।

নিম্নলিখিত কোড নমুনাটি 'full' এবং 'demo' প্রোডাক্ট ফ্লেভারগুলিকে গ্রুপ করার জন্য একটি 'mode' ফ্লেভার ডাইমেনশন এবং API লেভেলের উপর ভিত্তি করে প্রোডাক্ট ফ্লেভার কনফিগারেশনগুলিকে গ্রুপ করার জন্য একটি 'api' ফ্লেভার ডাইমেনশন তৈরি করতে flavorDimensions প্রপার্টি ব্যবহার করে:

কোটলিন

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"
      ...
    }
  }
}
...

Gradle যতগুলো বিল্ড ভ্যারিয়েন্ট তৈরি করে, তার সংখ্যা প্রতিটি ফ্লেভার ডাইমেনশনের ফ্লেভার সংখ্যা এবং আপনার কনফিগার করা বিল্ড টাইপের সংখ্যার গুণফলের সমান। Gradle যখন প্রতিটি বিল্ড ভ্যারিয়েন্ট বা সংশ্লিষ্ট আর্টিফ্যাক্টের নামকরণ করে, তখন উচ্চ-অগ্রাধিকারের ফ্লেভার ডাইমেনশনের অন্তর্গত প্রোডাক্ট ফ্লেভারগুলো প্রথমে আসে, এরপর নিম্ন-অগ্রাধিকারের ডাইমেনশনগুলোর ফ্লেভার এবং সবশেষে বিল্ড টাইপটি আসে।

পূর্ববর্তী বিল্ড কনফিগারেশনকে উদাহরণ হিসেবে ব্যবহার করে, গ্রেডল নিম্নলিখিত নামকরণ পদ্ধতি অনুসারে মোট ১২টি বিল্ড ভ্যারিয়েন্ট তৈরি করে:

  • বিল্ড ভেরিয়েন্ট: [minApi24, minApi23, minApi21][Demo, Full][Debug, Release]
  • সংশ্লিষ্ট APK: app-[minApi24, minApi23, minApi21]-[demo, full]-[debug, release].apk
  • উদাহরণস্বরূপ,
    বিল্ড ভেরিয়েন্ট: minApi24DemoDebug
    সংশ্লিষ্ট APK: app-minApi24-demo-debug.apk

প্রতিটি স্বতন্ত্র প্রোডাক্ট ফ্লেভার এবং বিল্ড ভ্যারিয়েন্টের জন্য সোর্স সেট ডিরেক্টরি তৈরি করার পাশাপাশি, আপনি প্রোডাক্ট ফ্লেভারগুলোর প্রতিটি সমন্বয়ের জন্যও সোর্স সেট ডিরেক্টরি তৈরি করতে পারেন। উদাহরণস্বরূপ, আপনি src/demoMinApi24/java/ ডিরেক্টরিতে জাভা সোর্স তৈরি করে যোগ করতে পারেন, এবং Gradle শুধুমাত্র তখনই সেই সোর্সগুলো ব্যবহার করে যখন ঐ দুটি প্রোডাক্ট ফ্লেভারকে একত্রিত করে এমন কোনো ভ্যারিয়েন্ট বিল্ড করা হয়।

প্রোডাক্ট ফ্লেভার কম্বিনেশনের জন্য আপনার তৈরি করা সোর্স সেটগুলোর অগ্রাধিকার, প্রতিটি স্বতন্ত্র প্রোডাক্ট ফ্লেভারের সোর্স সেটগুলোর চেয়ে বেশি। সোর্স সেট এবং গ্রেডল কীভাবে রিসোর্স মার্জ করে সে সম্পর্কে আরও জানতে, "কীভাবে সোর্স সেট তৈরি করবেন" অংশটি পড়ুন।

ফিল্টার ভ্যারিয়েন্ট

আপনার কনফিগার করা প্রোডাক্ট ফ্লেভার এবং বিল্ড টাইপের প্রতিটি সম্ভাব্য সমন্বয়ের জন্য গ্রেডল একটি বিল্ড ভ্যারিয়েন্ট তৈরি করে। তবে, এমন কিছু বিল্ড ভ্যারিয়েন্ট থাকতে পারে যা আপনার প্রয়োজন নেই অথবা আপনার প্রোজেক্টের প্রেক্ষাপটে সেগুলোর কোনো অর্থ হয় না। নির্দিষ্ট কিছু বিল্ড ভ্যারিয়েন্ট কনফিগারেশন অপসারণ করতে, আপনার মডিউল-স্তরের build.gradle.kts ফাইলে একটি ভ্যারিয়েন্ট ফিল্টার তৈরি করুন।

পূর্ববর্তী বিভাগের বিল্ড কনফিগারেশনকে উদাহরণ হিসেবে ব্যবহার করে, ধরুন আপনি অ্যাপটির ডেমো সংস্করণের জন্য শুধুমাত্র এপিআই লেভেল ২৩ এবং তার উপরের সংস্করণগুলো সমর্থন করার পরিকল্পনা করছেন। আপনি 'minApi21' এবং 'demo' প্রোডাক্ট ফ্লেভারগুলোকে একত্রিত করে এমন সমস্ত বিল্ড ভ্যারিয়েন্ট কনফিগারেশন ফিল্টার করে বাদ দিতে variantFilter ব্লকটি ব্যবহার করতে পারেন:

কোটলিন

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)
      }
  }
}
...

একবার আপনি আপনার বিল্ড কনফিগারেশনে একটি ভ্যারিয়েন্ট ফিল্টার যোগ করে নোটিফিকেশন বারে থাকা 'সিঙ্ক নাউ' (Sync Now) বোতামে ক্লিক করলে, গ্রেডল (Gradle) আপনার নির্দিষ্ট করা শর্ত পূরণকারী যেকোনো বিল্ড ভ্যারিয়েন্টকে উপেক্ষা করে। এরপর আপনি মেনু বার থেকে 'বিল্ড > সিলেক্ট বিল্ড ভ্যারিয়েন্ট' (Build > Select Build Variant) বা 'বিল্ড ভ্যারিয়েন্টস' (Build Variants) বোতামে ক্লিক করলে, সেই বিল্ড ভ্যারিয়েন্টগুলো আর মেনুতে দেখা যায় না। টুল উইন্ডো বারে।

উৎস সেট তৈরি করুন

ডিফল্টরূপে, অ্যান্ড্রয়েড স্টুডিও আপনার সমস্ত বিল্ড ভ্যারিয়েন্টের মধ্যে শেয়ার করতে চান এমন সবকিছুর জন্য main/ সোর্স সেট এবং ডিরেক্টরি তৈরি করে। তবে, নির্দিষ্ট বিল্ড টাইপ, প্রোডাক্ট ফ্লেভার, প্রোডাক্ট ফ্লেভারের সংমিশ্রণ ( ফ্লেভার ডাইমেনশন ব্যবহার করার সময়), এবং বিল্ড ভ্যারিয়েন্টের জন্য গ্রেডল ঠিক কোন ফাইলগুলো কম্পাইল ও প্যাকেজ করবে, তা নিয়ন্ত্রণ করতে আপনি নতুন সোর্স সেট তৈরি করতে পারেন।

উদাহরণস্বরূপ, আপনি main/ সোর্স সেটে মৌলিক কার্যকারিতা সংজ্ঞায়িত করতে পারেন এবং বিভিন্ন ক্লায়েন্টের জন্য আপনার অ্যাপের ব্র্যান্ডিং পরিবর্তন করতে প্রোডাক্ট ফ্লেভার সোর্স সেট ব্যবহার করতে পারেন, অথবা শুধুমাত্র ডিবাগ বিল্ড টাইপ ব্যবহারকারী বিল্ড ভ্যারিয়েন্টগুলোর জন্য বিশেষ অনুমতি এবং লগিং কার্যকারিতা অন্তর্ভুক্ত করতে পারেন।

Gradle আশা করে যে সোর্স সেট ফাইল এবং ডিরেক্টরিগুলো main/ সোর্স সেটের মতোই একটি নির্দিষ্ট উপায়ে সাজানো থাকবে। উদাহরণস্বরূপ, Gradle আশা করে যে আপনার "debug" বিল্ড টাইপের জন্য নির্দিষ্ট Kotlin বা Java ক্লাস ফাইলগুলো src/debug/kotlin/ অথবা src/debug/java/ ডিরেক্টরিতে অবস্থিত থাকবে।

অ্যান্ড্রয়েড গ্রেডল প্লাগইন একটি দরকারি গ্রেডল টাস্ক প্রদান করে, যা আপনাকে দেখায় কীভাবে আপনার প্রতিটি বিল্ড টাইপ, প্রোডাক্ট ফ্লেভার এবং বিল্ড ভ্যারিয়েন্টের জন্য ফাইলগুলো সাজাতে হয়। উদাহরণস্বরূপ, টাস্ক আউটপুট থেকে নেওয়া নিম্নলিখিত নমুনাটি বর্ণনা করে যে 'ডিবাগ' বিল্ড টাইপের জন্য গ্রেডল নির্দিষ্ট ফাইলগুলো কোথায় খুঁজে পাওয়ার আশা করে:

------------------------------------------------------------
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. ফাইল > সেটিংস > এক্সপেরিমেন্টাল-এ ক্লিক করুন (macOS-এ Android Studio > সেটিংস > এক্সপেরিমেন্টাল )।
    2. Gradle সিঙ্কের সময় Gradle টাস্ক তালিকা বিল্ড না করার বিকল্পটি অনির্বাচিত করুন।
  3. Gradle টাস্কটি সম্পাদন করার পর, আউটপুট দেখানোর জন্য রান উইন্ডোটি খোলে।

দ্রষ্টব্য: টাস্ক আউটপুট আপনাকে এটাও দেখায় যে, আপনার অ্যাপের টেস্ট চালানোর জন্য যে ফাইলগুলো ব্যবহার করতে চান, যেমন test/ এবং androidTest/ টেস্টিং সোর্স সেট , সেগুলোর জন্য সোর্স সেটগুলো কীভাবে সাজাতে হয়।

যখন আপনি একটি নতুন বিল্ড ভ্যারিয়েন্ট তৈরি করেন, অ্যান্ড্রয়েড স্টুডিও আপনার জন্য সোর্স সেট ডিরেক্টরিগুলো তৈরি করে দেয় না, তবে এটি আপনাকে সাহায্য করার জন্য কয়েকটি বিকল্প দেয়। উদাহরণস্বরূপ, আপনার 'ডিবাগ' বিল্ড টাইপের জন্য শুধু java/ ডিরেক্টরিটি তৈরি করতে:

  1. প্রজেক্ট পেইনটি খুলুন এবং পেইনের উপরের মেনু থেকে প্রজেক্ট ভিউ নির্বাচন করুন।
  2. MyProject/app/src/ -এ যান।
  3. src ডিরেক্টরিতে রাইট-ক্লিক করুন এবং New > Directory নির্বাচন করুন।
  4. Gradle Source Sets-এর অধীনে থাকা মেনু থেকে full/java নির্বাচন করুন।
  5. এন্টার চাপুন।

অ্যান্ড্রয়েড স্টুডিও আপনার ডিবাগ বিল্ড টাইপের জন্য একটি সোর্স সেট ডিরেক্টরি তৈরি করে এবং তারপর এর ভিতরে java/ ডিরেক্টরিটি তৈরি করে। বিকল্পভাবে, আপনি যখন কোনো নির্দিষ্ট বিল্ড ভ্যারিয়েন্টের জন্য আপনার প্রজেক্টে একটি নতুন ফাইল যোগ করেন, তখন অ্যান্ড্রয়েড স্টুডিও আপনার জন্য ডিরেক্টরিগুলো তৈরি করে দিতে পারে।

উদাহরণস্বরূপ, আপনার 'ডিবাগ' বিল্ড টাইপের জন্য একটি ভ্যালু XML ফাইল তৈরি করতে:

  1. প্রজেক্ট প্যানে, src ডিরেক্টরিতে রাইট-ক্লিক করুন এবং New > XML > Values ​​XML File নির্বাচন করুন।
  2. XML ফাইলের জন্য নাম লিখুন অথবা ডিফল্ট নামটি রাখুন।
  3. টার্গেট সোর্স সেট-এর পাশের মেনু থেকে ডিবাগ নির্বাচন করুন।
  4. শেষ করুন- এ ক্লিক করুন।

যেহেতু টার্গেট সোর্স সেট হিসেবে 'ডিবাগ' বিল্ড টাইপ নির্দিষ্ট করা হয়েছিল, অ্যান্ড্রয়েড স্টুডিও XML ফাইল তৈরি করার সময় স্বয়ংক্রিয়ভাবে প্রয়োজনীয় ডিরেক্টরিগুলো তৈরি করে নেয়। এর ফলে যে ডিরেক্টরি কাঠামোটি তৈরি হয়, তা চিত্র ১-এর মতো দেখায়।

চিত্র ১. 'ডিবাগ' বিল্ড টাইপের জন্য নতুন সোর্স সেট ডিরেক্টরিসমূহ।

সক্রিয় সোর্স সেটগুলোর আইকনে একটি সবুজ নির্দেশক থাকে, যা নির্দেশ করে যে সেগুলো সক্রিয়। debug সোর্স সেটের শেষে [main] যুক্ত করা হয়, যা বোঝায় যে এটিকে main সোর্স সেটের সাথে একীভূত করা হবে।

একই পদ্ধতি ব্যবহার করে, আপনি প্রোডাক্ট ফ্লেভারের জন্য (যেমন src/demo/ এবং বিল্ড ভ্যারিয়েন্টের জন্য (যেমন src/demoDebug/ সোর্স সেট ডিরেক্টরি তৈরি করতে পারেন। এছাড়াও, আপনি নির্দিষ্ট বিল্ড ভ্যারিয়েন্টকে লক্ষ্য করে টেস্টিং সোর্স সেট তৈরি করতে পারেন (যেমন src/androidTestDemoDebug/ )। আরও জানতে, টেস্টিং সোর্স সেট সম্পর্কে পড়ুন।

ডিফল্ট উৎস সেট কনফিগারেশন পরিবর্তন করুন

যদি আপনার সোর্সগুলো গ্রেডলের প্রত্যাশিত ডিফল্ট সোর্স সেট ফাইল কাঠামো অনুযায়ী সাজানো না থাকে, যেমনটা সোর্স সেট তৈরি করার বিষয়ে আগের অংশে বর্ণনা করা হয়েছে, তাহলে আপনি sourceSets ব্লকটি ব্যবহার করে পরিবর্তন করতে পারেন যে একটি সোর্স সেটের প্রতিটি উপাদানের জন্য গ্রেডল কোথা থেকে ফাইল সংগ্রহ করবে।

` sourceSets ব্লকটি অবশ্যই android ব্লকের মধ্যে থাকতে হবে। সোর্স ফাইলগুলো স্থানান্তরিত করার কোনো প্রয়োজন নেই; আপনাকে শুধু মডিউল-স্তরের build.gradle.kts ফাইলের সাপেক্ষে `path(গুলি)` গ্রেডলকে সরবরাহ করতে হবে, যেখানে গ্রেডল প্রতিটি সোর্স সেট কম্পোনেন্টের জন্য ফাইল খুঁজে পাবে। আপনি কোন কম্পোনেন্টগুলো কনফিগার করতে পারবেন এবং সেগুলোকে একাধিক ফাইল বা ডিরেক্টরিতে ম্যাপ করতে পারবেন কিনা, তা জানতে ` Android Gradle plugin API reference` দেখুন।

নিম্নলিখিত কোড নমুনাটি app/other/ ডিরেক্টরি থেকে সোর্সগুলোকে main সোর্স সেটের নির্দিষ্ট কিছু উপাদানের সাথে ম্যাপ করে এবং androidTest সোর্স সেটের রুট ডিরেক্টরি পরিবর্তন করে:

কোটলিন

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 উভয় সোর্স সেটের সাথে একই টেস্ট সোর্স শেয়ার করতে পারবেন না। এর কারণ হলো, অ্যান্ড্রয়েড স্টুডিও প্রতিটি সোর্স সেটের জন্য আলাদা ইন্টেলিজ মডিউল তৈরি করে এবং বিভিন্ন সোর্স সেটের মধ্যে ডুপ্লিকেট কন্টেন্ট রুট সমর্থন করতে পারে না।

উৎস সেট দিয়ে তৈরি করুন

আপনি যে কোড এবং রিসোর্সগুলো শুধুমাত্র নির্দিষ্ট কনফিগারেশনের সাথে প্যাকেজ করতে চান, সেগুলো রাখার জন্য সোর্স সেট ডিরেক্টরি ব্যবহার করতে পারেন। উদাহরণস্বরূপ, যদি আপনি "demoDebug" বিল্ড ভ্যারিয়েন্টটি বিল্ড করেন, যা একটি "demo" প্রোডাক্ট ফ্লেভার এবং "debug" বিল্ড টাইপের সংমিশ্রণ, তাহলে Gradle এই ডিরেক্টরিগুলো দেখে এবং সেগুলোকে নিম্নলিখিত অগ্রাধিকার দেয়:

  1. src/demoDebug/ (বিল্ড ভ্যারিয়েন্ট সোর্স সেট)
  2. src/debug/ (বিল্ড টাইপ সোর্স সেট)
  3. src/demo/ (পণ্যের ফ্লেভার উৎস সেট)
  4. src/main/ (প্রধান উৎস সেট)

পণ্যের বিভিন্ন ফ্লেভারের সমন্বয়ে তৈরি সোর্স সেটে অবশ্যই সমস্ত ফ্লেভার ডাইমেনশন অন্তর্ভুক্ত থাকতে হবে। উদাহরণস্বরূপ, বিল্ড ভ্যারিয়েন্ট সোর্স সেটটি অবশ্যই বিল্ড টাইপ এবং সমস্ত ফ্লেভার ডাইমেনশনের সমন্বয়ে গঠিত হতে হবে। যেসব ফোল্ডার একাধিক কিন্তু সব ফ্লেভার ডাইমেনশন অন্তর্ভুক্ত করে না, সেগুলোর সাথে জড়িত কোড এবং রিসোর্স মার্জ করা সমর্থিত নয়।

আপনি যদি একাধিক প্রোডাক্ট ফ্লেভার একত্রিত করেন , তবে প্রোডাক্ট ফ্লেভারগুলোর মধ্যে অগ্রাধিকার নির্ধারিত হয় তারা কোন ফ্লেভার ডাইমেনশনের অন্তর্ভুক্ত তার উপর ভিত্তি করে। android.flavorDimensions প্রপার্টি ব্যবহার করে ফ্লেভার ডাইমেনশনগুলো তালিকাভুক্ত করার সময়, আপনার তালিকাভুক্ত প্রথম ফ্লেভার ডাইমেনশনের অন্তর্ভুক্ত প্রোডাক্ট ফ্লেভারগুলোর অগ্রাধিকার দ্বিতীয় ফ্লেভার ডাইমেনশনের অন্তর্ভুক্তগুলোর চেয়ে বেশি থাকে এবং এভাবেই চলতে থাকে। এছাড়াও, প্রোডাক্ট ফ্লেভারের সংমিশ্রণের জন্য আপনার তৈরি করা সোর্স সেটগুলোর অগ্রাধিকার, কোনো একক প্রোডাক্ট ফ্লেভারের অন্তর্ভুক্ত সোর্স সেটগুলোর চেয়ে বেশি থাকে।

কোড এবং রিসোর্স একত্রিত করার সময় গ্রেডল কোন সোর্স সেটের উচ্চতর অগ্রাধিকার দেবে, তা প্রায়োরিটি অর্ডার নির্ধারণ করে। যেহেতু demoDebug/ সোর্স সেট ডিরেক্টরিতে সম্ভবত সেই বিল্ড ভ্যারিয়েন্টের জন্য নির্দিষ্ট ফাইল থাকে, তাই যদি demoDebug/ এ এমন কোনো ফাইল অন্তর্ভুক্ত থাকে যা debug/ এও সংজ্ঞায়িত, তাহলে গ্রেডল demoDebug/ সোর্স সেটের ফাইলটি ব্যবহার করে। একইভাবে, গ্রেডল main/ এর একই ফাইলগুলোর চেয়ে বিল্ড টাইপ এবং প্রোডাক্ট ফ্লেভার সোর্স সেটের ফাইলগুলোকে উচ্চতর অগ্রাধিকার দেয়। নিম্নলিখিত বিল্ড নিয়মগুলো প্রয়োগ করার সময় গ্রেডল এই প্রায়োরিটি অর্ডার বিবেচনা করে:

  • kotlin/ অথবা java/ ডিরেক্টরিতে থাকা সমস্ত সোর্স কোড একসাথে কম্পাইল করে একটি একক আউটপুট তৈরি করা হয়।

    দ্রষ্টব্য: কোনো নির্দিষ্ট বিল্ড ভ্যারিয়েন্টের জন্য, যদি গ্রেডল এমন দুই বা ততোধিক সোর্স সেট ডিরেক্টরি খুঁজে পায় যেখানে একই কোটলিন বা জাভা ক্লাস সংজ্ঞায়িত করা আছে, তাহলে এটি একটি বিল্ড এরর দেখায়। উদাহরণস্বরূপ, একটি ডিবাগ অ্যাপ বিল্ড করার সময়, আপনি src/debug/Utility.kt এবং src/main/Utility.kt উভয়ই সংজ্ঞায়িত করতে পারবেন না, কারণ গ্রেডল বিল্ড প্রক্রিয়ার সময় এই দুটি ডিরেক্টরিই দেখে এবং একটি "ডুপ্লিকেট ক্লাস" এরর দেখায়। আপনি যদি বিভিন্ন বিল্ড টাইপের জন্য Utility.kt এর ভিন্ন ভিন্ন সংস্করণ চান, তবে প্রতিটি বিল্ড টাইপকে অবশ্যই ফাইলটির নিজস্ব সংস্করণ সংজ্ঞায়িত করতে হবে এবং এটিকে main/ সোর্স সেটে অন্তর্ভুক্ত করা যাবে না।

  • ম্যানিফেস্টগুলো একত্রিত করে একটি একক ম্যানিফেস্টে পরিণত করা হয়। পূর্ববর্তী উদাহরণের তালিকার মতোই একই ক্রমে অগ্রাধিকার দেওয়া হয়। অর্থাৎ, একটি বিল্ড টাইপের জন্য ম্যানিফেস্ট সেটিংস একটি প্রোডাক্ট ফ্লেভারের ম্যানিফেস্ট সেটিংসকে ওভাররাইড করে, এবং এভাবেই চলতে থাকে। আরও জানতে, ম্যানিফেস্ট মার্জিং সম্পর্কে পড়ুন।
  • values/ ডিরেক্টরির ফাইলগুলো একত্রিত করা হয়। যদি দুটি ফাইলের নাম একই হয়, যেমন দুটি strings.xml ফাইল, তাহলে পূর্ববর্তী উদাহরণের তালিকার মতোই একই ক্রমে অগ্রাধিকার দেওয়া হয়। অর্থাৎ, বিল্ড টাইপ সোর্স সেটের কোনো ফাইলে সংজ্ঞায়িত মানগুলো প্রোডাক্ট ফ্লেভারের একই ফাইলে সংজ্ঞায়িত মানগুলোকে ওভাররাইড করে, এবং এভাবেই চলতে থাকে।
  • res/ এবং asset/ ডিরেক্টরিতে থাকা রিসোর্সগুলো একসাথে প্যাকেজ করা হয়। যদি দুই বা ততোধিক সোর্স সেটে একই নামের রিসোর্স সংজ্ঞায়িত থাকে, তবে পূর্ববর্তী উদাহরণের তালিকার ক্রম অনুসারে অগ্রাধিকার দেওয়া হয়।
  • অ্যাপটি বিল্ড করার সময় গ্রেডল লাইব্রেরি মডিউল ডিপেন্ডেন্সির সাথে অন্তর্ভুক্ত রিসোর্স এবং ম্যানিফেস্টগুলোকে সর্বনিম্ন অগ্রাধিকার দেয়।

নির্ভরতা ঘোষণা করুন

একটি নির্দিষ্ট বিল্ড ভ্যারিয়েন্ট বা টেস্টিং সোর্স সেটের জন্য কোনো ডিপেন্ডেন্সি কনফিগার করতে, Implementation কীওয়ার্ডের আগে বিল্ড ভ্যারিয়েন্ট বা টেস্টিং সোর্স সেটের নামটি যুক্ত করুন, যেমনটি নিম্নলিখিত উদাহরণে দেখানো হয়েছে:

কোটলিন

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'
}

ডিপেন্ডেন্সি কনফিগার করার বিষয়ে আরও তথ্যের জন্য, ‘বিল্ড ডিপেন্ডেন্সি যোগ করুন ’ দেখুন।

ভ্যারিয়েন্ট-সচেতন নির্ভরতা ব্যবস্থাপনা ব্যবহার করুন

অ্যান্ড্রয়েড গ্রেডল প্লাগইন ৩.০.০ এবং এর পরবর্তী সংস্করণগুলোতে একটি নতুন ডিপেন্ডেন্সি মেকানিজম অন্তর্ভুক্ত করা হয়েছে, যা কোনো লাইব্রেরি ব্যবহার করার সময় স্বয়ংক্রিয়ভাবে ভ্যারিয়েন্টগুলো মিলিয়ে নেয়। এর মানে হলো, একটি অ্যাপের debug ভ্যারিয়েন্ট স্বয়ংক্রিয়ভাবে একটি লাইব্রেরির debug ভ্যারিয়েন্ট ব্যবহার করে, এবং এভাবেই চলতে থাকে। এটি ফ্লেভার ব্যবহার করার ক্ষেত্রেও কাজ করে: একটি অ্যাপের freeDebug ভ্যারিয়েন্ট একটি লাইব্রেরির freeDebug ভ্যারিয়েন্ট ব্যবহার করবে।

প্লাগইনটি যাতে ভ্যারিয়েন্টগুলো সঠিকভাবে মেলাতে পারে, সেজন্য সরাসরি মিল সম্ভব না হলে আপনাকে নিম্নলিখিত বিভাগে বর্ণিত ম্যাচিং ফলব্যাক প্রদান করতে হবে।

উদাহরণস্বরূপ, ধরুন আপনার অ্যাপে "স্টেজিং" নামের একটি বিল্ড টাইপ কনফিগার করা আছে, কিন্তু এর কোনো একটি লাইব্রেরি ডিপেন্ডেন্সিতে তা নেই। যখন প্লাগইনটি আপনার অ্যাপের "স্টেজিং" ভার্সনটি বিল্ড করার চেষ্টা করবে, তখন এটি বুঝতে পারবে না যে লাইব্রেরির কোন ভার্সনটি ব্যবহার করতে হবে, এবং আপনি নিচের মতো একটি এরর মেসেজ দেখতে পাবেন:

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

ভ্যারিয়েন্ট মেলানোর সাথে সম্পর্কিত বিল্ড ত্রুটিগুলি সমাধান করুন

এই প্লাগইনটিতে DSL এলিমেন্ট অন্তর্ভুক্ত রয়েছে, যা আপনাকে নিয়ন্ত্রণ করতে সাহায্য করে যে, যেসব ক্ষেত্রে একটি অ্যাপ এবং একটি ডিপেন্ডেন্সির মধ্যে সরাসরি ভ্যারিয়েন্ট ম্যাচ সম্ভব হয় না, গ্রেডল কীভাবে সেই পরিস্থিতিগুলোর সমাধান করবে।

ভেরিয়েন্ট-অ্যাওয়ার ডিপেন্ডেন্সি ম্যাচিং সম্পর্কিত কিছু সমস্যা এবং DSL প্রোপার্টি ব্যবহার করে সেগুলোর সমাধান নিচে দেওয়া হলো:

  • আপনার অ্যাপে এমন একটি বিল্ড টাইপ অন্তর্ভুক্ত আছে যা কোনো লাইব্রেরি ডিপেন্ডেন্সিতে নেই।

    উদাহরণস্বরূপ, আপনার অ্যাপে একটি 'staging' বিল্ড টাইপ অন্তর্ভুক্ত আছে, কিন্তু একটি ডিপেন্ডেন্সিতে শুধুমাত্র 'debug' এবং 'release' বিল্ড টাইপ রয়েছে।

    উল্লেখ্য যে, কোনো লাইব্রেরি ডিপেন্ডেন্সিতে এমন একটি বিল্ড টাইপ অন্তর্ভুক্ত থাকলেও কোনো সমস্যা নেই যা আপনার অ্যাপে নেই। এর কারণ হলো, প্লাগইনটি কখনোই ডিপেন্ডেন্সি থেকে সেই বিল্ড টাইপটির জন্য অনুরোধ করে না।

    একটি নির্দিষ্ট বিল্ড টাইপের জন্য বিকল্প মিলগুলি নির্দিষ্ট করতে matchingFallbacks ব্যবহার করুন, যেমনটি এখানে দেখানো হয়েছে:

    কোটলিন

    // 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']
            }
        }
    }
  • একটি নির্দিষ্ট ফ্লেভার ডাইমেনশনের ক্ষেত্রে, যা অ্যাপ এবং এর লাইব্রেরি ডিপেন্ডেন্সি উভয়ের মধ্যেই বিদ্যমান, আপনার অ্যাপে এমন কিছু ফ্লেভার অন্তর্ভুক্ত রয়েছে যা লাইব্রেরিতে নেই।

    উদাহরণস্বরূপ, আপনার অ্যাপ এবং এর লাইব্রেরি ডিপেন্ডেন্সি উভয়ই একটি "টিয়ার" ফ্লেভার ডাইমেনশন অন্তর্ভুক্ত করে। তবে, অ্যাপের "টিয়ার" ডাইমেনশনটিতে "ফ্রি" এবং "পেইড" ফ্লেভার রয়েছে, কিন্তু একটি ডিপেন্ডেন্সিতে একই ডাইমেনশনের জন্য শুধুমাত্র "ডেমো" এবং "পেইড" ফ্লেভার রয়েছে।

    উল্লেখ্য যে, অ্যাপ এবং এর লাইব্রেরি ডিপেন্ডেন্সি উভয় ক্ষেত্রেই বিদ্যমান একটি নির্দিষ্ট ফ্লেভার ডাইমেনশনের ক্ষেত্রে, কোনো লাইব্রেরিতে এমন একটি প্রোডাক্ট ফ্লেভার অন্তর্ভুক্ত থাকলেও কোনো সমস্যা হয় না যা আপনার অ্যাপে নেই। এর কারণ হলো, প্লাগইনটি কখনোই ডিপেন্ডেন্সি থেকে সেই ফ্লেভারটির জন্য অনুরোধ করে না।

    অ্যাপটির 'ফ্রি' প্রোডাক্ট ফ্লেভারের জন্য বিকল্প ম্যাচ নির্দিষ্ট করতে matchingFallbacks ব্যবহার করুন, যেমনটি এখানে দেখানো হয়েছে:

    কোটলিন

    // 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" সংস্করণটি ব্যবহার করবে।

    নিম্নলিখিত নমুনায় দেখানো অনুযায়ী, প্রতিটি অনুপস্থিত ডাইমেনশন থেকে প্লাগইনটি কোন ডিফল্ট ফ্লেভার নির্বাচন করবে তা নির্দিষ্ট করতে defaultConfig ব্লকে missingDimensionStrategy ব্যবহার করুন। এছাড়াও আপনি productFlavors ব্লকে আপনার নির্বাচনগুলো ওভাররাইড করতে পারেন, যাতে প্রতিটি ফ্লেভার একটি অনুপস্থিত ডাইমেনশনের জন্য ভিন্ন ভিন্ন ম্যাচিং স্ট্র্যাটেজি নির্দিষ্ট করতে পারে।

    কোটলিন

    // 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 দেখুন।

স্বাক্ষর করার সেটিংস কনফিগার করুন

আপনি যদি এই বিল্ডের জন্য স্পষ্টভাবে কোনো সাইনিং কনফিগারেশন নির্ধারণ না করেন, তাহলে গ্রেডল আপনার রিলিজ বিল্ডের APK বা AAB সাইন করে না। আপনার কাছে যদি এখনও কোনো সাইনিং কী না থাকে, তাহলে অ্যান্ড্রয়েড স্টুডিও ব্যবহার করে একটি আপলোড কী এবং কীস্টোর তৈরি করুন

Gradle বিল্ড কনফিগারেশন ব্যবহার করে আপনার রিলিজ বিল্ড টাইপের জন্য সাইনিং কনফিগারেশন ম্যানুয়ালি সেট করতে:

  1. একটি কীস্টোর তৈরি করুন। কীস্টোর হলো একটি বাইনারি ফাইল, যাতে এক সেট প্রাইভেট কী থাকে। আপনাকে অবশ্যই আপনার কীস্টোরটি একটি নিরাপদ ও সুরক্ষিত স্থানে রাখতে হবে।
  2. একটি প্রাইভেট কী তৈরি করুন। প্রাইভেট কী আপনার অ্যাপ বিতরণের জন্য সাইন করতে ব্যবহৃত হয় এবং এটি কখনোই অ্যাপের সাথে অন্তর্ভুক্ত করা হয় না বা অননুমোদিত তৃতীয় পক্ষের কাছে প্রকাশ করা হয় না।
  3. মডিউল-স্তরের build.gradle.kts ফাইলে সাইনিং কনফিগারেশনটি যোগ করুন:

    কোটলিন

    ...
    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
            }
        }
    }

দ্রষ্টব্য: আপনার রিলিজ কী এবং কীস্টোরের পাসওয়ার্ডগুলো বিল্ড ফাইলের মধ্যে অন্তর্ভুক্ত করা একটি ভালো নিরাপত্তা অনুশীলন নয়। এর পরিবর্তে, বিল্ড ফাইলটিকে এমনভাবে কনফিগার করুন যাতে এটি এনভায়রনমেন্ট ভেরিয়েবল থেকে এই পাসওয়ার্ডগুলো সংগ্রহ করে অথবা বিল্ড প্রক্রিয়াটি আপনাকে এই পাসওয়ার্ডগুলোর জন্য অনুরোধ করে।

এনভায়রনমেন্ট ভেরিয়েবল থেকে এই পাসওয়ার্ডগুলো পেতে:

কোটলিন

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

গ্রুভি

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

বিকল্পভাবে, আপনি একটি স্থানীয় প্রোপার্টিজ ফাইল থেকে কীস্টোরটি লোড করতে পারেন। নিরাপত্তার কারণে, এই ফাইলটি সোর্স কন্ট্রোলে যোগ করবেন না। পরিবর্তে, প্রতিটি ডেভেলপারের জন্য এটি স্থানীয়ভাবে সেট আপ করুন। আরও জানতে, "আপনার বিল্ড ফাইল থেকে সাইনিং তথ্য সরান" পড়ুন।

এই প্রক্রিয়াটি সম্পন্ন করার পর, আপনি আপনার অ্যাপটি বিতরণ করতে এবং গুগল প্লে-তে প্রকাশ করতে পারবেন।

সতর্কীকরণ: আপনার কীস্টোর এবং প্রাইভেট কী একটি নিরাপদ ও সুরক্ষিত স্থানে রাখুন এবং নিশ্চিত করুন যে সেগুলোর সুরক্ষিত ব্যাকআপ আপনার কাছে আছে। আপনি যদি প্লে অ্যাপ সাইনিং ব্যবহার করেন এবং আপনার আপলোড কী হারিয়ে ফেলেন, তাহলে আপনি প্লে কনসোল ব্যবহার করে একটি রিসেটের জন্য অনুরোধ করতে পারেন। আপনি যদি প্লে অ্যাপ সাইনিং ছাড়া কোনো অ্যাপ প্রকাশ করেন (আগস্ট ২০২১-এর আগে তৈরি অ্যাপের জন্য) এবং আপনার অ্যাপ সাইনিং কী হারিয়ে ফেলেন, তাহলে আপনি আপনার অ্যাপের কোনো আপডেট প্রকাশ করতে পারবেন না, কারণ আপনাকে সর্বদা আপনার অ্যাপের সমস্ত সংস্করণ একই কী দিয়ে সাইন করতে হবে।

Wear OS অ্যাপে স্বাক্ষর করা

Wear OS অ্যাপ প্রকাশ করার সময়, ওয়াচ APK এবং ঐচ্ছিক ফোন APK উভয়কেই একই কী (key) দিয়ে সাইন করতে হবে। Wear OS অ্যাপ প্যাকেজিং এবং সাইনিং সম্পর্কে আরও তথ্যের জন্য, “Ware অ্যাপ প্যাকেজ এবং বিতরণ করুন” দেখুন।