Gradle প্লাগইন লিখুন

অ্যান্ড্রয়েড গ্রেডল প্লাগইন (এজিপি) অ্যান্ড্রয়েড অ্যাপ্লিকেশনের জন্য অফিসিয়াল বিল্ড সিস্টেম। এটিতে বিভিন্ন ধরণের উত্স সংকলন করার জন্য সমর্থন রয়েছে এবং সেগুলিকে একটি অ্যাপ্লিকেশনের সাথে সংযুক্ত করার জন্য যা আপনি একটি শারীরিক Android ডিভাইস বা একটি এমুলেটরে চালাতে পারেন৷

এজিপি বিল্ড ইনপুট নিয়ন্ত্রণ করতে প্লাগইনগুলির জন্য এক্সটেনশন পয়েন্ট ধারণ করে এবং নতুন পদক্ষেপের মাধ্যমে এর কার্যকারিতা প্রসারিত করে যা স্ট্যান্ডার্ড বিল্ড কাজের সাথে একীভূত করা যেতে পারে। AGP-এর পূর্ববর্তী সংস্করণগুলিতে অভ্যন্তরীণ বাস্তবায়ন থেকে স্পষ্টভাবে আলাদা করা অফিসিয়াল API ছিল না। সংস্করণ 7.0 থেকে শুরু করে, এজিপিতে অফিসিয়াল, স্থিতিশীল API- এর একটি সেট রয়েছে যা আপনি নির্ভর করতে পারেন।

AGP API জীবনচক্র

AGP তার API-এর অবস্থা নির্ধারণ করতে Gradle বৈশিষ্ট্যের জীবনচক্র অনুসরণ করে:

  • অভ্যন্তরীণ : সর্বজনীন ব্যবহারের উদ্দেশ্যে নয়
  • ইনকিউবেটিং : সর্বজনীন ব্যবহারের জন্য উপলব্ধ কিন্তু চূড়ান্ত নয়, যার অর্থ হল তারা চূড়ান্ত সংস্করণে পিছিয়ে সামঞ্জস্যপূর্ণ নাও হতে পারে
  • সর্বজনীন : সর্বজনীন ব্যবহারের জন্য উপলব্ধ এবং স্থিতিশীল
  • অপ্রচলিত : আর সমর্থিত নয়, এবং নতুন API দিয়ে প্রতিস্থাপিত হয়েছে৷

অবচয় নীতি

AGP পুরানো API-এর অবচয় এবং নতুন, স্থিতিশীল API এবং একটি নতুন ডোমেন স্পেসিফিক ল্যাঙ্গুয়েজ (DSL) দিয়ে তাদের প্রতিস্থাপনের সাথে বিকশিত হচ্ছে। এই বিবর্তনটি একাধিক এজিপি রিলিজকে বিস্তৃত করবে, এবং আপনি এজিপি এপিআই/ডিএসএল মাইগ্রেশন টাইমলাইনে এটি সম্পর্কে আরও জানতে পারবেন।

যখন AGP API গুলিকে অবমূল্যায়িত করা হয়, এই স্থানান্তরের জন্য বা অন্যথায়, তারা বর্তমান প্রধান রিলিজে উপলব্ধ থাকবে কিন্তু সতর্কতা তৈরি করবে। পরবর্তী বড় রিলিজে AGP থেকে অবহেলিত APIগুলি সম্পূর্ণরূপে সরানো হবে। উদাহরণস্বরূপ, যদি AGP 7.0-এ একটি API অবচয় করা হয়, তবে এটি সেই সংস্করণে উপলব্ধ হবে এবং সতর্কতা তৈরি করবে। সেই API আর AGP 8.0 এ উপলব্ধ হবে না।

সাধারণ বিল্ড কাস্টমাইজেশনে ব্যবহৃত নতুন API-এর উদাহরণ দেখতে, Android Gradle প্লাগইন রেসিপিগুলি দেখুন। তারা সাধারণ বিল্ড কাস্টমাইজেশনের উদাহরণ প্রদান করে। আপনি আমাদের রেফারেন্স ডকুমেন্টেশনে নতুন API সম্পর্কে আরও বিশদ জানতে পারেন।

