I'm trying to build an Android app that I've been working on (it's a project I adopted so most of the code isn't mine - which isn't helping :P) and I'm running into an issue.
The app builds just fine in debug mode (building and installing it on a device for testing). But when I try to build a release it fails.
This is the error in the gradle console:
Execution failed for task ':app:lintVitalRelease'.
Lint found fatal errors while assembling a release target.
And this is what it says in Messages Gradle Build:
Error:Error: This fragment class should be public ([com.company.appname].fragments.create_dilemma.CreateDilemmaFragment1_2.UploadDialogFragment) [ValidFragment]
This is the offending line:
private class UploadDialogFragment extends DialogFragment implements View.OnClickListener
So I change private to public and then it complains that it should be a static class. Thing is, it looks like this class isn't supposed to be static because AS isn't happy with pretty much any of the code as soon as I make it static.
So I'm left with a few questions:
How is this only an issue for the release build and not for debug?
Is there a way to ignore this 'error' when building a release?
There must be a reason for this error, right? Why is it ignored for debug and not for release? What are the up/downsides of fixing this? Because the app works just fine as far as I can tell so I don't really see the problem..?
PS: My java skills are so-so. I know my way around the language but I have a lot to learn when it comes to knowing what a static class exactly is and what is allowed and what not, why it is(n't), etc. So plz be gentle, I'm trying to learn this stuff :)
Update: As per request here's the relevant part of my build.gradle:
android {
compileSdkVersion 24
buildToolsVersion '24.0.1'
useLibrary 'org.apache.http.legacy'
defaultConfig {
applicationId "[com.pany.appname]"
minSdkVersion 15
targetSdkVersion 22
multiDexEnabled true
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'
}
}
productFlavors {
}
}
I'm summarizing some of the answers given in this post to collect them in one. Makes it easier to accept it, too :)
-- WHY THE QUICK FIX IS NOT A REAL FIX --
It's POSSIBLE to have this project build a release apk successfully (see below). But it's probably not a good idea to just do that and not think about it.
As pointed out by X3Btel:
Fragments needs to be public because systems recreates them on orientation change. The other warning is because non static inner classes holds reference to their outter class, hence it creates memmory leak. Best course of action would be move the fragment to his own class. Or make it public and ignore the lint warning (this may create memmory leak but keeping it private may crash the app)
In my case I can keep it non-static and circumvent memory leaks by making sure I finish the fragment whenever its parent activity is finished. It's not pretty but it'll fix the memory leak and I don't have to refactor A LOT of code I didn't write. That said: It apparently is bad practice to have an activity or fragment and declare another activity/fragment as an inner class because of the way the Android lifecycle works.
Here's some more reading on the topic which I found useful (and only found AFTER I posted this question):
Should an internal DialogFragment class be static or not?
-- THE QUICK FIXES --
1) Don't check for lint errors during build (as pointed out by Jay Shan)
Add lintOptions -> checkReleaseBuilds option to build.gradle
android {
// ..
lintOptions {
checkReleaseBuilds false
}
}
2) Check for errors but keep building even when they are found
This is probably a little bit safer than not checking for errors at all because at least you'll get a warning somewhere in the log output.
Add lintOptions -> abortOnError option to build.gradle
android {
// ..
lintOptions {
abortOnError false
}
}
3) Supress the error where it happens
I find this to be the preferred method because you can still use lint for finding other problems AND have it abort when that happens BUT at the same time you can ignore things you've checked.
In my case I had to add #SuppressLint("ValidFragment") before the offending line:
#SuppressLint("ValidFragment")
private class UploadDialogFragment extends DialogFragment implements View.OnClickListener
{
// ..
}
UPDATE 2018/01/04
If you use a recent version of Android Support Library (and its Fragment implementation instead of the OS's) your app will crash (IllegalStateException) if you try to initialize that Fragment. Suppressing the warning will not help you. You'll just have to fix the underlying problem, make the inner class public and static, or move the class to a separate file.
You can put this option in android block of build.gradle section
lintOptions {
checkReleaseBuilds false
}
Jay Shan`s answer should work. But better to understand what is the problem. Fragments needs to be public because systems recreates them on orientation change. The other warning is because non static inner classes holds reference to their outter class, hence it creates memmory leak.
Best course of action would be move the fragment to his own class. Or make it public and ignore the lint warning (this may create memmory leak but keeping it private may crash the app)
Related
When I try to debug my app, I can't see variables values in the debugger.
I have the following error when I try to evaluate an expression for instance (same with variables watchers) :
Cannot find local variable 'data' with type com.myorg.myapp.data.objects.DataToUpdate
The IDE seems to understand the type of my variable but it can't find it.
The variable is used just after so it has not been optimized away.
I belive that the code has been optimized but only part of it.
I built an apk and decompiled it with some decompilers online to see if it was minifyed or something.
Part of my code is not
My class fields are ok, I can see their values in the debugger and their name is the same as in my code
However, other parts of my code seem to be optimized (variables names are not the same)
E.g. : my variable "data" of type DataToUpdate becomes "DataToUpdate r118" in the decompiled code
I had nothing put in the debug buildType of my build.gradle. I added the following lines according to what I saw on the web to try to make it work :
buildTypes {
debug {
debuggable true
testCoverageEnabled = false
minifyEnabled false
useProguard false
}
}
I am using Android Studio 4.0 Beta 4 but it also didn't work reliably in Android Studio 3.6.
I ended up using the logcat to understand and then correct the bugs in my code.
The debugger worked in other methods so it may be usefull to try to reproduce the problem in other methods, to debug it there.
A debugger working as expected would be better, but I couldn't make it work in this particular method.
I have an issue with R8. In MyLib I have public abstract MyLibsClass in which I have protected methods. MyChildClass extends from MyLibsClass in MyApp and after R8's magic all protected methods (including protected abstract) in MyLibsClass are changed into public ones, and of course in MyChildClass I'm getting "attempting to assign weaker access privileges ('protected'); was 'public') issue as trying to override protected abstract methods.
Additional info
gradle-6.0.1
MyLib's build.gradle
release {
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'),'proguard-rules.pro'
}
proguard-rules.pro
-keep class com.example.mylib.*{
public protected *; }
-keep class com.example.mylib.*$*{
public protected *; }
Anyone had this kind of issue or know a way to fix this?
So based on discussion here ,
DON'T USE DEFAULT PROGUARD SETTINGS FOR LIBRARIES
as allowAccessModification is enabled in default proguard settings, which is located in Android SDK (\Android\Sdk\tools\proguard\proguard-android-optimize.txt) and my mistake was using this for my libraries.
Citation from proguard manual
you probably shouldn't use this option when processing code that is to
be used as a library, since classes and class members that weren't
designed to be public in the API may become public.
So if anyone has the same issue I will suggest to create your own base config file for proguard and copy past whole default configs without "allowAccessModification" into it.
Also if someone interested more, you can track this issue. Hopefully will get separate config file for libraries in near feature.
I faced the same problem, and thanks to #Hayk Nahapetyan's answer, I could resolve it.
Here is my solution with a little more detail.
In the library module's build.gradle, remove the default file from the buildTypes's release closure:
release {
minifyEnabled true
proguardFiles 'proguard-rules.pro'
}
R8 no longer uses the default file that is provided in the Android SDK. It generates one at build time, and puts it in the module's build directory at build/intermediates/default_proguard_files/global.
Copy the contents of proguard-android-optimize.txt-a.b.c (where a.b.c is the library version, if set) from that location to the top of the module's proguard-rules.pro. Then remove -allowaccessmodification; two times, if it originally appeared in both files.
This was also reported on the R8 bug tracker, and resolved there. See http://issuetracker.google.com/147447502.
I'm looking for a way to temporarily suppress a lint error or to schedule an error after a particular date or condition.
Here's some context:
I have an android app that is translated to multiple languages and I have set it up so that lint fails with an error if there are missing translations. Evidently this is done so that untranslated strings don't escape into the wild.
Whenever there's a new string resource, it takes a while until it gets translated, during which lint fails.
This can be suppressed or the strings can be marked as untranslatable but that beats the purpose of having the check in the first place.
Aside from that context, there are plenty more instances when a particular setting is "temporary". I don't like having to remember to flip back every switch.
The builds are on a CI server, releases are often and this happens in a team where anyone is able to "temporarily" ignore warnings.
Is there an automagical way to make sure things don't get ignored upon release?
Update:
Since asking this question, someone has developed a library that does just what I asked and more: https://github.com/Stuie/papercut
I don't know how you determin when it is time to de/activate lint. But maybe this helps:
You can switch Lint on/off with a boolean in the gradle script:
android {
lintOptions {
if (lintOn){
checkReleaseBuilds true
abortOnError true
} else {
checkReleaseBuilds false
abortOnError false
}
}
}
Add lintOn=false to your gradle.properties, otherwise gradle won't recognize it as variable.
Now you cann add a task and make it run before the build task:
task preBuild << {
// do stuff to determin if lint should run
lintOn = true
}
build.dependsOn preBuild
In this preBuild task you can now implement some code to check if you should run lint or not.
Here are some good examples of what a task can do.
When I add to my project the multidex:true, and make an Application class that extends from the MultiDexApplication, my project build time passed from 20 sec to around 90 sec.How to do some faster?
If you are like me who already tried Vic Vu's solution but still can't avoid enabling multiDex then you can try this (as long as your are using a device that has Android 5.0 and above).
Note This will only speed up your development build. Your production build will still be slow.
Basically you need to introduce 2 product flavors one for dev and one for prod.
Add multiDexEnabled true
android {
productFlavors {
// Define separate dev and prod product flavors.
dev {
// dev utilizes minSDKVersion = 21 to allow the Android gradle plugin
// to pre-dex each module and produce an APK that can be tested on
// Android Lollipop without time consuming dex merging processes.
minSdkVersion 21
}
prod {
// The actual minSdkVersion for the application.
minSdkVersion 14
}
}
...
buildTypes {
release {
runProguard true
proguardFiles getDefaultProguardFile('proguard-android.txt'),
'proguard-rules.pro'
}
defaultConfig {
applicationId "com.something.something"
targetSdkVersion 23
versionCode 1
versionName "1.0.0"
multiDexEnabled true
}
}
dependencies {
compile 'com.android.support:multidex:1.0.1'
}
And I have a class which extends Application so I had to override attachBaseContext()
#Override
protected void attachBaseContext(Context base) {
super.attachBaseContext(base);
MultiDex.install(this);
}
If you are not extending Application simply use MultiDexApplication in your AndroidManifest.xml application tag.
Ensure that in your Android Studio Build Variants you are pointing to devDebug.
Read the complete instructions here https://developer.android.com/studio/build/multidex.html#dev-build
Supplying as an answer because this is better fit with the formatting.
To simply answer your question: No, there is no way. Multidex is a process meant to help lift the burden of the 65k method limit. This process is complicated and will simply make your build times longer.
The best you can can do is lower your method count.
In your build.gradle (supplied here) you're using:
`compile 'com.google.android.gms:play-services:8.3.0'`
But if you look at the most recent play services api you can pick and choose what services you actually need.
Look at Table 1 on this page.
Only use the ones you need. Google play services as a whole is somewhere around 30k methods.
That should help.
Multidexing uses more memory. As you get closer to your max heap size in Java you'll find Java spends more time doing GC than it does doing any real work, this can slow things down a lot.
I'd strongly recommend increasing the max heap size when using multidex. Add the following to the android closure in your build.gradle file to make the max heap size 4GB (Make it larger/smaller if you wish):
dexOptions {
javaMaxHeapSize "4g"
}
It depends.
You haven't specified it in your question, but if you just want to speed-up your development builds - then you can avoid the extra work. Official documentation includes a whole section about that.
I'm converting my app to use gradle, and I'm trying to use the buildTypes. I have a Constants class which I wish to modify for my release build. So I have a file at src/main/java/my/package/name/Constants.java and at src/release/java/my/package/name/Constants.java.
When I try to build this, gradle tells me the build failed on the Constants file in my release buildtype, with the error that it's a duplicate class.
I also tried adding a different sourceSet for this in my build.gradle like this:
sourceSets {
main {
java.srcDirs = ['src/main/java'];
//...
}
release {
java.srcDirs = ['src/release/java'];
}
}
But this still gives me the same error. So I'm wondering, what am I doing wrong here?
You can not have a class in main and release. You need to split it into something like debug and release.
gradle will merge the source sets for each buildType with main.
This is the reason, why the class gets duplicated in your release build.
So the rule is: put a class into main, or in every buildType but not both.
The answer from "fix" helped me on the way, but I got an error from the main Gradle, saying a constant was missing (in my class Config). This since I had my class only in paid and free version and not in main. Could not find the Config class.
Im not sure if this is a bug in Studio... I finally solved it with the following:
buildTypes {
release {
...
buildConfig "public static final boolean XYZ = false;"
}
}
And the instead of using my Config.XYZ class constant I used buildConfig.XYZ