9

I'm using Jack's Wharton Hugo Library and AndroidDevMetrics plugins to measure method execution time and application performance in Android. I need to make those libraries compile only in debug build and to exclude them from release builds.

Because both of those library applied only using the plugin syntax:

apply plugin: 'com.frogermcs.androiddevmetrics'
apply plugin: 'com.jakewharton.hugo'

and do not require any dependencies in the Gradle file I can't exclude them using the testCompile option. The only way supplied to control Hugo is setting this in the gradle file:

hugo {
   enabled false
}

while the only way to control AndroidDevMetrics is:

public class ExampleApplication extends Application {

@Override
 public void onCreate() {
     super.onCreate();
     //Use it only in debug builds
     if (BuildConfig.DEBUG) {
         AndroidDevMetrics.initWith(this);
     }
  }
 }

The question: Those control options do not prevent from those library files from being complied to the release version of the application. I'm looking for a way to exclude those plugins in Gradle in case I build a release version.

halfer
  • 19,824
  • 17
  • 99
  • 186
Emil Adz
  • 40,709
  • 36
  • 140
  • 187

2 Answers2

3

You can use a combination of Proguard and Sourcesets to ensure that libraries aren't compiled into your release application, and gradle properties to apply plugins conditionally.

Conditionally including plugins

You can conditionally include a gradle plugin by declaring it as you normally would at the top of your build.gradle, and surrounding it with a conditional clause. For example, the code below checks whether a property exists, and if it does, applies the plugin.

if (hasProperty('shouldApplyDevMetrics')) {
    println "Applying devmetrics plugin"
    apply plugin: 'com.frogermcs.androiddevmetrics'
}
else {
    println "Not applying devmetrics plugin in release build"
}

To include a property, you can use the command line flag below when invoking gradle. You can create similar launch configurations if you wish to use Android Studio.

./gradlew assembleDebug -PshouldApplyDevMetrics=true

This removes the gradle plugin from the release build, but depending on the library, may leave compiled code in your app. You can address this using one of the two methods detailed below.

Using Proguard

The first (and simplest) approach to completely removing a library from an APK is to strip out all the relevant code using the Proguard tool. You should update your release buildType to enable proguard, and load a custom rules file.

release {
    minifyEnabled true
    proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}

By default, this should strip out the annotations. It's possible that you will need to update your proguard configuration for other dependencies which rely on reflection or annotations. If enabling proguard produces compiler warnings relating to Hugo, you can disable them by adding the following line:

-dontwarn hugo.weaving**

This approach does mean that you need to keep the dependency in your build.gradle file, but is the best approach for something like Hugo, which is used all over the place by adding annotations.

Using Sourcesets

To remove the AndroidDevMetrics library from the release build entirely, we should start off by creating a debug and release sourceset, then add a functional class under src/debug, and a no-op class under src/release.

// src/debug
public class DevMetricWrapper() {
    void doMetricsThings(Context context) {
        AndroidDevMetrics.initWith(context);
    }
}

// src/release
public class DevMetricWrapper() {
    void doMetricsThings(Context context) {
        // no-op
    }
}

You can then alter the build.gradle file for your module so that the library is only included as a debug dependency:

debugCompile 'com.example.yourlibrary'

Please note that if you're planning on doing anything more complicated, Dagger is a very useful library as it allows you to inject different dependencies depending on what flavor you are building.

fractalwrench
  • 4,028
  • 7
  • 33
  • 49
  • but you can't do "debugCompile 'com.example.yourlibrary'" on the AndroidDevMetrics as it has no dependency in the gradle file, but it's only added as a plugin. That the all point of this question: how to exclude plugins from release builds. – Emil Adz Jun 05 '16 at 08:04
  • @EmilAdz please see updated answer, you can use a command line flag to apply plugins conditionally. – fractalwrench Jun 05 '16 at 09:07
  • That looks like what I need, but can this be done without providing additional parameter from command line? is there some kind of condition for gradle that will let me know that the debug/release version is currently built? – Emil Adz Jun 05 '16 at 09:49
  • You can use gradle.properties if you want to include the property that way. You can add command line arguments via launch configurations though (via Run > Edit Configurations), which is arguably more flexible – fractalwrench Jun 05 '16 at 10:15
  • isn't there a way from gradle file to know that it's a debug compile and do it only in this case? – Emil Adz Jun 05 '16 at 10:18
  • @EmilAdz you could try moving the apply plugin line to the debug closure? I'm not 100% on whether that will work. – fractalwrench Jun 05 '16 at 10:54
0

Hugo auto ensure that:

project.dependencies {
  debugCompile 'com.jakewharton.hugo:hugo-runtime:1.2.2-SNAPSHOT'
  // TODO this should come transitively
  debugCompile 'org.aspectj:aspectjrt:1.8.6'
  compile 'com.jakewharton.hugo:hugo-annotations:1.2.2-SNAPSHOT'
}

find it here

Only annotation can do nothing, is ok.

About AndroidDevMetrics, you can read Get Start

public class ExampleApplication extends Application {

 @Override
 public void onCreate() {
 super.onCreate();
 //Use it only in debug builds
 if (BuildConfig.DEBUG) {
     AndroidDevMetrics.initWith(this);
 }
 }
}

This maybe no so clean, but is work. When you run into release, the code is clean.

laomo
  • 436
  • 4
  • 12
  • while it looks like you are right and the only thing that getting compiled in hugo at release build is the annotations. it doesn't change the fact that the annotations leave a small footprint on the project. in the android dev metrics on the other hand there is a dependency that's called: 'com.frogermcs.androiddevmetrics:androiddevmetrics-runtime-noop:0.4' that is getting compiled for release build. I don't wan;t those files even compiled in a release. – Emil Adz May 30 '16 at 07:18
  • @EmilAdz When release with proguard, those files will removed(proguard remove unused code and file). – laomo May 30 '16 at 09:07
  • are you sure that all files of hugo and android dev matrics are removed at release build? because android dev matrics has some dependencies that are compiled not only for debugCompile. – Emil Adz May 31 '16 at 07:31
  • Proguard ensure this. you can view then apk with [ClassyShark](https://github.com/google/android-classyshark) to check it. – laomo May 31 '16 at 08:27
  • thanks, I will use that to test if the files are there or not. But actually the solution I'm looking for is less in the proguard stage, but ratter in the gradle file phase.. – Emil Adz May 31 '16 at 08:29
  • @EmilAdz if you remove the Hugo dependency entirely from the release build, the compiler won't be able to resolve the annotations. Using proguard to strip them out seems like the cleanest solution. – fractalwrench Jun 04 '16 at 13:01
  • @fractalwrench you might be right about Hugo but what about AndroidDevMetrics that doesn't uses annotations, I don't really have to compile it when I running a release build. – Emil Adz Jun 04 '16 at 22:45