গ্রেডল বিল্ড বেসিক

এই নির্দেশিকা পুরো গ্রেডল বিল্ড সিস্টেমকে কভার করে না। যাইহোক, এটি আপনাকে আমাদের API-এর সাথে সংহত করতে সাহায্য করার জন্য ন্যূনতম প্রয়োজনীয় ধারণাগুলির সেট কভার করে এবং আরও পড়ার জন্য প্রধান গ্রেডল ডকুমেন্টেশনের সাথে লিঙ্ক করে।

আমরা গ্র্যাডল কীভাবে কাজ করে সে সম্পর্কে প্রাথমিক জ্ঞান ধরে নিই, যার মধ্যে কীভাবে প্রকল্পগুলি কনফিগার করা যায়, বিল্ড ফাইলগুলি সম্পাদনা করা যায়, প্লাগইনগুলি প্রয়োগ করা যায় এবং কাজগুলি চালানো যায়। AGP-এর ক্ষেত্রে Gradle-এর মূল বিষয়গুলি সম্পর্কে জানতে, আমরা আপনার বিল্ড কনফিগার করুন পর্যালোচনা করার পরামর্শ দিই। গ্রেডল প্লাগইনগুলি কাস্টমাইজ করার জন্য সাধারণ কাঠামো সম্পর্কে জানতে, কাস্টম গ্রেডল প্লাগইনগুলির বিকাশ দেখুন।

Gradle অলস প্রকার শব্দকোষ

Gradle অনেক ধরণের অফার করে যা "অলসভাবে" আচরণ করে বা ভারী গণনা বা Task তৈরিকে বিল্ডের পরবর্তী পর্যায়ে স্থগিত করতে সহায়তা করে। এই ধরনের অনেক Gradle এবং AGP API এর মূলে রয়েছে। নিম্নলিখিত তালিকায় অলস সম্পাদনের সাথে জড়িত প্রধান গ্রেডল প্রকারগুলি এবং তাদের মূল পদ্ধতিগুলি অন্তর্ভুক্ত রয়েছে।

Provider<T>
map() ব্যবহার করে T টাইপের একটি মান প্রদান করে (যেখানে "T" মানে যেকোনো প্রকার), যা এক্সিকিউশন ফেজ চলাকালীন get() ব্যবহার করে পড়া যায় অথবা একটি নতুন Provider<S> (যেখানে "S" মানে অন্য কোনো প্রকার) রূপান্তরিত করা যায়। map() , flatMap() , এবং zip() পদ্ধতি। নোট করুন যে কনফিগারেশন পর্বের সময় get() কখনই কল করা উচিত নয়।
  • map() : একটি ল্যাম্বডা গ্রহণ করে এবং S টাইপের একটি Provider তৈরি করে, Provider<S> >। map() এর ল্যাম্বডা আর্গুমেন্ট T মান নেয় এবং মান S উৎপন্ন করে। ল্যাম্বডা অবিলম্বে মৃত্যুদন্ড কার্যকর করা হয় না; পরিবর্তে, ফলাফল Provider<S>get() কল করার মুহূর্ত পর্যন্ত এর সম্পাদন স্থগিত করা হয়, পুরো চেইনটিকে অলস করে তোলে।
  • flatMap() : এছাড়াও একটি ল্যাম্বডা গ্রহণ করে এবং Provider<S> তৈরি করে, কিন্তু ল্যাম্বডা একটি মান T নেয় এবং Provider<S> তৈরি করে (সরাসরি S মান তৈরি করার পরিবর্তে)। ফ্ল্যাটম্যাপ() ব্যবহার করুন যখন S কনফিগারেশনের সময় নির্ধারণ করা যায় না এবং আপনি শুধুমাত্র Provider<S> পেতে পারেন। ব্যবহারিকভাবে বলতে গেলে, আপনি যদি map() ব্যবহার করেন এবং একটি Provider<Provider<S>> ফলাফলের ধরন দিয়ে শেষ করেন, তাহলে সম্ভবত এর অর্থ হল আপনার পরিবর্তে flatMap() ব্যবহার করা উচিত ছিল।
  • zip() : আপনাকে দুটি Provider ইন্সট্যান্স একত্রিত করে একটি নতুন Provider তৈরি করতে দেয়, একটি ফাংশন ব্যবহার করে কম্পিউট করা একটি মান সহ যা দুটি ইনপুট Providers ইনস্ট্যান্সের মানগুলিকে একত্রিত করে।
