Android resource IDs suddenly not final, switch()'es broken
Asked Answered
S

7

55

PREAMBLE: this question is quite obsolete, it was written when the preferred Android dev environment was Eclipse with the Android plugin.


I had a Java Android project for a while. Today, I've updated the Android dev tools to the Google's latest. And the project broke - I get a bunch of "case expressions must be constant expressions" compilation error messages.

Turns out that the R.java file is being now generated differently. Formerly, it would have a bunch of

public static final int MyID=0x12340000;

statements; now, it looks (after a clean/rebuild) like this:

public static int MyID=0x12340000;

final is gone. So all switches on resource IDs that I had (and I had a few) are wrong. What happened, please? Is it just me? What's the rationale here? Is it documented anywhere? Can I bring final back somehow?

Selfabnegation answered 20/10, 2011 at 19:6 Comment(0)
M
43

This happened about yesterday, when the SDK/ADT 14 got released:

As of ADT 14, resource constants in library projects are no longer final. This is explained in greater detail in http://tools.android.com/tips/non-constant-fields

There's a quickfix available from ADT 14: http://tools.android.com/recent/switchstatementconversion

To quote from the rationale:

When multiple library projects are combined, the actual values of the fields (which must be unique) could collide. Before ADT 14, all fields were final, so as a result, all libraries had to have all their resources and associated Java code recompiled along with the main project whenever they were used. This was bad for performance, since it made builds very slow. It also prevented distributing library projects that didn't include the source code, limiting the usage scope of library projects.

The reason the fields are no longer final is that it means that the library jars can be compiled once and reused directly in other projects. As well as allowing distributing binary version of library projects (coming in r15), this makes for much faster builds.

Mcgarry answered 20/10, 2011 at 19:11 Comment(7)
Makes sense, actually. I see where they're coming from. The fact that Android libraries were just linked sources as opposed to, well, compiled libraries, has been a minor pain point for me for some time. Also, kudos to Google for providing a magic refactorer.Selfabnegation
This is just a consequence of an idiotic design decision by the Android team. Whenever you see some generated file with a bunch of magic number constants, you should look at it funny. Thanks, Google for breaking a bunch of your developers' source code due to complete lack of foresight. Public static ints. Much better?!Superfamily
This answer is marked as solution - but I don't see it... Very nice discussions, suggestions, documentation links and etc only... What is concrete solution? What should I change in my code to fix the issue - please in a few words? Or no solution? - then please answer in one word: "no". ?Toot
@RussiaDroneFlights Did you read any of the links? They say to use if-else clauses instead of switch statements if you need to do someting conditionally with resource IDs. Switch statements would require the IDs to be final, which they haven't been for slightly over nine years now.Mcgarry
This will makes you do a lot of extra work and do a lot of unnecessary modify of your code, just add android.nonFinalResIds=false in the gradle.properties file will working.Malawi
Bad Android design, ids must be final.Canberra
@Malawi - I am doing this. I think I love you! This is saving my day!!! I just spent 2 hours researching this and your little comment here is the ONLY good answer I have found. ty.Quizmaster
R
14

You could switch over to using If/Else statements and the warning will go away.

Sample:

    @Override
    public void onClick(final View v) {
        //finds which button was pressed
        final int buttonView = v.getId();
        String current = fromEditText.getText().toString();
        if (buttonView == R.id.bA) {
            current += getString(R.string.a);
        } 
  }
Ramsgate answered 5/11, 2020 at 9:20 Comment(1)
😂😭🤣😀❤️ Good one!Burhans
A
13

Just add parentheses:

switch (view.getId()) {
    case (R.id.view1):
        break;
    case (R.id.view2):
        break;
}
Akmolinsk answered 12/6, 2022 at 7:5 Comment(4)
Why does this work? And is it safe to do?Prude
@Prude I think it makes it some kind of constant, not only a reference. I use it some months in production mode with many customers and yet i've not gotten any crash or complaint.Akmolinsk
The case statements are hardcoded at compile time. What you need is the resource to be environment constant and it is not. with or without parentheses.. This is similar with: "final int VIEW_ID_1 = R.id.view1;" The switch will fail if the resource will be changed later because it will no longer match the hardcoded resource.Pydna
The only thing this does is potentially make the detector miss this case. But it doesn't solve the case when the ids will actually be non-final (the Java compiler will not compile this code then). So it's a band-aid at best (all it does it silence the warning, but it doesn't fix the problem).Stutter
M
6

Google recommends you use if/else conditions

http://tools.android.com/tips/non-constant-fields

To change them automatically you can place the caret on the switch keyword and press Alt + Enter on Windows (Option + Enter on Mac) and select Replace 'switch' with 'if'

Manmade answered 4/10, 2021 at 15:42 Comment(0)
M
4

This is because in new AGP verion, Google make all rescours ids non final, add android.nonFinalResIds=false in your gradle.properties file in the root directory of your project to make sure the AGP generate final res ids will make you avoid modify your code.

Malawi answered 29/7, 2023 at 0:37 Comment(2)
@SevaAlekseyev well i can confirm it also just happened again now in gradle version 8.0Lordan
It's odd that I wrote an entire android app (thankfully the only one I ever had to write) in 2017 and the auto generated code and "recommended" code was all using switch statements. (sure, I targeted an older Android OS version... still. Very messed up situation). This makes me question the wisdom of Java switch() statements entirely. It never would have mattered if the hardware didn't fail and I didn't have to "recompile" to "re-target' a newer phone and end up practically re-writing the entire application.Quizmaster
C
1

You should use view binding!

android {
    ...
    viewBinding {
        enabled = true
    }
}
Crayton answered 10/11, 2020 at 6:12 Comment(2)
Doesn't work with menus. The original question was mostly about menu IDs.Selfabnegation
Adds a lot of classes and also doesn't actually fix the problem, it just uses another way to identify and work with views.Vivi
P
-1

Just add this snipped in your module-level build.gradle file:

android {
    ...
    lintOptions {
        disable 'NonConstantResourceId'
    }
}

More: https://developer.android.com/studio/write/lint#gradle

Phonograph answered 15/10, 2020 at 17:47 Comment(2)
Any disadvantage of doing disabling lint option.Legnica
Careful, this just disables the Lint Warning, it does not actually fix the problem in itself.Kraemer

© 2022 - 2024 — McMap. All rights reserved.