Proguard shrinks too much - android

Since ADT 17 you can find in the gen folder a BuildConfig class with a DEBUG constant. I often use this constant in my code, since ADT changes the value automatically when you export the application.
However with Proguard this doesn't work anymore. E.g. I have following snippet:
if (!BuildConfig.DEBUG) {
ACRA.init(this);
}
Proguard notices, that DEBUG is true, so it removes this snippet completely and shrinks the app. After that ADT changes DEBUG constant, but this is too late.
The only solution I know is to create my own DEBUG constant and to change it manually again. But I really like the functionality of ADT. Do you know a better solution?
Thanks in advance.
Edit:
There is a workaround. Create your own DEBUG constant, which is initialized at runtime:
debug = (0 != (getApplicationInfo().flags & ApplicationInfo.FLAG_DEBUGGABLE));
I create this variable at the very first in the onCreate method of my Application class. That is a workaround, which works, but it isn't the solution of the problem.

Related

How to disable a CMAKE option

On ${CMAKE_CURRENT_SOURCE_DIR}/JUCE, there is:
option(JUCE_BUILD_EXTRAS "Add build targets for the Projucer and other tools" OFF)
if(JUCE_BUILD_EXTRAS)
add_subdirectory(extras)
endif()
link: https://github.com/juce-framework/JUCE/blob/master/CMakeLists.txt#L57
So this is what I did:
set(JUCE_BUILD_EXTRAS OFF)
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/JUCE)
However it still tries to build extras. I'm on Android Studio.
What should I do?
I also tried set(JUCE_BUILD_EXTRAS OFF)
The default value is OFF and even then it tried to build.
I always get these errors when trying to disable something on Android CMAKE but it always fails
The easiest way to guarantee that this will be overridden correctly from a parent CMakeLists.txt is to set it as a CACHE variable with a FORCEd value:
set(JUCE_BUILD_EXTRAS OFF CACHE BOOL "" FORCE)
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/JUCE)
This will ensure that any state that may already exist from a previous configuration of the project that defines the option will be overridden and picked up first.
Technically, the documentation for option states:
... If is already set as a normal or cache variable, then the command does nothing.
However, this is only true if there have been no prior configurations -- so this only works if the variable was set before the first configuration
In practice I've found that the FORCE-approach is much more reliable.

Proguard deletes method parameters

Recently I found very strange thing with ProGuard. I have this code snippet
As you can see, method showTipHoodLock takes 2 parameters, fragmentManager and top (some offset)
but after I compile the app with minifyEnabled true
I got this on click callback
and this is Utils.showTipHoodLock method
As you can see, proguard deleted 2nd parameter (named top) from method signature and replaced its occurances with 0 literal.
Is it a bug, or a feature, or did I miss something?
P.S. If I change values in line
int coords[] = {0, 0}
to any other numbers, then everything works perfect, and nothing is deleted. Moreover The same snippet of code (which is copy-pasted) in different part of application (in other fragment), starts to work.
Is it a bug, or a feature, or did I miss something?
This is NOT a bug, this is a feature to optimise your code.
According to your piece of code, the second parameter is referenced by following logic as READ ONLY and its value is FIXED to be 0.
Proguard will remove (a kind of Proguard optimisation: Remove unused parameters or Propagate constant arguments) this parameter with this KNOWN FIXED value to simplify the invocation flow.

Using mock data in eclipse which doesn't produce warnings

How can I create a layout file with data and fill them with some example data so that I can see what I have done without producing warnings.
Here is an example:
If I remove the text (which wants Android Lint) I have a plain blue screen, which is not helpful at all.
By the way this is the warning:
[I18N] Hardcoded string "Foobar", should use #string resource example.xml /Example/res/layout Line 42 Android Lint Problem
Create an entry in your strings.xml for each of your example Strings.
On the other hand, this is a Lint warning. You can safely ignore this without causing immediate harm to your app, as these are only meant to guide you in best practices.
Go to the Eclipse preferences, into the Android submenu, then into the Lint Error Checking menu and disable the HardcodedText warning.
Second Method
Instead of setting the example text through XML, use the setText() through Java code, and enclose it in an if condition as follows:
if(BuildConfig.DEBUG) {
view.setText("My example text");
}
BuildConfig.DEBUG was added in ADT 17, and is true whenever you do a debug build. When you export the apk, it is set to false. As the variable is set to false and never reassigned, the Java compiler will optimize your code and remove these statements. They will not be compiled into a production app.

better way to do Debug only assert code