Property<T>
Provider<T> প্রয়োগ করে, তাই এটি T টাইপের একটি মানও প্রদান করে। Provider<T> এর বিপরীতে, যা শুধুমাত্র পঠনযোগ্য, আপনি Property<T> এর জন্য একটি মানও সেট করতে পারেন। এটি করার দুটি উপায় আছে:
  • বিলম্বিত গণনার প্রয়োজন ছাড়াই সরাসরি T টাইপের মান সেট করুন যখন এটি উপলব্ধ থাকে।
  • অন্য একটি Provider<T> Property<T> >। এই ক্ষেত্রে, T মানটি তখনই বাস্তবায়িত হয় যখন Property.get() বলা হয়।
TaskProvider
প্রয়োগকারী Provider<Task> । একটি TaskProvider তৈরি করতে, tasks.register() ব্যবহার করুন এবং tasks.create() নয়, নিশ্চিত করুন যে কাজগুলি যখন প্রয়োজন হয় তখনই অলসভাবে তাত্ক্ষণিক হয়। Task তৈরি হওয়ার আগে আপনি একটি Task আউটপুট অ্যাক্সেস করতে flatMap() ব্যবহার করতে পারেন, আপনি যদি অন্যান্য Task উদাহরণে ইনপুট হিসাবে আউটপুট ব্যবহার করতে চান তবে এটি কার্যকর হতে পারে।

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

প্রদানকারীরাও টাস্ক নির্ভরতার তথ্য বহন করে। আপনি যখন একটি Task আউটপুট রূপান্তর করে একটি Provider তৈরি করেন, তখন সেই Task Provider একটি অন্তর্নিহিত নির্ভরতা হয়ে ওঠে এবং যখনই Provider মান সমাধান করা হয়, যেমন অন্য একটি Task প্রয়োজন হলে তা তৈরি এবং চালানো হবে৷

এখানে দুটি টাস্ক নিবন্ধন করার একটি উদাহরণ দেওয়া হল, GitVersionTask এবং ManifestProducerTask , যখন Task দৃষ্টান্তগুলি বাস্তবে প্রয়োজন না হওয়া পর্যন্ত তৈরি করা স্থগিত করে৷ ManifestProducerTask ইনপুট মান GitVersionTask এর আউটপুট থেকে প্রাপ্ত একটি Provider জন্য সেট করা হয়, তাই ManifestProducerTask সম্পূর্ণরূপে GitVersionTask উপর নির্ভর করে।

// Register a task lazily to get its TaskProvider.
val gitVersionProvider: TaskProvider =
    project.tasks.register("gitVersionProvider", GitVersionTask::class.java) {
        it.gitVersionOutputFile.set(
            File(project.buildDir, "intermediates/gitVersionProvider/output")
        )
    }

...

/**
 * Register another task in the configuration block (also executed lazily,
 * only if the task is required).
 */
val manifestProducer =
    project.tasks.register(variant.name + "ManifestProducer", ManifestProducerTask::class.java) {
        /**
         * Connect this task's input (gitInfoFile) to the output of
         * gitVersionProvider.
         */
        it.gitInfoFile.set(gitVersionProvider.flatMap(GitVersionTask::gitVersionOutputFile))
    }

এই দুটি কাজ শুধুমাত্র তখনই কার্যকর হবে যদি তাদের স্পষ্টভাবে অনুরোধ করা হয়। এটি একটি Gradle আহ্বানের অংশ হিসাবে ঘটতে পারে, উদাহরণস্বরূপ, যদি আপনি ./gradlew debugManifestProducer চালান, অথবা যদি ManifestProducerTask এর আউটপুট অন্য কোনও কাজের সাথে সংযুক্ত থাকে এবং এর মান প্রয়োজন হয়।

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

গ্রেডল বিল্ড ফেজ

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

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

