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

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

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.content.res.Resources $NotFoundException: Идентификатор ресурса строки # 0x7f0a000a

Но если я помещаю внутри onCreate():

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

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

Ответы

Ответ 1

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

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

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

Альтернативой является объявление одиночного класса для вашего массива. Тогда для функции Singleton Accessor требуется 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, предложенном в его ответе.) Еще раз, поле участника не может быть объявлено final.

Ответ 2

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

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

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

Ответ 3

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

  • Приложение подкласса и укажите "статический контекст"
  • Зарегистрировать класс приложения в манифесте
  • Используйте статический контекст для инициализации ваших констант

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 not gonna work!
     * 
     * @return
     */
    public static Context getContext() {
        return context;
    }
}

2. AndroidManifest.xml

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

3. Ваш код приложения, например. Активность

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.
  • Кроме того, он немного склонен к NullPointerException s. Как только контекст null (например, в вашем тестовом коде), код приложения сработает. Один из вариантов этого - выполнить инициализацию в конструкторе, где вы можете реагировать на getContext() возврат null (см. пример).

Ответ 4

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

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

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

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

Ответ 5

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