I am writing my first Android application and I am liberally using asserts() from junit.framework.Assert
I would like to find a way to ensure that the asserts are only compiled into the debug build, not in the release build.
I know how to query the android:debuggable attribute from the manifest so I could create a variable and accomplish this in the following fashon:
static final boolean mDebug = ...
if (mDebug)
Assert.assertNotNull(view);
Is there a better way to do this? i.e. I would prefer not to use an if() with each assert.
thanks
I think the Java language's assert keyword is likely what you want. Under the covers, the assert keyword essentially compiles into Dalvik byte code that does two things:
Checks whether the static variable assertionsDisabled (set in the class' static constructor via a call to java.lang.Class.desiredAssertionStatus()) is != 0 and if so, does nothing
If it is 0, then it checks the assertion expression and throws a java.lang.AssertionError if the expression resolves to false, effectively terminating your application.
The Dalvik runtime by default has assertions turned off, and therefore desiredAssertionStatus always returns 1 (or more precisely, some non-zero value). This is akin to running in "retail" mode. In order to turn on "debug" mode, you can run the following command against the emulator or the device:
adb shell setprop debug.assert 1
and this should do the trick (should work on the emulator or any rooted debugging-ready device).
Note however that the aforementioned Dalvik code that checks the value of assertionsDisabled and throws an AssertionError if the expression is false is always included in your byte code and liberal sprinkling of asserts in your code may lead to byte code bloat.
Please see this for a bit more detail: Can I use assert on Android devices?
If you're concerned about shipping code with the JUnit asserts in (or any other class path), you can use the ProGuard config option 'assumenosideeffects', which will strip out a class path on the assumption that removing it does nothing to the code.
Eg.
-assumenosideeffects class junit.framework.Assert {
*;
}
I have a common debug library I put all my testing methods in, and then use this option to strip it from my released apps.
This also removes the hard to spot problem of strings being manipulated that are never used in release code. For example if you write a debug log method, and in that method you check for debug mode before logging the string, you are still constructing the string, allocating memory, calling the method, but then opting to do nothing. Stripping the class out then removes the calls entirely, meaning as long as your string is constructed inside the method call, it goes away as well.
Make sure it is genuinely safe to just strip the lines out however, as it is done with no checking on ProGuard's part. Removing any void returning method will be fine, however if you are taking any return values from whatever you are removing, make sure you aren't using them for actual operational logic.
I mean if you were using a language feature, like assert(), the compiler should be able to strip that out. But this is an actual class and if a class is referenced by executable code it will be included or assumed included in the final product by the compiler.
However there is nothing stopping you from creating a script that removes all the references to the Assert class in all of your code base before compilation.
Another approach would be to make a test project that targets your application and within JUnit tests actually calls the Assert on the areas which you want to make sure work. I personally like this approach because it is a nice and clean separation of test and application.
If you are just worried about the having an if-statement everywhere, then just wrap Assert with your own class, DebuggableAssert which does that check before each call to Assert.X. It will be sort of less performant because of the method entry/exit and the conditionals but if you can maintain your code better then it might be worth it.

Android: Proguard NoSuchMethodError

I recently activated ProGuard for my Eclipse Android project. After adding external libs and dynamically referenced classes to the proguard.cfg, I don't get any errors when building the apk. I get however a NoSuchMethodError when I try to start the installed app.
I narrowed it down to a specific method called in the onCreate method of the main activity. To simplify things, here's what the class and method look like (I left out a lot of code, but I think this should illustrate it):
public class TestMain extends TabActivity implements OnSharedPreferenceChangeListener{
...
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
...
testMethod();
}
}
testMethod() is defined as follows:
private void testMethod() {
int charsLeft = maxPostMessageLength - someEditText.length();
...
}
When I remove the "someEditText.length()" part, the app starts. So, the way I see it, the method that can't be found is the EditText.length() method. Strangely, though, the app also starts when I remove "someEditText.length()" from the testMethod and put it directly into the onCreate method:
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
test = someEditText.length();
testMethod();
}
Does anyone know how I can get rid of this error and why I can call someEditText.length() directly in the onCreate method but not in a method called by the onCreate method?
Without using Proguard the app works fine, of course.
Edit:
I tried the -dontshrink, -dontobfuscate and the -dontoptimzie options in the proguard.cfg. With -dontoptimize the app starts without errors.
Still, it would be interesting what exactly causes this specific error.
The Proguard documentation proudly states: "The ProGuard tool shrinks, optimizes, and obfuscates your code by removing unused code and renaming classes".
Well, I gave up with the 'shrinking' part of it after getting runtime errors like you describe. I added the line
-dontshrink
to the proguard.cfg
You can see which routines have been removed from your code by inspecting the file usage.txt.
I'm happy to say that in my projects it's always missing, meaning that the code is obfuscated but nothing has been removed. I don't get any runtime errors now.
I accidentally stumbled upon a possible solution. Well, it totally works in my case, so this IS a solution to the original problem:
Today, I implemented some code with #Override annotations, which didn't work, at first. Luckily, someone else already had the same problem and an easy Eclipse-related solution:
'Must Override a Superclass Method' Errors after importing a project into Eclipse
Now, I thought, well, if I was always using Java level 1.5 before, why not try ProGuard again, without the -dontoptimize option, now that I set it to 1.6. Can't hurt...
And what can I say, now the app starts and I don't get the strange error when EditText.length() is called in a method.
The optimizer may remove the method invocation and the method if it comes to the conclusion that the method doesn't have any side-effects. It should never create inconsistent code though, and I'm not aware of a problem like this. You should check if it persists with the latest version of ProGuard. Otherwise, you should file a bug report on the ProGuard site, preferably with a small example that illustrates the problem.
I had a similar problem to the OP and it ended up being a proguard config option I set -allowaccessmodification, removing this solved the issue.

Categories

Resources