কনফিগারেশন ফেজ

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

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

মৃত্যুদন্ডের পর্যায়

সঞ্চালন পর্বে, অনুরোধকৃত কাজ এবং তাদের নির্ভরশীল কাজগুলি সম্পাদিত হয়। বিশেষভাবে, @TaskAction দিয়ে চিহ্নিত Task ক্লাস পদ্ধতি(গুলি) কার্যকর করা হয়। কার্য সম্পাদনের সময়, আপনাকে ইনপুটগুলি (যেমন ফাইলগুলি) থেকে পড়তে এবং Provider<T>.get() কল করে অলস সরবরাহকারীদের সমাধান করার অনুমতি দেওয়া হয়। এইভাবে অলস প্রদানকারীদের সমাধান করা map() বা flatMap() কলগুলির একটি ক্রম বন্ধ করে যা প্রদানকারীর মধ্যে থাকা টাস্ক নির্ভরতা তথ্য অনুসরণ করে। প্রয়োজনীয় মানগুলি বাস্তবায়িত করার জন্য কাজগুলি অলসভাবে চালানো হয়।

ভেরিয়েন্ট এপিআই, আর্টিফ্যাক্টস এবং টাস্ক

ভেরিয়েন্ট এপিআই হল অ্যান্ড্রয়েড গ্রেডল প্লাগইন-এর একটি এক্সটেনশন মেকানিজম যা আপনাকে বিভিন্ন অপশন ম্যানিপুলেট করতে দেয়, সাধারণত বিল্ড কনফিগারেশন ফাইলে ডিএসএল ব্যবহার করে সেট করা হয়, যা অ্যান্ড্রয়েড বিল্ডকে প্রভাবিত করে। ভেরিয়েন্ট এপিআই আপনাকে মধ্যবর্তী এবং চূড়ান্ত আর্টিফ্যাক্টগুলিতে অ্যাক্সেস দেয় যা বিল্ড দ্বারা তৈরি করা হয়, যেমন ক্লাস ফাইল, মার্জ করা ম্যানিফেস্ট বা APK/AAB ফাইল।

অ্যান্ড্রয়েড বিল্ড ফ্লো এবং এক্সটেনশন পয়েন্ট

AGP এর সাথে ইন্টারঅ্যাক্ট করার সময়, সাধারণ গ্রেডল লাইফসাইকেল কলব্যাকগুলি নিবন্ধন করার পরিবর্তে বিশেষভাবে তৈরি এক্সটেনশন পয়েন্টগুলি ব্যবহার করুন (যেমন afterEvaluate() ) বা স্পষ্ট Task নির্ভরতা সেট আপ করুন৷ AGP দ্বারা তৈরি করা কার্যগুলিকে বাস্তবায়নের বিবরণ হিসাবে বিবেচনা করা হয় এবং একটি সর্বজনীন API হিসাবে প্রকাশ করা হয় না। আপনাকে অবশ্যই Task অবজেক্টের উদাহরণ পাওয়ার চেষ্টা করা বা Task নাম অনুমান করা এবং সরাসরি সেই Task অবজেক্টগুলিতে কলব্যাক বা নির্ভরতা যুক্ত করা এড়াতে হবে।

