Jetpack Keep আপনাকে আলাদা আলাদা Keep রুল ফাইলের পরিবর্তে সরাসরি আপনার Kotlin বা Java সোর্স কোডে অ্যানোটেশন সহ রিফ্লেকশন এবং Java Native Interface (JNI) ব্যবহার ঘোষণা করতে দেয়। Jetpack Keep Gradle প্লাগ-ইন বিল্ড করার সময় এই অ্যানোটেশনগুলি পড়ে এবং R8-এর জন্য প্রয়োজনীয় নিয়ম তৈরি করে।
আপনার কোডবেস পরিবর্তন হওয়ার সাথে সাথে Jetpack Keep, R8 অপ্টিমাইজেশনকে কার্যকর রাখতে সাহায্য করে:
যে কোডের জন্য নিয়ম প্রয়োজন, তার ঠিক পরেই নিয়ম রাখুন। আপনি যখন রিফ্লেকশন ব্যবহার করে এমন কোডের নাম পরিবর্তন করেন বা মুছে দেন, তখন আপনি একই জায়গায় অ্যানোটেশন আপডেট করেন। সিঙ্ক করার জন্য আলাদা কোনও রুল ফাইল নেই।
জেনারেট করা নিয়ম শর্তসাপেক্ষ। R8 শুধুমাত্র তখনই রিফ্লেক্টিভ কলের টার্গেট রাখে যদি এটি কল করার কোডও রাখে। কলিং কোড অব্যবহৃত থাকলে এবং R8 সেটি সরিয়ে দিলে (ঐচ্ছিক লাইব্রেরি ফিচারের ক্ষেত্রে প্রায়ই এটি হয়), R8 টার্গেটও সরিয়ে দিতে পারে, যাতে অব্যবহৃত ফিচারের নিয়ম অপ্রয়োজনীয় কোড যোগ না করে।
Lint আপনার জন্য প্রতিফলন খুঁজে দেয়। লাইব্রেরিতে লিন্ট চেক অন্তর্ভুক্ত থাকে যা অ্যানোটেট করা হয়নি এমন রিফ্লেক্টিভ কল ফ্ল্যাগ করে এবং যা অটোমেটিক অনেক অ্যানোটেশন যোগ করতে পারে।
Jetpack Keep কীভাবে কাজ করে
এমন একটি ফাংশন বিবেচনা করুন যা নাম অনুসারে একটি ক্লাস ইনস্ট্যান্টিয়েট করে। R8 যদি বুঝতে না পারে যে আপনার কোড ক্লাস ব্যবহার করে, তাহলে এটি ক্লাসের কনস্ট্রাক্টর সরিয়ে দেয় এবং রানটাইমে অ্যাপ ক্র্যাশ করে। ক্র্যাশ হওয়া আটকাতে, আপনার 'কিপ রুল' ফাইলে নিচের মতো নিয়ম যোগ করতে পারেন:
# Unconditional: R8 keeps this constructor even if it removes the reflection that invokes it.
-keep class com.example.ResourceLocatorImpl {
<init>();
}
Jetpack Keep-এর মাধ্যমে, আপনি সেই ফাংশন অ্যানোটেট করেন যা রিফ্লেকশন পারফর্ম করে পরিবর্তে:
// ResourceLocatorImpl isn't on this module's compile classpath, so both the
// annotation and the code refer to it by name.
@UsesReflectionToConstruct(
className = "com.example.ResourceLocatorImpl",
// Omitting parameterTypes keeps every constructor. An empty array keeps
// only the no-argument constructor, which the code calls.
parameterTypes = [],
)
fun loadResourceLocator(): ResourceLocator {
val implClass = Class.forName("com.example.ResourceLocatorImpl")
return implClass.getDeclaredConstructor().newInstance() as ResourceLocator
}
বিল্ড করার সময়, Gradle প্লাগ-ইন কনস্ট্রাক্টরের জন্য একটি 'কিপ' নিয়ম তৈরি করে।
আগের ম্যানুয়ালি লেখা নিয়মের মতো নয়, জেনারেট করা নিয়মটি শর্তসাপেক্ষ:
R8 কনস্ট্রাক্টরটি তখনই রাখে যদি loadResourceLocator অপ্টিমাইজ করা অ্যাপে
থাকে।
প্রয়োজনীয়তা
Jetpack Keep ব্যবহার করতে, আপনার প্রোজেক্টে Android Gradle প্লাগ-ইন (AGP) 8.7 বা তার পরবর্তী যেকোনও ভার্সন থাকতে হবে।
Gradle প্লাগ-ইন নিম্নলিখিত ধরনের মডিউলে কাজ করে:
| মডিউলের ধরন | মডিউল যে প্লাগ-ইন প্রয়োগ করে | অতিরিক্ত সেট-আপ |
|---|---|---|
| Android অ্যাপ | com.android.application |
কোনওটিই নয় |
| Android লাইব্রেরি | com.android.library |
কোনওটিই নয় |
| Android টার্গেট সহ Kotlin Multiplatform লাইব্রেরি | com.android.kotlin.multiplatform.library |
কোনওটিই নয় |
| JVM লাইব্রেরি (Java বা Kotlin) | java-library বা org.jetbrains.kotlin.jvm |
JAR ট্রান্সফর্ম রেজিস্টার করা |
Jetpack Keep সেট-আপ করুন
Jetpack Keep ব্যবহার করতে, প্রতিফলন বা JNI ব্যবহার করে এমন প্রতিটি মডিউলে লাইব্রেরি এবং Gradle প্লাগ-ইন উভয়ই যোগ করুন। লাইব্রেরি অ্যানোটেশন ও লিন্ট চেক প্রদান করে এবং প্লাগ-ইন অ্যানোটেশনকে কিপ রুলে পরিণত করে।
আপনার
libs.versions.tomlফাইলে, লাইব্রেরি ও প্লাগ-ইন যোগ করুন। লাইব্রেরি এবং প্লাগ-ইন একই ভার্সন শেয়ার করে:[versions] # The library and the plugin ship together. Use one version for both so # that they stay in sync when you upgrade. annotationKeep = "1.0.0-alpha01" [libraries] androidx-annotation-keep = { group = "androidx.annotation.keep", name = "keep", version.ref = "annotationKeep" } [plugins] androidx-annotation-keep = { id = "androidx.annotation.keep", version.ref = "annotationKeep" }Gradle, Google-এর Maven রিপোজিটরি থেকে প্লাগ-ইন ডাউনলোড করে, তাই নিশ্চিত করুন যে আপনার
settings.gradle.ktsফাইলেরpluginManagementরিপোজিটরিতেgoogle()অন্তর্ভুক্ত আছে।আপনার রুট
build.gradle.ktsফাইলে, AGP-এর পাশাপাশি প্লাগ-ইন ঘোষণা করুন এটি প্রয়োগ না করেই:plugins { alias(libs.plugins.android.application) apply false // Other plugins. // The plugin relies on AGP classes that it doesn't bundle, so declare // it next to AGP. Declaring it here also gives every module the same // plugin version. alias(libs.plugins.androidx.annotation.keep) apply false }রিফ্লেকশন বা JNI ব্যবহার করে এমন প্রতিটি মডিউলের
build.gradle.ktsফাইলে, প্লাগ-ইন প্রয়োগ করুন এবং লাইব্রেরি ডিপেন্ডেন্সি যোগ করুন:plugins { alias(libs.plugins.android.application) // The plugin reads annotations only from this module's own code, so // apply it in every module that uses Jetpack Keep. alias(libs.plugins.androidx.annotation.keep) } dependencies { implementation(libs.androidx.annotation.keep) }Gradle ফাইলের সাথে আপনার প্রোজেক্ট সিঙ্ক করুন।
Android অ্যাপ মডিউলের প্রতিটি বিল্ড ভ্যারিয়েন্টের জন্য, প্লাগ-ইন একটি
<variant>ExtractKeepRules টাস্ক রেজিস্টার করে, যেমন releaseExtractKeepRules এবং
সেই ভ্যারিয়েন্টের জন্য তৈরি করা নিয়ম R8-এ পাস করে। লাইব্রেরি মডিউলের জন্য, প্লাগ-ইন
তৈরি করা নিয়মগুলিকে লাইব্রেরির আউটপুটে গ্রাহকের রাখা নিয়ম হিসেবে প্যাকেজ করে।
JVM লাইব্রেরি মডিউলের জন্য অতিরিক্ত সেট-আপ প্রয়োজন।
লিন্ট সহ প্রতিফলন খুঁজুন
Jetpack Keep-এ নিম্নলিখিত লিন্ট চেক অন্তর্ভুক্ত রয়েছে:
ReflectionWithoutKeepAnnotationsClass.forName,getDeclaredConstructor,getDeclaredMethodএবংgetDeclaredField-এর মতো রিফ্লেক্টিভ কল ফ্ল্যাগ করে, যেগুলির সাথে ম্যাচিং কিপ অ্যানোটেশন নেই।MissingKeepAnnotationPlugin- ফ্ল্যাগ মডিউল যা
androidx.annotation.keep:keep-এর উপর নির্ভর করে কিন্তুandroidx.annotation.keepGradle প্লাগ-ইন প্রয়োগ করে না। প্লাগ-ইন ছাড়া, আপনার বিল্ড অ্যানোটেশন থেকে রাখার নিয়ম তৈরি করে না।
Android মডিউল অ্যানোটেট করতে লিন্ট ব্যবহার করতে, এই ধাপগুলি অনুসরণ করুন:
অ্যানোটেশন প্রয়োজন এমন রিফ্লেক্টিভ কল খুঁজে পেতে মডিউলে লিন্ট রান করুন:
./gradlew :app:lintলিন্ট অটোমেটিক যেসব সমাধান করতে পারে সেগুলি প্রয়োগ করুন:
./gradlew :app:lintFixকোডটি কোন ক্লাস এবং মেম্বারকে প্রতিফলিত করে তা নির্ধারণ করতে পারলে, Lint অটোমেটিক একটি অ্যানোটেশন যোগ করতে পারে।
আপনার কোড যা করে তার সাথে এটি মিলছে কিনা তা কনফার্ম করতে যোগ করা প্রতিটি অ্যানোটেশন পর্যালোচনা করুন। যেমন, প্যারামিটারের ধরন আপনার কোড কল করে এমন কনস্ট্রাক্টর বা মেথডের সাথে মিলছে কিনা তা চেক করুন।
Android Studio-তে বাকি ফলাফলগুলি সমাধান করুন। লিন্ট যখন টার্গেট ক্লাস নির্ধারণ করতে পারে না, তখন কুইক ফিক্স আপনার সম্পূর্ণ করার জন্য
TODOপ্লেসহোল্ডার সহ অ্যানোটেশন যোগ করে। এছাড়াও, আপনি ম্যানুয়ালি অ্যানোটেশন যোগ করতে পারবেন।যেমন, নিম্নলিখিত ফাংশনটি প্যারামিটার হিসেবে ক্লাসের নাম গ্রহণ করে, তাই লিন্ট এটি কোন ক্লাস তৈরি করে তা নির্ধারণ করতে পারে না। কুইক ফিক্স প্লেসহোল্ডার সহ একটি অ্যানোটেশন যোগ করে:
@UsesReflectionToConstruct( className = TODO(), parameterTypes = [] ) fun createInstance(className: String): Any = Class.forName(className).getDeclaredConstructor().newInstance()আপনার কোড যে ক্লাস তৈরি করে তার নাম দিয়ে প্লেসহোল্ডারটি প্রতিস্থাপন করুন। কম্পাইল করার সময় ক্লাস উপলভ্য থাকলে,
className-এর পরিবর্তেclassConstantব্যবহার করুন। Kotlin-এ, আপনিTODO()পরিবর্তন না করা পর্যন্ত আপনার কোড কম্পাইল হয় না।আগে থেকে থাকা কোনও নিয়ম সরানোর আগে, আপনার অ্যানোটেশন থেকে প্লাগ-ইন কোন কোন নিয়ম তৈরি করে তা চেক করুন। অ্যাপ মডিউলের জন্য, রিলিজ ভেরিয়েন্টের জন্য এক্সট্র্যাকশন টাস্ক রান করুন:
./gradlew :app:releaseExtractKeepRulesপ্লাগ-ইনটি
app/build/generated/release/androidx.annotation.keep.rules.pro-এ নিয়মগুলি লেখে। লাইব্রেরি মডিউলের জন্য, জেনারেট করা নিয়ম যাচাই করুন দেখুন।আপনার 'কিপ' রুল ফাইল থেকে নতুন অ্যানোটেশন যে 'কিপ' রুলকে সরিয়ে দেয়, সেটি সরিয়ে দিন। এছাড়াও, কোনও রুল এমন প্রতিফলন কভার করতে পারে যা লিন্ট ফ্ল্যাগ করেনি, যেমন অন্য মডিউল বা লাইব্রেরিতে প্রতিফলন, তাই প্রতিটি রুল সরানোর আগে চেক করুন:
-keep class com.example.** { *; }-এর মতো প্যাকেজ-ওয়াইড নিয়মের ক্ষেত্রে, অ্যানোটেশন বা অন্যান্য নিয়ম প্যাকেজের ক্লাসের সব রিফ্লেকশন কভার করছে কিনা তা নিশ্চিত করুন।- নির্দিষ্ট ক্লাস বা মেম্বারদের রাখে এমন নিয়মের ক্ষেত্রে, এটি যাতে জেনারেট করা নিয়মের সাথে খুব কাছাকাছি মেলে বা অন্য কোনও রিফ্লেকশন এর উপর নির্ভর না করে তা নিশ্চিত করুন।
আপনার রিলিজ বিল্ড পরীক্ষা করুন।
আপনার কোড অ্যানোটেট করুন
রিফ্লেকশন পারফর্ম করে এমন কোডে Jetpack Keep অ্যানোটেশন যোগ করুন: ফাংশন, কনস্ট্রাক্টর, ফিল্ড বা ক্লাস যাতে রিফ্লেক্টিভ কল থাকে। The annotation describes the target of the reflection, and the generated rule keeps that target only if R8 keeps the annotated code.
আপনার কোড কী করে তার উপর ভিত্তি করে একটি অ্যানোটেশন বেছে নিন:
| টিকা | আপনার কোড ব্যবহার করুন |
|---|---|
@UsesReflectionToConstruct |
রিফ্লেকশনের মাধ্যমে কনস্ট্রাক্টর কল করে, যেমন
getDeclaredConstructor().newInstance()-এর সাথে। |
@UsesReflectionToAccessMethod |
রিফ্লেকশনের মাধ্যমে কোনও পদ্ধতিকে কল করে, যেমন
getMethod বা getDeclaredMethod-এর মাধ্যমে। |
@UsesReflectionToAccessField |
রিফ্লেকশনের মাধ্যমে কোনও ফিল্ড পড়ে বা লেখে, যেমন
getField বা getDeclaredField-এর মাধ্যমে। |
@UnconditionallyKeep |
আপনার অ্যাপের Kotlin বা Java কোডের বাইরে থেকে কল করা হয়, যেমন
JNI-এর মাধ্যমে নেটিভ কোড থেকে। শুধুমাত্র @UsesReflection* অ্যানোটেশন ব্যবহার করতে না পারলে,
এই অ্যানোটেশন ব্যবহার করুন, যা
শর্তসাপেক্ষ নিয়ম তৈরি করে। |
@UsesReflection*টি অ্যানোটেশনের সবকটিই এই কনভেনশন অনুসরণ করে:
classConstantবাclassName-এর সাহায্যে টার্গেট ক্লাস শনাক্ত করুন। কম্পাইল করার সময় ক্লাস উপলভ্য থাকলে,classConstantব্যবহার করুন। কম্পাইলার রেফারেন্স চেক করে এবং IDE রিফ্যাক্টরিং যেমন নাম পরিবর্তন করা, এটি আপডেট করে। কম্পাইল করার সময় ক্লাস উপলভ্য না থাকলে, শুধুমাত্র তখনইclassNameব্যবহার করুন, যেমন ঐচ্ছিক ডিপেন্ডেন্সিতে থাকা ক্লাস। এই দুটি প্যারামিটার একে অপরের থেকে আলাদা।- সবচেয়ে নির্দিষ্ট ক্লাস টার্গেট করুন। জেনারেট করা নিয়ম আপনার উল্লেখ করা ক্লাস এবং এর সাবক্লাস বা ইন্টারফেস ইমপ্লিমেন্টেশনে মেম্বারদের ম্যাচ করতে থাকে। ক্লাস যত বেশি সাধারণ হবে, R8 তত বেশি কোড রাখবে।
- একটি ওভারলোড রাখতে প্যারামিটারের ধরন নির্দিষ্ট করুন। আপনি
parameterTypesবাparameterTypeNamesসেট না করলে, নিয়মটি সব কনস্ট্রাক্টর বা পদ্ধতির সব ওভারলোড রেখে দেয়। শুধুমাত্র নো-আর্গুমেন্ট কনস্ট্রাক্টর বা পদ্ধতি রাখতে, একটি খালি অ্যারে পাস করুন। - একাধিক টার্গেটের জন্য অ্যানোটেশন রিপিট করুন। একটি ফাংশন একাধিক ক্লাস বা মেম্বারের উপর প্রভাব ফেললে, প্রতিটি টার্গেটের জন্য একটি করে অ্যানোটেশন যোগ করুন।
প্রতিফলন সহ অবজেক্ট তৈরি করো
আপনার কোড রিফ্লেকশনের মাধ্যমে কোনও ক্লাস ইনস্ট্যান্টিয়েট করলে @UsesReflectionToConstruct ব্যবহার করুন। নিচের উদাহরণে, একটি টাস্ক রানার নাম অনুসারে StartupTask ইন্টারফেসের
ইমপ্লিমেন্টেশন ইনস্ট্যান্টিয়েট করে। অ্যানোটেশন ইন্টারফেস নির্দিষ্ট করে বলে, জেনারেট করা নিয়ম প্রতিটি StartupTask ইমপ্লিমেন্টেশনের
নো-আর্গুমেন্ট কনস্ট্রাক্টর রেখে দেয়, যতক্ষণ R8 execute রেখে দেয়:
interface StartupTask {
fun run()
}
object TaskRunner {
// The task class is known only at runtime, so target the interface that
// every task implements.
@UsesReflectionToConstruct(classConstant = StartupTask::class, parameterTypes = [])
fun execute(className: String) {
val task = Class.forName(className)
.getDeclaredConstructor()
.newInstance() as StartupTask
task.run()
}
}
রিফ্লেকশন সহ মেথড কল করুন
আপনার কোড রিফ্লেকশনের মাধ্যমে কোনও মেথড কল করলে,
@UsesReflectionToAccessMethod ব্যবহার করুন। methodName-এর জায়গায় পদ্ধতির নাম লিখুন। নিচের
উদাহরণে, কোডটি একটি ঐচ্ছিক ডিপেন্ডেন্সিতে স্ট্যাটিক মেথড কল করে। কম্পাইল করার সময় ক্লাস
উপলভ্য নেই, তাই অ্যানোটেশন classConstant-এর পরিবর্তে
className ব্যবহার করে:
object CrashReporting {
@UsesReflectionToAccessMethod(
className = "com.example.crashlib.CrashReporter",
methodName = "log",
// Omitting parameterTypes keeps every log overload. Keep only the one
// that the code calls.
parameterTypes = [String::class],
)
fun log(message: String) {
val reporterClass = try {
Class.forName("com.example.crashlib.CrashReporter")
} catch (e: ClassNotFoundException) {
// Crash reporting is optional, so skip it instead of failing.
return
}
reporterClass.getMethod("log", String::class.java).invoke(null, message)
}
}
প্রতিফলন সহ অ্যাক্সেস ফিল্ড
আপনার কোড রিফ্লেকশনের মাধ্যমে কোনও ফিল্ড রিড বা রাইট করলে,
@UsesReflectionToAccessField ব্যবহার করুন। fieldName-কে ফিল্ডের নাম হিসেবে সেট করুন:
class UserProfileFetcher {
// User doesn't expose internalId, so reading it requires reflection.
@UsesReflectionToAccessField(classConstant = User::class, fieldName = "internalId")
fun readInternalId(user: User): Any? {
val field = User::class.java.getDeclaredField("internalId")
field.isAccessible = true
return field.get(user)
}
}
আপনার অ্যাপের বাইরে থেকে কল করা কোড রাখুন
আপনার অ্যাপের Kotlin বা Java কোডের বাইরে থেকে কল করা কোডের জন্য @UnconditionallyKeep ব্যবহার করুন, যেমন JNI-এর মাধ্যমে নেটিভ কোড কল করে এমন পদ্ধতি। যে
এলিমেন্টকে কল করা হয়েছে সেটিকে অ্যানোটেট করুন, যে কোড এটিকে কল করেছে সেটিকে নয়:
class NativeBridge {
// Only native code calls this method, through a JNI lookup by name that
// R8 can't see. Keep the method and its name unconditionally.
@UnconditionallyKeep
fun onNativeEvent(message: String) {
Log.d("NativeBridge", "Received from native code: $message")
}
// The default proguard-android-optimize.txt file already keeps native
// method names, so this method doesn't need an annotation.
external fun startNativeProcess()
}
জেনারেট করা নিয়মটি অ্যানোটেট করা পদ্ধতি এবং এর আসল নাম সহ ক্লাসটি রাখে। এটি অন্যান্য ক্লাস রাখে না যা পদ্ধতির স্বাক্ষর রেফারেন্স করে। নেটিভ কোড যদি সেইসব ক্লাসের উপর নির্ভর করে, তাহলে সেগুলিকেও অ্যানোটেট করুন অথবা সেগুলির জন্য নিয়ম রাখুন। JNI ও R8 সম্পর্কে আরও জানতে, JNI-এর মাধ্যমে নেটিভ কোডের সাথে ইন্টার্যাকশন দেখুন।
ডিফল্ট হিসেবে, @UnconditionallyKeep এলিমেন্টের নাম অপরিবর্তিত রাখে। এক্সটার্নাল কলার
নামের উপর নির্ভর না করলে, shouldPreserveName = false সেট করুন যাতে
R8 এলিমেন্টের নাম পরিবর্তন করতে পারে।
জেনারেট করা নিয়ম যাচাই করুন
এই বিভাগটি ব্যাখ্যা করে যে কীভাবে প্রতিটি ধরনের মডিউলের জন্য তৈরি করা নিয়মগুলি চেক করতে হয়। অ্যাপ মডিউলের জন্য, প্লাগ-ইন সরাসরি R8-এ নিয়ম পাস করে। লাইব্রেরি মডিউলের জন্য, প্লাগ-ইন নিয়মগুলিকে কনজিউমার কিপ রুল হিসেবে লাইব্রেরির আউটপুটে প্যাকেজ করে। আপনার লাইব্রেরির উপর নির্ভর করে এমন অ্যাপগুলি R8 চালানোর সময় এই নিয়মগুলি অটোমেটিক প্রয়োগ করে, এমনকি অ্যাপগুলি Jetpack Keep ব্যবহার না করলেও। কনজিউমার কিপ রুল সম্পর্কে আরও তথ্যের জন্য, লাইব্রেরি লেখকদের জন্য অপ্টিমাইজেশন দেখুন।
Android অ্যাপ
কোনও অ্যাপ মডিউলের জন্য প্লাগ-ইন কোন কোন নিয়ম বজায় রাখে তা চেক করতে, বিল্ড ভ্যারিয়েন্টের জন্য এক্সট্র্যাকশন টাস্ক রান করুন:
./gradlew :app:releaseExtractKeepRules
প্লাগ-ইন প্রতিটি ভেরিয়েন্টের জন্য নিয়ম
app/build/generated/<variant>/androidx.annotation.keep.rules.pro-এ লেখে। প্রতিটি নিয়মের আগে একটি
# context: মন্তব্য, অ্যানোটেট করা কোড শনাক্ত করে যা
এটি তৈরি করেছে।
তৈরি করা নিয়মগুলি আপনার অন্যান্য রাখার নিয়মের সাথে অপ্টিমাইজেশনকে কীভাবে প্রভাবিত করে তা চেক করতে, R8 কনফিগারেশন বিশ্লেষক ব্যবহার করুন।
Android লাইব্রেরি
Android লাইব্রেরি মডিউলের জন্য কোনও অতিরিক্ত সেট-আপের প্রয়োজন নেই। আপনি যখন
AAR তৈরি করেন, তখন প্লাগ-ইনটি তৈরি করা নিয়মগুলি AAR-এর proguard.txt ফাইলে যোগ করে, AAR-এ আগে থেকেই থাকা যেকোনও কনজিউমার কিপ নিয়মের পরে। এটি করার জন্য প্লাগ-ইন প্রতিটি বিল্ড ভেরিয়েন্টের জন্য একটি
<variant>KeepRulesTransformAar টাস্ক রেজিস্টার করে।
নিয়ম যাচাই করতে, AAR তৈরি করুন, যেমন ./gradlew
:mylibrary:assembleRelease-এর সাথে এবং জেনারেট করা AAR-এ proguard.txt খুলুন। তৈরি করা নিয়মগুলি নিম্নলিখিত হেডার সহ একটি বিভাগে দেখানো হয়:
#################################################################
# Rules generated from androidx.annotation.keep annotations #
#################################################################
macOS ও Linux-এ, আপনি কমান্ড লাইন থেকেও নিয়ম প্রিন্ট করতে পারবেন:
unzip -p mylibrary/build/intermediates/aar/release/releaseKeepRulesTransformAar/mylibrary-release.aar proguard.txt
Kotlin Multiplatform লাইব্রেরি
Android টার্গেট সহ Kotlin Multiplatform লাইব্রেরি মডিউলের জন্য কোনও অতিরিক্ত সেট-আপের
প্রয়োজন নেই। Android টার্গেটের জন্য AAR তৈরি করার সময়, প্লাগ-ইনটি Android লাইব্রেরির মতোই
AAR-এর proguard.txt ফাইলে জেনারেট করা নিয়ম যোগ করে। প্লাগ-ইনটি এটি করার জন্য একটি
androidMainKeepRulesTransformAar টাস্ক রেজিস্টার করে।
নিয়ম যাচাই করতে, সেই টাস্ক রান করুন এবং AAR-এ proguard.txt খুলুন যা এটি
তৈরি করে। macOS ও Linux-এ, আপনি কমান্ড লাইন থেকে নিয়ম প্রিন্ট করতে পারবেন:
./gradlew :mylibrary:androidMainKeepRulesTransformAar
unzip -p mylibrary/build/intermediates/aar/androidMain/androidMainKeepRulesTransformAar/mylibrary.aar proguard.txt
JVM লাইব্রেরি
JVM লাইব্রেরি মডিউলের জন্য নিয়ম যাচাই করার আগে, মডিউল কনফিগার করুন যাতে প্লাগ-ইন তৈরি করা নিয়মগুলি এর JAR-এর একটি কপিতে যোগ করে। তারপর আপনার রেজিস্টার করা ট্রান্সফর্ম টাস্ক রান করুন এবং এটি যে JAR তৈরি করে তাতে রুল ফাইল চেক করুন। যেমন, আপনি টাস্কের নাম
keepRulesJar রাখলে, macOS ও Linux-এ নিম্নলিখিত কমান্ডের সাহায্যে
নিয়ম প্রিন্ট করতে পারবেন:
./gradlew :mylibrary:keepRulesJar
unzip -p mylibrary/build/intermediates/annotation-keep/keepRulesJar/mylibrary-keep-rules.jar \
"META-INF/com.android.tools/*" "META-INF/proguard/*"
JVM লাইব্রেরি কনফিগার করুন
JAR তৈরি করে এমন JVM লাইব্রেরি মডিউলের জন্য, প্লাগ-ইন অটোমেটিক JAR পরিবর্তন করে না। পরিবর্তে, annotationKeep.registerJavaArchiveTransform
ফাংশন ব্যবহার করে এমন একটি টাস্ক রেজিস্টার করুন যা তৈরি করা নিয়মের সাথে JAR-এর একটি কপি তৈরি করে এবং তারপর সেই JAR প্রকাশ করুন। লাইব্রেরি মডিউলের build.gradle.kts ফাইলে
নিম্নলিখিতগুলি যোগ করুন:
plugins {
id("java-library")
alias(libs.plugins.jetbrains.kotlin.jvm)
alias(libs.plugins.androidx.annotation.keep)
}
val jarTask = tasks.named<Jar>("jar")
// Apps run R8 on this library's code, so the generated rules must be inside
// the JAR. The plugin handles this automatically only for Android modules.
val keepRulesJarTask = annotationKeep.registerJavaArchiveTransform(
taskName = "keepRulesJar",
input = jarTask.flatMap { it.archiveFile },
fileName = jarTask.flatMap { it.archiveFileName }.map { it.replace(".jar", "-keep-rules.jar") },
)
// Consumers of this library resolve apiElements and runtimeElements, so add
// the JAR that contains the generated rules to them.
configurations.configureEach {
if (name == "apiElements" || name == "runtimeElements") {
outgoing.artifact(keepRulesJarTask.flatMap { it.outputJar })
}
}
dependencies {
implementation(libs.androidx.annotation.keep)
}
ট্রান্সফর্ম, তৈরি করা নিয়ম JAR-এর টার্গেট করা R8 নিয়মের লোকেশনে যোগ করে,
META-INF/com.android.tools/r8/proguard.txt। JAR-এ আগে থেকেই META-INF/com.android.tools/ বা META-INF/proguard/-এ নিয়ম থাকলে,
ট্রান্সফর্ম, জেনারেট করা নিয়মগুলি
আগে থেকে থাকা নিয়ম ফাইলের সাথে যোগ করে। এইসব লোকেশন সম্পর্কে আরও তথ্য পেতে, বিভিন্ন R8 ভার্সন সাপোর্ট করা দেখুন।
ট্রান্সফর্ম রেজিস্টার করার পরে, JAR-এ নিয়মগুলি যাচাই করুন।
উদাহরণ: একটি ঐচ্ছিক অ্যাড-অন লাইব্রেরি লোড করুন
ঐচ্ছিক অ্যাড-অন লোড করার জন্য লাইব্রেরি প্রায়শই রিফ্লেকশন ব্যবহার করে। যেমন, একটি Analytics SDK একটি কোর লাইব্রেরি এবং একটি ভিডিও অ্যাড-অন শিপ করতে পারে। অ্যাড-অনটি কোর লাইব্রেরির উপর নির্ভর করে, তাই কোর লাইব্রেরি সরাসরি অ্যাড-অনের ক্লাসকে রেফার করতে পারে না। পরিবর্তে, কোর লাইব্রেরি রানটাইমে নাম অনুসারে অ্যাড-অনের ক্লাস খোঁজে এবং ক্লাস খুঁজে পেলেই শুধুমাত্র ভিডিও অ্যানালিটিক্স চালু করে।
যেহেতু কোর লাইব্রেরি শুধুমাত্র রিফ্লেকশনের মাধ্যমে অ্যাড-অনের ক্লাস ব্যবহার করে, তাই R8 অ্যাপের রিলিজ বিল্ড থেকে ক্লাস বা এর কনস্ট্রাক্টর সরিয়ে দিতে পারে, যার ফলে লুক-আপ ব্যর্থ হয়। অ্যাড-অন অন্তর্ভুক্ত থাকা অ্যাপের রিলিজ বিল্ডে শুধুমাত্র এই সমস্যা হয়, তাই টেস্টিংয়ের সময় এটি ধরা কঠিন।
Jetpack Keep ছাড়া, অ্যাড-অন তার ক্লাসের জন্য একটি কনজিউমার Keep রুল শিপ করে, যেমন ঐচ্ছিক ডিপেন্ডেন্সি সাপোর্ট করার জন্য রিফ্লেকশন-এ দেখানো হয়েছে:
# In the add-on's consumer keep rules file.
-keep class com.example.analytics.video.VideoEventTracker {
<init>();
}
এই নিয়মটি লুকআপের থেকে আলাদা লাইব্রেরিতে রয়েছে যার এটি প্রয়োজন, তাই দুটি সিঙ্ক থেকে বেরিয়ে আসতে পারে। Jetpack Keep-এর মাধ্যমে, আপনি পরিবর্তে কোর লাইব্রেরিতে লুকআপ অ্যানোটেট করেন:
// In the core library.
interface AnalyticsPlugin {
fun start()
}
object AnalyticsManager {
// The annotation and the lookup share this constant so that they can't
// refer to different classes.
private const val VIDEO_TRACKER_CLASS =
"com.example.analytics.video.VideoEventTracker"
@UsesReflectionToConstruct(
className = VIDEO_TRACKER_CLASS,
parameterTypes = [],
)
fun initialize() {
val tracker = try {
Class.forName(VIDEO_TRACKER_CLASS)
.getDeclaredConstructor()
.newInstance() as AnalyticsPlugin
} catch (e: ClassNotFoundException) {
// Apps that don't play video leave out the add-on, so a missing
// class isn't an error.
return
}
// This is an ordinary interface call, not a reflective one, so R8
// keeps start() without an annotation.
tracker.start()
}
}
অ্যানোটেশন এইসব নির্দেশিকা মেনে চলে:
- রিফ্লেকশন পারফর্ম করে এমন কোড অ্যানোটেট করুন। অ্যানোটেশন
initialize-এ আছে, যাClass.forName-কে কল করে, অ্যাড-অনের ক্লাসে নয়। - কোডটি কী করে তার উপর ভিত্তি করে অ্যানোটেশন বেছে নিন। লুকআপ ক্লাসের একটি ইনস্ট্যান্স তৈরি করে, তাই কোডটি
@UsesReflectionToConstructব্যবহার করে। - আপনি রেফারেন্স দিতে পারবেন না এমন ক্লাসের জন্য
classNameব্যবহার করুন। অ্যাড-অনের ক্লাস কোর লাইব্রেরির কম্পাইল ক্লাসপাথে নেই, তাই অ্যানোটেশনclassConstantব্যবহার করতে পারবে না। - কোড যে কনস্ট্রাক্টরকে কল করে শুধুমাত্র সেটিই রাখুন। লুকআপ
নো-আর্গুমেন্ট কনস্ট্রাক্টরকে কল করে, তাই
parameterTypesহল একটি খালি অ্যারে।
আপনি যখন কোর লাইব্রেরির AAR তৈরি করেন, তখন প্লাগ-ইন একটি নিয়ম যোগ করে যা
VideoEventTracker এবং এর নো-আর্গুমেন্ট কনস্ট্রাক্টর রাখে যখনই R8
initialize রাখে। SDK ব্যবহার করে এমন অ্যাপগুলি অটোমেটিক এই নিয়ম প্রয়োগ করে, এমনকি তারা
Jetpack Keep ব্যবহার না করলেও, তাই অ্যাড-অনের নিজস্ব নিয়মের প্রয়োজন হয় না।
অ্যাড-অনের নিয়মের সাথে তুলনা করলে, এই পদ্ধতির নিম্নলিখিত সুবিধা আছে:
- নিয়মটি লুকআপের পাশে থাকে। কোর লাইব্রেরি লুকআপ ও নিয়ম, দুটিই ম্যানেজ করে, তাই দুটি লাইব্রেরি জুড়ে পরিবর্তন করার জন্য সমন্বয় করার পরিবর্তে আপনি সেগুলি একসাথে আপডেট করতে পারেন।
- R8 এখনও অব্যবহৃত কোড সরাতে পারে। কোনও অ্যাপে অ্যাড-অন অন্তর্ভুক্ত থাকলেও
কখনও
initializeকল না করলে, জেনারেট করা নিয়ম প্রযোজ্য হয় না এবং R8VideoEventTrackerসরিয়ে দিতে পারে। অ্যাড-অনের নিয়ম অনুযায়ী, অ্যাড-অন অন্তর্ভুক্ত আছে এমন প্রতিটি অ্যাপে এটি থাকে। - Lint অ্যানোটেট না করা লুকআপ ধরে। কোর লাইব্রেরি পরে যদি অ্যানোটেশন ছাড়া অন্য রিফ্লেক্টিভ লুকআপ যোগ করে, তাহলে লিন্ট সেটিকে ফ্ল্যাগ করে, যাতে লাইব্রেরি রিলিজ করার আগে আপনি সেটি ঠিক করতে পারেন।
ফলাফল কনফার্ম করতে, Android লাইব্রেরিতে যেমন বর্ণনা করা হয়েছে, সেই অনুযায়ী তৈরি করা নিয়ম AAR-এর
proguard.txt ফাইলে দেখা যাচ্ছে কিনা তা চেক করুন। তারপর
অ্যাড-অন সহ অ্যাপের রিলিজ বিল্ড টেস্ট করুন, কারণ সেখানেই
নিয়ম না থাকলে ভিডিও অ্যানালিটিক্স কাজ করবে না।
সীমাবদ্ধতা
Jetpack Keep এখনও এমন রিফ্লেকশন শনাক্ত করে না যা তাদের অ্যানোটেশন দ্বারা ক্লাস এবং মেম্বার খুঁজে পায়, বা এর জন্য Keep নিয়ম তৈরি করে। এই ফিচারটি প্ল্যান করা হয়েছে।
ততক্ষণ পর্যন্ত, এই ধরনের প্রতিফলনের জন্য নিয়ম মেনে চলুন। যেমন, নিম্নলিখিত নিয়মটি
R8-এর রাখা ক্লাসে @SomeAnnotation-এর সাথে অ্যানোটেট করা
প্রতিটি মেম্বারকে রাখে:
-keepclassmembers class * {
@com.example.annotation.SomeAnnotation <methods>;
@com.example.annotation.SomeAnnotation <fields>;
}
Jetpack Keep আপনার ব্যবহারের ক্ষেত্রে কাজ না করলে অথবা আপনার অন্য কোনও মতামত থাকলে, সমস্যা ফাইল করুন।
অতিরিক্ত রিসোর্স
- Jetpack Keep স্যাম্পেল: একটি Android Studio প্রোজেক্ট যা অ্যাপ, Android লাইব্রেরি, Kotlin মাল্টিপ্ল্যাটফর্ম লাইব্রেরি এবং JVM লাইব্রেরিতে Jetpack Keep ব্যবহার করে।
- নিয়ম ব্যবহারের উদাহরণ ও কেস রাখুন: সাধারণ রিফ্লেকশন ও JNI পরিস্থিতির জন্য নিয়ম রাখুন।
- পেশাদার পদ্ধতি অনুসরণ করুন: নির্দিষ্ট স্কোপের মধ্যে রাখার নিয়ম লেখার ব্যাপারে নির্দেশিকা।