Как использовать 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
. Вы можете читать его так, как только захотите, правильно?