এজিপি তার Task ইন্সট্যান্স তৈরি এবং কার্যকর করার জন্য নিম্নলিখিত ধাপগুলি সম্পন্ন করে, যার ফলে বিল্ড আর্টিফ্যাক্ট তৈরি হয়। Variant অবজেক্ট তৈরিতে জড়িত প্রধান পদক্ষেপগুলি কলব্যাক দ্বারা অনুসরণ করা হয় যা আপনাকে একটি বিল্ডের অংশ হিসাবে তৈরি করা নির্দিষ্ট বস্তুতে পরিবর্তন করতে দেয়। এটা মনে রাখা গুরুত্বপূর্ণ যে সমস্ত কলব্যাক কনফিগারেশন পর্বের সময় ঘটে (এই পৃষ্ঠায় বর্ণিত) এবং দ্রুত চালাতে হবে, পরিবর্তে কার্যকরী পর্বের সময় সঠিক Task উদাহরণে যেকোন জটিল কাজকে স্থগিত করে।

  1. ডিএসএল পার্সিং : এটি যখন বিল্ড স্ক্রিপ্টগুলি মূল্যায়ন করা হয় এবং যখন android ব্লক থেকে অ্যান্ড্রয়েড ডিএসএল অবজেক্টের বিভিন্ন বৈশিষ্ট্য তৈরি এবং সেট করা হয়। নিম্নলিখিত বিভাগে বর্ণিত ভেরিয়েন্ট API কলব্যাকগুলিও এই পর্যায়ে নিবন্ধিত হয়৷
  2. finalizeDsl() : কলব্যাক যা আপনাকে DSL অবজেক্টগুলিকে কম্পোনেন্ট (ভেরিয়েন্ট) তৈরির জন্য লক করার আগে পরিবর্তন করতে দেয়। VariantBuilder অবজেক্টগুলি ডিএসএল অবজেক্টে থাকা ডেটার উপর ভিত্তি করে তৈরি করা হয়।

  3. DSL লকিং : DSL এখন লক করা হয়েছে এবং পরিবর্তন আর সম্ভব নয়।

  4. beforeVariants() : এই কলব্যাক VariantBuilder মাধ্যমে কোন উপাদানগুলি তৈরি করা হয়েছে এবং তাদের কিছু বৈশিষ্ট্যকে প্রভাবিত করতে পারে। এটি এখনও বিল্ড প্রবাহ এবং উত্পাদিত শিল্পকর্মে পরিবর্তনের অনুমতি দেয়।

  5. বৈকল্পিক সৃষ্টি : তৈরি করা হবে এমন উপাদান এবং শিল্পকর্মের তালিকা এখন চূড়ান্ত করা হয়েছে এবং পরিবর্তন করা যাবে না।

  6. onVariants() : এই কলব্যাকে, আপনি তৈরি Variant অবজেক্টগুলিতে অ্যাক্সেস পাবেন এবং আপনি অলসভাবে গণনা করার জন্য তাদের মধ্যে থাকা Property মানগুলির জন্য মান বা প্রদানকারী সেট করতে পারেন।

  7. বৈকল্পিক লকিং : বৈকল্পিক বস্তুগুলি এখন লক করা হয়েছে এবং পরিবর্তনগুলি আর সম্ভব নয়।

  8. তৈরি করা টাস্ক : Variant অবজেক্ট এবং তাদের Property মানগুলি বিল্ড করার জন্য প্রয়োজনীয় Task ইনস্ট্যান্স তৈরি করতে ব্যবহৃত হয়।

AGP একটি AndroidComponentsExtension চালু করেছে যা আপনাকে finalizeDsl() , beforeVariants() এবং onVariants() এর জন্য কলব্যাক নিবন্ধন করতে দেয়। এক্সটেনশনটি androidComponents ব্লকের মাধ্যমে বিল্ড স্ক্রিপ্টে পাওয়া যায়:

// This is used only for configuring the Android build through DSL.
android { ... }

// The androidComponents block is separate from the DSL.
androidComponents {
   finalizeDsl { extension ->
      ...
   }
}

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

abstract class ExamplePlugin: Plugin<Project> {

    override fun apply(project: Project) {
        val androidComponents = project.extensions.getByType(AndroidComponentsExtension::class.java)
        androidComponents.finalizeDsl { extension ->
            ...
        }
    }
}

আসুন উপলভ্য কলব্যাকগুলি এবং আপনার প্লাগইন তাদের প্রতিটিতে সমর্থন করতে পারে এমন ব্যবহারের ধরনগুলি ঘনিষ্ঠভাবে দেখে নেওয়া যাক:

finalizeDsl(callback: (DslExtensionT) -> Unit)

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

abstract class ExamplePlugin: Plugin<Project> {

    override fun apply(project: Project) {
        val androidComponents = project.extensions.getByType(AndroidComponentsExtension::class.java)
        androidComponents.finalizeDsl { extension ->
            extension.buildTypes.create("extra").let {
                it.isJniDebuggable = true
            }
        }
    }
}

beforeVariants()

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

androidComponents {
    beforeVariants { variantBuilder ->
        variantBuilder.minSdk = 23
    }
}

