20

I am trying to use getString() to get an String from resources to assign it to an String array before my activity is created:

private static final String[] MenuNames = {
    Resources.getSystem().getString(R.string.LCMeterMenu),
    Resources.getSystem().getString(R.string.FrecMenu),
    Resources.getSystem().getString(R.string.LogicAnalyzerMenu),
    "Prueba con achartengine",
    Resources.getSystem().getString(R.string.BrazoMenu)
};

When I use Resources.getSystem().getString(R.string.LCMeterMenu), Eclipse doesn't complain but I get an error at runtime:

Caused by: android.content.res.Resources$NotFoundException: String Resource ID #0x7f0a000a

But if I put inside onCreate():

Log.i("StringR", "String: " + getString(R.string.LCMeterMenu));

I get the String but I can't assign it to the final String I defined before. If I use only getString() before onCreate() I get and static error message. How can I use resources before onCreate() for global variables?

Ted Hopp
  • 232,168
  • 48
  • 399
  • 521
Andres
  • 6,080
  • 13
  • 60
  • 110
  • 1
    you have try String `getResources().getString(R.string.LCMeterMenu);` instead of `Resources.getSystem().getString(R.string.LCMeterMenu)` – ρяσѕρєя K Sep 20 '12 at 04:29
  • Why do you want to declare the array as final. Why can't you declare the array as private static and initialize the array in onCreate(). – knvarma Sep 20 '12 at 04:31
  • Because the array will not change along the Activity execution isn't it better to use final ? – Andres Sep 20 '12 at 04:34
  • 1
    @Andres if you want it to be final, you can use it this way. final String[] sa = new String[n]; as you want to populate the array with the resource strings you know n value. and then in onCreate update it like. sa[0] = resourceString1; etc..,remember you can change the values in, or say populate the Array whenever you want, even if its declared as final. – Archie.bpgc Sep 20 '12 at 06:51

5 Answers5

28

You cannot initialize a static final field from resources; the field needs to be initialized at the time the class is initialized and that happens before the application resources have been bound at run time. (By the way, the reason you cannot use Resources.getSystem() is that the Resources object you obtain that way contains only system resources, not any application resources.)

If you need those strings available before the application resources are bound, the only practical thing to do is to put the strings into the code directly. However, the "Android way" would be to organize your code so initialization only needs to happen during (or after) onCreate(). Just initialize the string array in onCreate() and don't worry about making the fields static or final.

If you don't want the string array to be associated with a particular activity, then you can subclass Application and read the array from resources inside the application class's onCreate() method. (You also need to declare your custom application class in the manifest.) However, the docs recommend against such an approach. (Since the array is private, I suspect that it is closely tied to a single activity anyway, so the use of an Application subclass doesn't seem warranted.)

An alternative is to declare a singleton class for your array. The singleton accessor function then needs a Context so it can retrieve the resources if necessary:

public class StringArray {
    private static String[] theArray;
    public static String[] getArray(Context context) {
        if (theArray == null) {
            theArray = context.getResources().getStringArray(R.array.my_strings);
        }
        return theArray;
    }
}

(This assumes the string data are defined in a <string-array> resource like @JaiSoni suggested in his answer.) Once again, the member field cannot be declared final.

Ted Hopp
  • 232,168
  • 48
  • 399
  • 521
  • 1
    @Jose_GD - I see that JaiSoni deleted that answer. The same info can be found in [the docs here on string array resources](http://developer.android.com/guide/topics/resources/string-resource.html#StringArray). – Ted Hopp Jun 07 '13 at 15:04
  • 1
    Note that you *can* assign a static field in onCreate(), it simply won't have a useful value previously. And if the value you assign it may be different on a subsequent run, that could "confuse" other instances which share it. – Chris Stratton Oct 19 '14 at 15:38
4

No, you can't use Resources before onCreate(). You can get the instance of Resources in onCreate() by using getResources() where you can get all the Strings. Also the strings are already declared as static by defining them in the strings.xml.

Pseudo code for accessing the Resources,

Resources res = getResources();
String app_name = res.getString(R.string.app_name);
Lalit Poptani
  • 67,150
  • 23
  • 161
  • 242
  • If I use getResources().getString() I get: "Cannot make a static reference to the non-static method getResources() from the type ContextWrapper" – Andres Sep 20 '12 at 04:36
  • Yes, I tried on that way and it works but I have to eliminate static keyword I think I will have to eliminate static or final there is not another way as far as I see. – Andres Sep 20 '12 at 04:42
4

Another approach could be to initialize the static array with resource identifiers (which are already available as opposed to the resources themselves).

private static final int[] MenuNames = {
    R.string.LCMeterMenu,
    R.string.FrecMenu,
    ...
};

This way, you can defer the loading of resources to when they are actually available:

String s = getResources().getString(MenuNames[i]);
bompf
  • 1,374
  • 1
  • 18
  • 24
1

The following is a working approach to initialize static final variables in android from XML, such as strings.xml.

  1. Subclass application and provide a "static context"
  2. Register the application class in manifest
  3. Use the static context to initialize your constants

1. MyApplication.java

public abstract class MyApplication extends Application {

    private static Context context;

    @Override
    public void onCreate() {
        super.onCreate();
        context = getApplicationContext();
    }

    /**
     * Returns a "static" application context. Don't try to create dialogs on
     * this, it's not gonna work!
     * 
     * @return
     */
    public static Context getContext() {
        return context;
    }
}

2. AndroidManifest.xml

<application
    android:name=".android.application.MyApplication"
    <!-- ... -->
</application>

3. Your application code, e.g. Activity

private static final String[] MenuNames = {
    getContext().getString(R.string.LCMeterMenu),
    getContext().getString(R.string.FrecMenu),
    getContext().getString(R.string.LogicAnalyzerMenu),
    "Prueba con achartengine",
    getContext().getString(R.string.BrazoMenu)
};
protected static Context getContext() {
    return MyApplication.getContext();
}

For working examples refer to AbstractApplication and PreferencesServiceSharedPreferences.

Note that this approach also has its downsides:

  • Apart from being opposed to the "Android way" (as @Ted Hopp suggested in his answer),
  • it makes testing a bit difficult. That is why the call to MyApplication.getContext() is wrapped in another method. As it is a static method, overriding it in testing code is not simple. But you could use a framework such as Powermock for this purpose.
  • In addition it is a bit prone to NullPointerExceptions. As soon as the context is null (e.g. in your testing code) the application code crashes. One option to overcome this, is to do the initialization in a constructor, where you could react to getContext()returning null (see example).
Community
  • 1
  • 1
schnatterer
  • 7,525
  • 7
  • 61
  • 80
0

Whatever you get by the getString(int resId) will already be a constant for your application. Why do you have to keep it in another final static variable. You can read it like that whenever you want, right?

midhunhk
  • 5,560
  • 7
  • 52
  • 83
  • Isn't it the right way of using my String if it is not going to change along my Activity execution ? Better performance also ? I am new to Java so I am learning :) – Andres Sep 20 '12 at 04:38
  • If you have a lot of instances where you would be calling the `getResources().getString()` method, then do the initialization in the `onCreate()` method and make your variable as static. That's my opinion. – midhunhk Sep 20 '12 at 04:44
  • "Why do you have to keep it in another final static variable". Because using **static final STRING_CONSTANT** gives you more readable code. For example: tv.setText(STRING_CONSTANT) vs. tv.setText(getString(R.string.string_constant)) – Jose_GD Jun 07 '13 at 13:16