9

In C++ I would normally setup 2 builds - debug and release with each having DEBUG and RELEASE predefined respectively. I would then use these definitions to determine constant values, like logging enabled/disabled, server URL and etc.

Right now, in Java/Android I comment out some stuff before building release. That is not a good way, I can tell. I may forget something.

What is a common practice for ensuring nothing is forgotten when building a release version (signed) or debug version (unsigned)?

Pijusn
  • 11,025
  • 7
  • 57
  • 76

5 Answers5

18

If you are running the application from Eclipse, it will always be a debug.

When you export the application (Android Tools -> Export (un)signed Application Package)

If you want to know dynamically if its release or debug, you can use BuildConfig.DEBUG (Its located in the gen folder, I don't know if this is supported by all the API levels)

Like as followed:

if (BuildConfig.DEBUG) {
    Log.d(TAG, "Text");
}

If you look at the generated bytecodes you will see the following (In debug mode):

public class Sample{

    private static final boolean LOG_ENABLED = true;

    public static void main(String args[]){
        if (BuildConfig.DEBUG){
            System.out.println("Hello World");
        }
    }
}

Produces the following bytecodes:

public class Sample extends java.lang.Object{
    public Sample();
      Code:
       0:   aload_0
       1:   invokespecial   #1; //Method java/lang/Object."<init>":()V
       4:   return

    public static void main(java.lang.String[]);
      Code:
       0:   getstatic   #2; //Field java/lang/System.out:Ljava/io/PrintStream;
       3:   ldc #3; //String Hello World
       5:   invokevirtual   #4; //Method Java/io/PrintStream.println(Ljava/lang/String;)V
       8:   return

}

And if the BuildConfig.DEBUG is false

public class Sample extends java.lang.Object{
    public Sample();
      Code:
       0:   aload_0
       1:   invokespecial   #1; //Method java/lang/Object."<init>":()V
       4:   return

    public static void main(java.lang.String[]);
      Code:
       0:   return
}
Ion Aalbers
  • 7,830
  • 3
  • 37
  • 50
8

There's no (by default) any preprocessor for Java, so no #ifdef stuff at compile time. But if you do not mind leaving debug code in your app, then you can check if app is release or debug at runtime with this code:

Boolean release = (getApplicationInfo().flags & ApplicationInfo.FLAG_DEBUGGABLE);

which checks debuggable flag value. And said flad is automatically set to false for release builds and true for debug builds.

If you want to get rid of some debug code, you may try using ProGuard to strip certain classes or methods. And by default ProGuard is involved in building process for release builds only.

Marcin Orlowski
  • 72,056
  • 11
  • 123
  • 141
  • 3
    I would recommend using BuildConfig.DEBUG because this happens in compile time, your solution works runtime. Therefor the logging code will be inside the package. On compile time, the java compiler won't take the code inside the if-statement into account. – Ion Aalbers Sep 17 '12 at 14:18
  • Please check my updated answer. This will clarify that it's true =) – Ion Aalbers Sep 18 '12 at 07:01
  • You may also want to consider your defining your own global `public static final boolean DEBUG = false;` for greater control over the result... (there have been incidents in which `BuildConfig.DEBUG` was not truly reflecting the state of the system). – Android Eve Jul 28 '13 at 21:46
0

I normally create a separate log class where i set a static DEBUG variable. Now all i need to go before getting a production build is to set that DEBUG variable to false.

public class Log {
     public final static String LOGTAG = "APP NAME";

      public static final boolean DEBUG = true;

      public static void v(String msg) {
        android.util.Log.v(LOGTAG, msg);
      }

      public static void e(String msg) {
        android.util.Log.e(LOGTAG, msg);
      }

      public static void d(String msg) {
          android.util.Log.d(LOGTAG, msg);
      }
}

For logging -

if(Log.DEBUG) Log.v("In some function x. Doing y.");
Mukesh Soni
  • 6,646
  • 3
  • 30
  • 37
  • I have 6 logging constants in my logging object. I set 2 of them to false when building a release. – Pijusn Sep 17 '12 at 12:18
0

I was facing the same issue as everytime I was running the project as android application it used to open in debugger mode but then the problem was solved.

-If you are working in eclipse you must be using Java EE perspective -Instead just select Java perspective.

-Clean your app. -uninstall the app from the device. -Restart your device (Just like that so that no cache is stored) -Run your app.

The debugger mode won't show up this time. Copy the apk generated in your bin folder and try it out on other devices as well

Nishant
  • 3
  • 1
  • 3
0

I found a way to decently emulate a preprocessing directive:

In my Gradle buildTypes I define:

release {
    buildConfigField "boolean", "isDebug", "false"
    ...
}

debug {
    buildConfigField "boolean", "isDebug", "true"
    ...
}

Then, in my code, I do as follows:

if (BuildConfig.isDebug) {
    ... do debug stuff ...
}

and if needed, of course:

else {
    ... do release stuff ...
}

Both blocks are present in the debug APK, but when building a release version, Proguard is clever enough to determine that the debug block can be removed as it is dependant of a if (false) (which is also removed from the resulting code).

Should you call some debug-specific classes from the debug block and only from there, they will be stripped out from the resulting APK as they are considered unused, and this also an interesting point: your code cannot be tempered in a way it would use that code.

I could determine all that by checking my dump, mapping and usage Proguard output files.

Shlublu
  • 10,917
  • 4
  • 51
  • 70