beforeVariants() কলব্যাকটি ঐচ্ছিকভাবে একটি VariantSelector নেয়, যা আপনি androidComponentsExtensionselector() পদ্ধতির মাধ্যমে পেতে পারেন। আপনি কলব্যাক আহ্বানে অংশগ্রহণকারী উপাদানগুলিকে তাদের নাম, বিল্ড টাইপ বা পণ্যের স্বাদের উপর ভিত্তি করে ফিল্টার করতে এটি ব্যবহার করতে পারেন।

androidComponents {
    beforeVariants(selector().withName("adfree")) { variantBuilder ->
        variantBuilder.minSdk = 23
    }
}

onVariants()

onVariants() কল করার সময়, AGP দ্বারা তৈরি করা সমস্ত আর্টিফ্যাক্টগুলি ইতিমধ্যেই সিদ্ধান্ত নেওয়া হয়েছে যাতে আপনি সেগুলি আর নিষ্ক্রিয় করতে পারবেন না। যাইহোক, আপনি Variant অবজেক্টে Property বৈশিষ্ট্যের জন্য সেট করে কাজের জন্য ব্যবহৃত কিছু মান পরিবর্তন করতে পারেন। কারণ Property মানগুলি শুধুমাত্র তখনই সমাধান করা হবে যখন AGP-এর কাজগুলি সম্পাদিত হয়, আপনি নিরাপদে সেগুলিকে আপনার নিজস্ব কাস্টম কাজগুলি থেকে সরবরাহকারীদের কাছে সংযুক্ত করতে পারেন যা ফাইল বা নেটওয়ার্কের মতো বাহ্যিক ইনপুটগুলি থেকে পড়া সহ যে কোনও প্রয়োজনীয় গণনা সম্পাদন করবে৷

// onVariants also supports VariantSelectors:
onVariants(selector().withBuildType("release")) { variant ->
    // Gather the output when we are in single mode (no multi-apk).
    val mainOutput = variant.outputs.single { it.outputType == OutputType.SINGLE }

    // Create version code generating task
    val versionCodeTask = project.tasks.register("computeVersionCodeFor${variant.name}", VersionCodeTask::class.java) {
        it.outputFile.set(project.layout.buildDirectory.file("${variant.name}/versionCode.txt"))
    }
    /**
     * Wire version code from the task output.
     * map() will create a lazy provider that:
     * 1. Runs just before the consumer(s), ensuring that the producer
     * (VersionCodeTask) has run and therefore the file is created.
     * 2. Contains task dependency information so that the consumer(s) run after
     * the producer.
     */
    mainOutput.versionCode.set(versionCodeTask.map { it.outputFile.get().asFile.readText().toInt() })
}

বিল্ডে উত্পন্ন উত্স অবদান

আপনার প্লাগইন কয়েকটি ধরণের উত্পন্ন উত্সগুলিতে অবদান রাখতে পারে, যেমন:

আপনি যোগ করতে পারেন এমন উত্সগুলির সম্পূর্ণ তালিকার জন্য, উত্স API দেখুন৷

এই কোড স্নিপেটটি দেখায় কিভাবে addStaticSourceDirectory() ফাংশন ব্যবহার করে জাভা সোর্স সেটে ${variant.name} নামে একটি কাস্টম সোর্স ফোল্ডার যোগ করতে হয়। অ্যান্ড্রয়েড টুলচেন তারপর এই ফোল্ডারটি প্রক্রিয়া করে।

onVariants { variant ->
    variant.sources.java?.let { java ->
        java.addStaticSourceDirectory("custom/src/kotlin/${variant.name}")
    }
}

আরো বিস্তারিত জানার জন্য addJavaSource রেসিপি দেখুন।

এই কোড স্নিপেটটি দেখায় যে কীভাবে একটি কাস্টম টাস্ক থেকে res সোর্স সেটে তৈরি হওয়া অ্যান্ড্রয়েড সংস্থানগুলির সাথে একটি ডিরেক্টরি যুক্ত করতে হয়৷ প্রক্রিয়া অন্যান্য উত্স ধরনের জন্য অনুরূপ.

