R8 की मदद से, ऐसे नियम जोड़े जा सकते हैं जो keep
rules के अलावा, आपके ऐप्लिकेशन के ऑप्टिमाइज़ेशन पर असर डालते हैं. इन नियमों को उसी proguard-rules.pro फ़ाइल में जोड़ें जिसमें आपने निजी डेटा के रखरखाव के नियम सेट किए हैं.
इन नियमों को इन कैटगरी में बांटा गया है:
- अनुमान
-assumevalues-assumenosideeffects
- अन्य ऑप्टिमाइज़ेशन
-convertchecknotnull-maximumremovedandroidloglevel
अनुमान
इन नियमों से R8 को पता चलता है कि रनटाइम के दौरान, वह किसी खास कोड के व्यवहार के बारे में कुछ अनुमान लगा सकता है.
-assumevalues
-assumevalues नियम, R8 को बताता है कि किसी फ़ील्ड की वैल्यू या किसी तरीके की रिटर्न वैल्यू, रनटाइम के दौरान हमेशा कोई खास कॉन्स्टेंट होती है या तय की गई रेंज में आती है. -assumevalues का इस्तेमाल फ़्लैग वैल्यू जैसी चीज़ों के लिए किया जाता है. बिल्ड टाइम पर, रनटाइम में इनकी वैल्यू तय होती है.
R8 का स्टैंडर्ड स्टैटिक विश्लेषण, सदस्यों की रनटाइम वैल्यू का पता नहीं लगा सकता. -assumevalues की मदद से, R8 को यह बताया जाता है कि कोड को ऑप्टिमाइज़ करते समय, बताई गई वैल्यू या रेंज को मान लिया जाए. इससे R8 को बेहतर ऑप्टिमाइज़ेशन करने में मदद मिलती है.
-assumevalues का सिंटैक्स, member_specification को बनाए रखने के सिंटैक्स जैसा ही होता है. हालांकि, इसमें return clause भी शामिल होता है. यह इस तरह दिखता है:
<member_specification> return <value> | <range>
<value> और <range> आर्ग्युमेंट के लिए, यहां दी गई वैल्यू और टाइप इस्तेमाल किए जा सकते हैं:
- खास वैल्यू:
true, false, null, @NonNull - प्रिमिटिव वैल्यू:
int - स्टैटिक फ़ील्ड रेफ़रंस (इसमें enum फ़ील्ड भी शामिल हैं)
किसी रेंज को तय करने के लिए, min..max फ़ॉर्मैट का इस्तेमाल करें. उदाहरण के लिए, यहां दिए गए स्निपेट से पता चलता है कि वैरिएबल CUSTOM_VAL, 26 से 2147483647 तक की वैल्यू स्वीकार करता है:
-assumevalues public class com.example.Foo {
public static int CUSTOM_VAL return 26..2147483647;
}
इस नियम का इस्तेमाल इन स्थितियों में किया जा सकता है:
- लाइब्रेरी के लिए: यह पक्का करने के लिए कि जब ऐप्लिकेशन ऑप्टिमाइज़ किए जाएं, तब सभी लोकल डीबगिंग हुक को सार्वजनिक लाइब्रेरी कोड से हटा दिया जाए.
- ऐप्लिकेशन के लिए: रिलीज़ किए गए ऐप्लिकेशन से डीबग कोड जैसी चीज़ें हटाने के लिए. बिल्ड वैरिएंट और खास सोर्स सेट या कॉन्स्टेंट के वैरिएंट इस्तेमाल करना बेहतर होता है. हालांकि, अगर वैरिएंट सोर्स सेट आपके मामले में काम नहीं करते हैं या आपको इस बात की ज़्यादा गारंटी चाहिए कि कोड पाथ पूरी तरह से हटा दिए गए हैं, तो
-assumevaluesका इस्तेमाल करें.
यहां दिए गए उदाहरण में एक ऐसी क्लास दिखाई गई है जिसमें R8, ऐप्लिकेशन के ऑप्टिमाइज़ किए गए वर्शन से डीबग टूल हटाता है:
package com.example;
public class MyConfig {
// This field is initialized to false but is overwritten by a resource
// value or other mechanism in the final build process. R8's static analysis
// might see the initial 'false' but the runtime value is known to be
// 'true'.
public static final boolean IS_OPTIMIZED_VERSION = false;
}
// In another class:
public void initFeatures() {
if (MyConfig.IS_OPTIMIZED_VERSION) {
System.out.println("Starting optimized features...");
android.util.Log.d(TAG, "Starting optimized features...");
initOptimizedService();
} else {
android.util.Log.d(TAG, "Starting debug/logging features...");
initDebugTools();
}
}
यहां दिए गए नियम से पता चलता है कि R8 को यह कैसे बताया जाए कि वैरिएबल IS_OPTIMIZED_VERSION को हमेशा true पर सेट किया जाना चाहिए.
-assumevalues class com.example.MyConfig {
public static final boolean IS_OPTIMIZED_VERSION return true;
}
-assumenosideeffects
-assumenosideeffects नियम, R8 को बताता है कि वह यह मान सकता है कि बताए गए सदस्यों पर कोई बुरा असर नहीं पड़ा है. R8, ऐसे तरीकों के कॉल को पूरी तरह से हटा सकता है जिनकी कोई रिटर्न वैल्यू नहीं होती या जो एक तय वैल्यू देते हैं.
-assumenosideeffects का सिंटैक्स, member_specification को बनाए रखने के सिंटैक्स से मिलता-जुलता है.
यहां दिए गए सैंपल में बताया गया है कि R8 को यह कैसे बताया जाए कि DebugLogger क्लास में log नाम वाले सभी public static तरीकों का कोई साइड इफ़ेक्ट नहीं होना चाहिए. इससे R8 को इन तरीकों के कॉल हटाने की अनुमति मिलती है.
-assumenosideeffects class com.example.DebugLogger {
public static void log(...);
}
अन्य ऑप्टिमाइज़ेशन
ये कुछ और बेहतर ऑप्टिमाइज़ेशन हैं, जो डिफ़ॉल्ट रूप से चालू नहीं होते. इन्हें चालू करने पर, R8 को डिफ़ॉल्ट ऑप्टिमाइज़ेशन के साथ-साथ, दिए गए निर्देशों के मुताबिक कोड को ऑप्टिमाइज़ करने की अनुमति मिलती है.
-convertchecknotnull
शून्य की जांच को ऑप्टिमाइज़ करने के लिए, -convertchecknotnull नियम का इस्तेमाल किया जा सकता है. यह उस हर तरीके पर लागू होता है जो ऑब्जेक्ट पैरामीटर लेता है और अगर ऑब्जेक्ट शून्य है, तो उसे खारिज कर देता है. यह स्टैंडर्ड Kotlin असर्शन की तरह ही काम करता है. ऐसा ज़रूरी नहीं है कि अपवाद का टाइप और मैसेज एक जैसा हो. हालांकि, क्रैश होने की वजह एक जैसी हो सकती है.
अगर कोई -convertchecknotnull नियम, किसी दिए गए तरीके से मेल खाता है, तो उस तरीके के हर कॉल को पहले आर्ग्युमेंट पर getClass() के कॉल से बदल दिया जाता है. getClass() को कॉल करने से, शून्य की जांच करने की सुविधा मिलती है. साथ ही, R8 को शून्य की जांच करने वाले ओरिजनल फ़ंक्शन के किसी भी अतिरिक्त आर्ग्युमेंट को हटाने की अनुमति मिलती है. जैसे, स्ट्रिंग के लिए मेमोरी का महंगा असाइनमेंट.
-convertchecknotnull का सिंटैक्स यहां दिया गया है:
-convertchecknotnull <class_specification> {
<member_specification>;
}
उदाहरण के लिए, अगर आपके पास Preconditions क्लास है और checkNotNull तरीके को इस तरह से इस्तेमाल किया गया है:
class Preconditions {
fun <T> checkNotNull(value: T?): T {
if (value == null) {
throw NullPointerException()
} else {
return value
}
}
}
इस नियम का इस्तेमाल करें:
-convertchecknotnull class com.example.package.Preconditions {
void checkNotNull(java.lang.Object);
}
यह नियम, checkNotNull() को किए गए सभी कॉल को पहले आर्ग्युमेंट में दिए गए getClass को किए गए कॉल में बदल देता है. इस उदाहरण में, checkNotNull(bar) को bar.getClass() से बदल दिया गया है. अगर bar null होता, तो bar.getClass() NullPointerException दिखाता. इससे, नल की जांच करने का वही असर मिलता, लेकिन ज़्यादा असरदार तरीके से.
-maximumremovedandroidloglevel
इस तरह के नियम से, Android के लॉगिंग स्टेटमेंट (जैसे कि Log.w(...) और Log.isLoggable(...)) को किसी लॉग लेवल पर या उससे नीचे हटा दिया जाता है.
maximumremovedandroidloglevel का सिंटैक्स यहां दिया गया है:
-maximumremovedandroidloglevel <log_level> [<class_specification>]
अगर आपने वैकल्पिक class_specification नहीं दिया है, तो R8 पूरे ऐप्लिकेशन से लॉग हटाने की सुविधा लागू करता है.
लॉग लेवल इस तरह से होते हैं:
लॉग लेबल |
लॉग लेवल |
ज़्यादा जानकारी देने वाला |
2 |
डीबग करें |
3 |
जानकारी |
4 |
चेतावनी |
5 |
गड़बड़ी |
6 |
ASSERT |
7 |
उदाहरण के लिए, अगर आपके पास यह कोड है:
class Foo {
private static final String TAG = "Foo";
void logSomething() {
if (Log.isLoggable(TAG, WARNING)) {
Log.e(TAG, "Won't be logged");
}
Log.w(TAG, "Won't be logged");
Log.e(TAG, "Will be logged");
}
}
इस नियम के साथ:
# A level of 5 corresponds to a log level of WARNING.
-maximumremovedandroidloglevel 5 class Foo { void logSomething(); }
ऑप्टिमाइज़ किया गया कोड यहां दिया गया है:
class Foo {
private static final String TAG = "Foo";
void logSomething() {
Log.e(TAG, "Will be logged");
}
}
अगर एक ही तरीके के लिए, ज़्यादा से ज़्यादा लॉग लेवल दिए जाते हैं, तो R8 सबसे कम लेवल का इस्तेमाल करता है. उदाहरण के लिए, इन नियमों के हिसाब से:
-maximumremovedandroidloglevel 7 class ** { void foo(); }
-maximumremovedandroidloglevel 4 class ** { void foo(); }
इसलिए, foo() के लिए हटाए गए लॉग लेवल की ज़्यादा से ज़्यादा संख्या 4 है.