Как использовать getString () в статической строке перед onCreate ()?

Я пытаюсь использовать getString() чтобы получить строку из ресурсов, чтобы назначить ее массиву строк до создания моей активности:

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)
};

когда я использую Resources.getSystem().getString(R.string.LCMeterMenu), Eclipse не жалуется, но я получаю ошибку во время выполнения:

вызвано: android.содержание.res.Resources$NotFoundException: строка ID ресурса #0x7f0a000a

но если я положу внутрь onCreate():

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

Я получаю строку, но я не могу назначить ее в финал Строка, которую я определил раньше. Если я использую только getString() до onCreate() Я получаю и статическое сообщение об ошибке. Как я могу использовать ресурсы до onCreate() глобальные переменные?

5 ответов


вы не можете инициализировать static final поле из ресурсов; поле должно быть инициализировано во время инициализации класса, и это происходит до привязки ресурсов приложения во время выполнения. (Кстати, причина, по которой вы не можете использовать Resources.getSystem() это Resources объект, который вы получаете таким образом, содержит только системные ресурсы, а не ресурсы приложения.)

Если вам нужны эти строки, доступные до привязки ресурсов приложения, единственное практическое что нужно сделать, так это поместить строки непосредственно в код. Тем не менее," Android way " будет организовывать ваш код, поэтому инициализация должна произойти только во время (или после) onCreate(). Просто инициализируйте массив строк в onCreate() и не беспокойтесь о том, чтобы сделать поля статическими или окончательными.

если вы не хотите, чтобы массив строк был связан с определенным действием, то вы можете подкласс Application и прочитайте массив из ресурсов внутри класса приложения onCreate() метод. (Необходимо также объявить пользовательский класс приложения в манифесте.) Однако, документы рекомендую против такого подхода. (Поскольку массив является частным, я подозреваю, что он тесно связан с одним действием в любом случае, поэтому использование Application подкласс не кажется оправданным.)

альтернативой является объявление одноэлементного класса для вашего массива. Функция одноэлементного доступа затем нуждается в Context таким образом, он может получить ресурсы, если необходимо:

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;
    }
}

(это предполагает, что строковые данные определены в <string-array> ресурс, как @JaiSoni предложил в своем ответе.) Еще раз, поле member не может быть объявлено final.


нет, вы не можете использовать ресурсы перед onCreate(). Вы можете получить экземпляр ресурсов в onCreate() С помощью getResources() где вы можете получить все строки. Кроме того, строки уже объявлены как статические, определив их в strings.xml.

псевдо-код для доступа к ресурсам,

Resources res = getResources();
String app_name = res.getString(R.string.app_name);

другим подходом может быть инициализация статического массива идентификаторами ресурсов (которые уже доступны, в отличие от самих ресурсов).

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

таким образом, вы можете отложить загрузку ресурсов, когда они действительно доступны:

String s = getResources().getString(MenuNames[i]);

ниже приведен рабочий подход для инициализации static final переменные в android из XML, такие как strings.xml.

  1. подкласс приложения и обеспечить "статический контекст"
  2. зарегистрировать класс приложения в manifest
  3. используйте статический контекст для инициализации констант

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. Ваш код приложения, например 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();
}

для рабочих примеров см. AbstractApplication и PreferencesServiceSharedPreferences.

обратите внимание, что этот подход также имеет свои минусы:

  • помимо того, что он противостоит "пути Android" (как предложил @Ted Hopp в ответ),
  • это делает тестирование намного сложнее. Вот почему призыв к MyApplication.getContext() обернут в другой метод. Поскольку это статический метод, переопределить его в тестовом коде непросто. Но вы можете использовать такую структуру, как Powermock для этой цели.
  • кроме того, он немного склонен к NullPointerExceptions. Как только контекст null (например, в вашем тестовом коде) код приложения аварийно завершает работу. Одним из вариантов преодоления этого является инициализация в конструкторе, где вы можете реагировать на getContext()возвращение null (см. пример).

что вы получаете от getString(int resId) уже будет константой для вашего приложения. Почему вы должны держать его в другой final static переменной. Ты можешь читать это так, когда захочешь, верно?