onVariants(selector().withBuildType("release")) { variant ->
    // Step 1. Register the task.
    val resCreationTask =
       project.tasks.register<ResCreatorTask>("create${variant.name}Res")

    // Step 2. Register the task output to the variant-generated source directory.
    variant.sources.res?.addGeneratedSourceDirectory(
       resCreationTask,
       ResCreatorTask::outputDirectory)
    }

...

// Step 3. Define the task.
abstract class ResCreatorTask: DefaultTask() {
   @get:OutputFiles
   abstract val outputDirectory: DirectoryProperty

   @TaskAction
   fun taskAction() {
      // Step 4. Generate your resources.
      ...
   }
}

আরো বিস্তারিত জানার জন্য addCustomAsset রেসিপি দেখুন।

আর্টিফ্যাক্টগুলি অ্যাক্সেস এবং সংশোধন করুন

Variant অবজেক্টে আপনাকে সাধারণ বৈশিষ্ট্যগুলি পরিবর্তন করতে দেওয়ার পাশাপাশি, এজিপি-তে একটি এক্সটেনশন মেকানিজমও রয়েছে যা আপনাকে বিল্ডের সময় উত্পাদিত মধ্যবর্তী এবং চূড়ান্ত শিল্পকর্মগুলি পড়তে বা রূপান্তর করতে দেয়। উদাহরণস্বরূপ, আপনি একটি কাস্টম Task চূড়ান্ত, মার্জ করা AndroidManifest.xml ফাইলের বিষয়বস্তুগুলিকে বিশ্লেষণ করতে পড়তে পারেন, অথবা আপনি এটির বিষয়বস্তু সম্পূর্ণরূপে আপনার কাস্টম Task দ্বারা তৈরি করা একটি ম্যানিফেস্ট ফাইলের সাথে প্রতিস্থাপন করতে পারেন৷

আপনি Artifact ক্লাসের জন্য রেফারেন্স ডকুমেন্টেশনে বর্তমানে সমর্থিত শিল্পকর্মের তালিকা খুঁজে পেতে পারেন। প্রতিটি আর্টিফ্যাক্ট প্রকারের নির্দিষ্ট বৈশিষ্ট্য রয়েছে যা জানার জন্য দরকারী:

কার্ডিনালিটি

একটি Artifact মূলত্ব তার FileSystemLocation দৃষ্টান্তের সংখ্যা, বা আর্টিফ্যাক্ট প্রকারের ফাইল বা ডিরেক্টরির সংখ্যা উপস্থাপন করে। আপনি একটি আর্টিফ্যাক্টের মূল শ্রেণী পরীক্ষা করে তার মূল বৈশিষ্ট্য সম্পর্কে তথ্য পেতে পারেন: একটি একক FileSystemLocation সহ আর্টিফ্যাক্টগুলি Artifact.Single এর একটি সাবক্লাস হবে; একাধিক FileSystemLocation ইনস্ট্যান্স সহ আর্টিফ্যাক্টগুলি Artifact.Multiple এর একটি সাবক্লাস হবে।

FileSystemLocation ধরন

আপনি একটি Artifact ফাইল বা ডিরেক্টরি প্রতিনিধিত্ব করে কিনা তা পরীক্ষা করতে পারেন তার প্যারামিটারাইজড FileSystemLocation ধরনটি দেখে, যা একটি RegularFile বা একটি Directory হতে পারে।

সমর্থিত অপারেশন

প্রতিটি Artifact ক্লাস এটি কোন ক্রিয়াকলাপগুলিকে সমর্থন করে তা নির্দেশ করতে নিম্নলিখিত যে কোনও ইন্টারফেস প্রয়োগ করতে পারে:

  • Transformable : একটি Artifact একটি Task একটি ইনপুট হিসাবে ব্যবহার করার অনুমতি দেয় যা এটিতে নির্বিচারে রূপান্তর করে এবং Artifact একটি নতুন সংস্করণ আউটপুট করে।
  • Appendable : শুধুমাত্র আর্টিফ্যাক্টের জন্য প্রযোজ্য যেগুলি Artifact.Multiple এর সাবক্লাস। এর মানে হল যে Artifact যুক্ত করা যেতে পারে, অর্থাৎ, একটি কাস্টম Task এই Artifact ধরণের নতুন উদাহরণ তৈরি করতে পারে যা বিদ্যমান তালিকায় যোগ করা হবে।
  • Replaceable : শুধুমাত্র সেই শিল্পকর্মের ক্ষেত্রে প্রযোজ্য যা Artifact.Single এর সাবক্লাস। একটি প্রতিস্থাপনযোগ্য Artifact একটি সম্পূর্ণ নতুন উদাহরণ দ্বারা প্রতিস্থাপিত হতে পারে, একটি Task আউটপুট হিসাবে উত্পাদিত হয়।

তিনটি আর্টিফ্যাক্ট-সংশোধন অপারেশন ছাড়াও, প্রতিটি আর্টিফ্যাক্ট একটি get() (অথবা getAll() ) অপারেশন সমর্থন করে, যা আর্টিফ্যাক্টের চূড়ান্ত সংস্করণ সহ একটি Provider ফেরত দেয় (এর উপর সমস্ত অপারেশন শেষ হওয়ার পরে)।

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

নিবন্ধন কার্যক্রমের এন্ট্রি পয়েন্ট হল Artifacts ক্লাস। নিচের কোড স্নিপেট দেখায় কিভাবে আপনি onVariants() কলব্যাকে Variant অবজেক্টের একটি প্রপার্টি থেকে Artifacts একটি উদাহরণে অ্যাক্সেস পেতে পারেন।

তারপরে আপনি একটি TaskBasedOperation অবজেক্ট (1) পেতে আপনার কাস্টম TaskProvider পাস করতে পারেন, এবং wiredWith* পদ্ধতি (2) ব্যবহার করে এর ইনপুট এবং আউটপুট সংযোগ করতে এটি ব্যবহার করতে পারেন।

আপনাকে যে সঠিক পদ্ধতিটি বেছে নিতে হবে তা নির্ভর করে আপনি রূপান্তর করতে চান এমন Artifact দ্বারা বাস্তবায়িত কার্ডিনালিটি এবং FileSystemLocation প্রকারের উপর।

এবং পরিশেষে, আপনি Artifact টাইপ একটি পদ্ধতিতে পাস করেন যেটি *OperationRequest অবজেক্টে নির্বাচিত অপারেশনের প্রতিনিধিত্ব করে যা আপনি বিনিময়ে পাবেন, উদাহরণস্বরূপ, toAppendTo() , toTransform() , অথবা toCreate() (3)।

androidComponents.onVariants { variant ->
    val manifestUpdater = // Custom task that will be used for the transform.
            project.tasks.register(variant.name + "ManifestUpdater", ManifestTransformerTask::class.java) {
                it.gitInfoFile.set(gitVersionProvider.flatMap(GitVersionTask::gitVersionOutputFile))
            }
    // (1) Register the TaskProvider w.
    val variant.artifacts.use(manifestUpdater)
         // (2) Connect the input and output files.
        .wiredWithFiles(
            ManifestTransformerTask::mergedManifest,
            ManifestTransformerTask::updatedManifest)
        // (3) Indicate the artifact and operation type.
        .toTransform(SingleArtifact.MERGED_MANIFEST)
}

এই উদাহরণে, MERGED_MANIFEST হল একটি SingleArtifact , এবং এটি একটি RegularFile । সেই কারণে, আমাদের wiredWithFiles পদ্ধতি ব্যবহার করতে হবে, যা ইনপুটের জন্য একটি একক RegularFileProperty রেফারেন্স এবং আউটপুটের জন্য একটি একক RegularFileProperty গ্রহণ করে। TaskBasedOperation ক্লাসে অন্যান্য wiredWith* পদ্ধতি রয়েছে যা Artifact cardinality এবং FileSystemLocation প্রকারের অন্যান্য সমন্বয়ের জন্য কাজ করবে।

AGP প্রসারিত করার বিষয়ে আরও জানতে, আমরা Gradle বিল্ড সিস্টেম ম্যানুয়াল থেকে নিম্নলিখিত বিভাগগুলি পড়ার পরামর্শ দিই: