Как я могу получить содержимое ресурса из статического контекста?

Я хочу прочитать строки из файла xml, прежде чем делать что-либо еще, например setText для виджетов, так как же я могу сделать это без объекта действия, для которого вызывается getResources()?


person lost baby    schedule 08.12.2010    source источник


Ответы (15)


  1. Создайте подкласс Application, например public class App extends Application {
  2. Установите атрибут android:name вашего тега <application> в AndroidManifest.xml, чтобы он указывал на ваш новый класс, например. android:name=".App"
  3. В методе onCreate() вашего экземпляра приложения сохраните свой контекст (например, this) в статическом поле с именем mContext и создайте статический метод, который возвращает это поле, например. getContext():

Вот как это должно выглядеть:

public class App extends Application{

    private static Context mContext;

    @Override
    public void onCreate() {
        super.onCreate();
        mContext = this;
    }

    public static Context getContext(){
        return mContext;
    }
}

Теперь вы можете использовать: App.getContext() всякий раз, когда хотите получить контекст, а затем getResources() (или App.getContext().getResources()).

person Cristian    schedule 08.12.2010
comment
Я согласен, что это уродливый хак. Никогда нельзя присваивать динамические значения статическому объекту. - person Bostone; 23.02.2012
comment
@Bostone - это не динамические значения. Речь идет о настройке статических констант через ресурсы. Наоборот, я думаю, проблема этого способа в том, что мы не можем использовать эти константы как статические для всего класса приложения, даже если они являются статическими для всего класса приложения. Но 99% проблем можно решить таким образом. +1 Кристиану! - person Gangnus; 26.11.2012
comment
Экземпляр приложения не является динамическим значением, как так, @Gangnus? В любом случае - я на собственном горьком опыте убедился, что полагаться на статику в Android - не что иное, как головная боль. Теперь ты видишь это, теперь ты не видишь - person Bostone; 26.11.2012
comment
1. Ах да, практически, по реализации они динамические, или, лучше сказать, экземплярные константы. Экземпляра Application. Но что касается действий и, конечно же, setText и т. д., как в вопросе, они могут быть установлены статически, т.е. после этого ответа вы можете иметь их как константы, принадлежащие потомку класса Activity. 2. Статические переменные нормально работают в Android. Проблема в том, что если вы хотите использовать некоторые ресурсы в качестве значений для статических переменных/констант приложения, это невозможно. У вас есть только два частичных решения, приведенные здесь в качестве ответов. - person Gangnus; 27.11.2012
comment
Я не могу отделаться от мысли, что это «взлом». Хотя я его использую (кстати, спасибо за это решение, так как я собирался внедрить локализацию), у меня плохое предчувствие, как будто это как-то неправильно. - person Illiax; 14.09.2013
comment
Лучше или хуже, чем просто передавать Context в качестве первого параметра в каждом статическом методе вашего приложения? Первый кажется хакерским, но второй излишне повторяется. - person Dave; 14.01.2014
comment
В документах говорится, что обычно нет необходимости создавать подклассы Application. В большинстве случаев статические синглтоны могут обеспечивать ту же функциональность более модульным способом. Если вашему синглтону нужен глобальный контекст (например, для регистрации широковещательных приемников), функция для его извлечения может получить контекст, который внутренне использует Context.getApplicationContext() при первом создании синглтона. ~developer.android.com/reference/android/app/Application.html - person David d C e Freitas; 29.10.2014
comment
@MarcinRobaszyński +1, пока мои занятия выглядят вполне нормально! такое же ощущение здесь! - person Brabbeldas; 05.01.2015
comment
Это должна быть вики из вики... Я имею в виду, что нигде не указано, и это сохраняет мой шаблон MVP очень чистым. У меня работает на SDK 19›22 - person Raffaeu; 24.03.2015
comment
Вы должны прочитать это: stackoverflow.com/a/21336200/2016562 и эпический блог о контексте, написанный Дэйвом Смитом. - person Gabriele Mariotti; 27.08.2015
comment
Чтобы избежать утечки памяти, было бы лучше хранить Context в WeakReference: private static WeakReference‹Context› mContext; общедоступный статический контекст getContext(){ return mContext.get(); } Это должно помочь, когда приложение дает сбой, и вы не можете установить статический контекст в null (WeakReference может быть удален сборщиком мусора). - person FrankKrumnow; 14.10.2015
comment
Но он указывает на себя, зачем нам хранить его в WeakReference? - person Kimi Chiu; 08.02.2016
comment
@FrankKrumnow Это зависит от того, откуда вы будете вызывать контекст позже, но я согласен, что это, вероятно, безопаснее. - person Nilpo; 31.03.2016
comment
@FrankKrumnow, как установить mContext = this; в onCreate()? Это дает ошибку. - person Orcun Sevsay; 18.01.2017
comment
@MiloRambaldi Я использую этот код в onCreate(): mContext = new WeakReference‹Context›(this); Надеюсь, это поможет вам. - person Tom Spee; 09.02.2017
comment
Как может быть утечка памяти? AppContext ЯВЛЯЕТСЯ объектом приложения. - person NikkyD; 01.08.2017
comment
@NikkyD, да, это как бы намеренно «предварительно просочилось» .... как синглтон ... просто не держитесь за этот контекст приложения в том месте, где вы не должны ... тогда у вас есть больший потенциал получения утечек памяти. - person Sakiboy; 12.01.2018
comment
Мне нужно было установить атрибут имени тега приложения = .App в манифесте, чтобы это решение работало. То есть приложение ‹application android:name=.App... - person Phil; 22.02.2019
comment
Для решения в Kotlin ознакомьтесь с этим ответом ниже: stackoverflow.com/a/58627769/3451975 - person Jeehut; 28.12.2019
comment
Решение отлично работает для общих сценариев, но в моем случае, когда я меняю язык, я все равно получаю строки языка по умолчанию. (Происходит только тогда, когда я использую этот синтаксис для вызова строковых объектов). Любое решение? - person segfault404; 04.08.2020

Только для системных ресурсов!

Использовать

Resources.getSystem().getString(android.R.string.cancel)

Вы можете использовать их везде в своем приложении, даже в объявлениях статических констант!

person Gangnus    schedule 06.01.2012
comment
Это классно. Я обычно не обижаюсь... просто когда кто-то пишет в верхнем регистре :P Шучу. Ну, ваш стандарт работает для некоторых ресурсов, таких как строки и чертежи... однако, как говорится в документации, он не работает для таких вещей, как меры ориентации и т. д. Кроме того, и самое главное, это не позволит вам получить глобальный контекст, который иногда полезен для вещей, которые могут в нем нуждаться (например, создание экземпляра Toast, получение экземпляра SharedPreference, открытие базы данных, как говорит мой учитель латинского языка: et cetera). - person Cristian; 07.01.2012
comment
Им даже мира во всем мире не завоюешь :-). Но это помогает решить проблему, поставленную вопросом здесь. Я не говорю, что он решает все задачи, просто он решает свои задачи почти в каждом месте приложения. Я искал такое решение 10 месяцев - все время пользуюсь Android. И теперь я нашел это. - person Gangnus; 08.01.2012
comment
Вы должны быть осторожны здесь. Не пытайтесь найти ресурсы вашего приложения, используя этот метод. Прочтите мелкий шрифт: верните глобальный объект общих ресурсов, который обеспечивает доступ только к системным ресурсам (без ресурсов приложения) и не настроен для текущего экрана (не может использовать единицы измерения, не изменяется в зависимости от ориентации и т. д.). - person Bostone; 23.02.2012
comment
@DroidIn.net Цитата: Но только для системных ресурсов!. Я знаю /*вздыхает/* - person Gangnus; 23.02.2012
comment
Я получил исключение, используя это: android.content.res.Resources$NotFoundException: идентификатор строкового ресурса - person vinidog; 29.05.2016
comment
@vinidog Это очень странно. Лучше опубликуйте вопрос об этом. - person Gangnus; 29.05.2016
comment
Просто для ясности: R.string.my_own_string НЕ является системным ресурсом. Так что этот ответ действительно не касается вопроса. - person Sébastien; 17.04.2020
comment
@ Себастьен, вопрос о получении ресурсов в статическом контексте. Он не ограничивает ответы системой или личными ресурсами. Так что ответ абсолютно правильный. - person Gangnus; 17.04.2020
comment
Я получаю ошибку java.lang.ExceptionInInitializerError, вызванную причиной: android.content.res.Resources$NotFoundException: идентификатор строкового ресурса #0x7f120038 при использовании вашего решения. В чем может быть проблема? - person VanessaF; 17.09.2020
comment
@VanessaF, вы должны задать его как новый вопрос с кодом, выводом ошибок и описанием ваших неудачных попыток решить проблему. Невозможно идентифицировать проблему с таким плохим входом. - person Gangnus; 18.09.2020
comment
Спасибо, Гангнус, за ваш комментарий: я уже задавал вопрос о своей проблеме, но пока не получил хорошего ответа: stackoverflow.com/questions/63921633/ - person VanessaF; 18.09.2020
comment
@VanessaF Спасибо за ссылку, я поместил туда свой ответ. - person Gangnus; 18.09.2020

Мое решение Kotlin заключается в использовании статического контекста приложения:

class App : Application() {
    companion object {
        lateinit var instance: App private set
    }

    override fun onCreate() {
        super.onCreate()
        instance = this
    }
}

И класс Strings, который я использую везде:

object Strings {
    fun get(@StringRes stringRes: Int, vararg formatArgs: Any = emptyArray()): String {
        return App.instance.getString(stringRes, *formatArgs)
    }
}

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

Strings.get(R.string.some_string)
Strings.get(R.string.some_string_with_arguments, "Some argument")

Пожалуйста, не удаляйте этот ответ, позвольте мне оставить его.

person Vitalii Malyi    schedule 30.10.2019
comment
Простое и чистое решение, спасибо, что поделились кодом! - person Jeehut; 28.12.2019
comment
Спасибо! Хотя это известное решение, Strings было полезно. - person CoolMind; 24.04.2020
comment
Большое тебе спасибо. Вы спасли меня - person Mixno; 06.08.2020
comment
Большое спасибо. Отличное решение - person Android_programmer_office; 29.03.2021
comment
Честно говоря, самое чистое и простое, что я нашел до сих пор, просто работало! - person Marios Yiannakou; 19.07.2021

Есть и другая возможность. Я загружаю шейдеры OpenGl из таких ресурсов:

static private String vertexShaderCode;
static private String fragmentShaderCode;

static {
    vertexShaderCode = readResourceAsString("/res/raw/vertex_shader.glsl");
    fragmentShaderCode = readResourceAsString("/res/raw/fragment_shader.glsl");
}

private static String readResourceAsString(String path) {
    Exception innerException;
    Class<? extends FloorPlanRenderer> aClass = FloorPlanRenderer.class;
    InputStream inputStream = aClass.getResourceAsStream(path);

    byte[] bytes;
    try {
        bytes = new byte[inputStream.available()];
        inputStream.read(bytes);
        return new String(bytes);
    } catch (IOException e) {
        e.printStackTrace();
        innerException = e;
    }
    throw new RuntimeException("Cannot load shader code from resources.", innerException);
}

Как видите, вы можете получить доступ к любому ресурсу по пути /res/... Изменить aClass на свой класс. Это также то, как я загружаю ресурсы в тесты (androidTests)

person Gregory Stein    schedule 12.02.2017
comment
Единственное решение, которое сработало для меня, когда у меня не было Activity (разработка плагина без класса, который мог бы расширить Application). Спасибо +1 - person itaton; 20.08.2018

Ярлык

Я использую App.getRes() вместо App.getContext().getResources() (как ответил @Cristian)

Его очень просто использовать в любом месте вашего кода!

Итак, вот уникальное решение, с помощью которого вы можете получить доступ к ресурсам откуда угодно, например Util class .

(1) Создайте или отредактируйте свой Application класс.

import android.app.Application;
import android.content.res.Resources;

public class App extends Application {
    private static App mInstance;
    private static Resources res;


    @Override
    public void onCreate() {
        super.onCreate();
        mInstance = this;
        res = getResources();
    }

    public static App getInstance() {
        return mInstance;
    }

    public static Resources getRes() {
        return res;
    }

}

(2) Добавьте поле имени в свой тег manifest.xml <application. (или пропустить, если уже есть)

<application
        android:name=".App"
        ...
        >
        ...
    </application>

Теперь вы можете идти.

Используйте App.getRes().getString(R.string.some_id) в любом месте кода.

person Khemraj Sharma    schedule 11.07.2018
comment
Это решение не сработало для меня, выдает «java.lang.NullPointerException: попытка вызвать виртуальный метод» java.lang.String android.content.res.Resources.getString(int)» для нулевой ссылки на объект в android.app. ActivityThread.performLaunchActivity(ActivityThread.java:3047)' - person segfault404; 04.08.2020
comment
Я отредактировал ответ, метод в классе приложения getRes(), а не getResources() - person Khemraj Sharma; 04.08.2020
comment
Даже если я изменю метод, он не сработает. Все еще дает исключение нулевого указателя. Обратите внимание, что я вызываю его из другого класса. - person segfault404; 04.08.2020
comment
Да, я сделал . Возможно, вы захотите взглянуть на мой вопрос здесь stackoverflow.com/q/63245020/13572191. Я пробовал и другие решения, хотя они работают для языка по умолчанию, но не работают при смене языка. Спасибо за ответ - person segfault404; 04.08.2020
comment
Ваши строки также должны быть переопределены в строковом файле другого языка. - person Khemraj Sharma; 04.08.2020

Синглтон:

package com.domain.packagename;

import android.content.Context;

/**
 * Created by Versa on 10.09.15.
 */
public class ApplicationContextSingleton {
    private static PrefsContextSingleton mInstance;
    private Context context;

    public static ApplicationContextSingleton getInstance() {
        if (mInstance == null) mInstance = getSync();
        return mInstance;
    }

    private static synchronized ApplicationContextSingleton getSync() {
        if (mInstance == null) mInstance = new PrefsContextSingleton();
        return mInstance;
    }

    public void initialize(Context context) {
        this.context = context;
    }

    public Context getApplicationContext() {
        return context;
    }

}

Инициализируйте Singleton в вашем подклассе Application:

package com.domain.packagename;

import android.app.Application;

/**
 * Created by Versa on 25.08.15.
 */
public class mApplication extends Application {

    @Override
    public void onCreate() {
        super.onCreate();
        ApplicationContextSingleton.getInstance().initialize(this);
    }
}

Если я не ошибаюсь, это дает вам доступ к applicationContext везде, вызывайте его с помощью ApplicationContextSingleton.getInstance.getApplicationContext(); Вам не нужно очищать это в какой-либо момент, так как когда приложение закрывается, это все равно происходит.

Не забудьте обновить AndroidManifest.xml, чтобы использовать этот подкласс Application:

<?xml version="1.0" encoding="utf-8"?>

<manifest
    xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.domain.packagename"
    >

<application
    android:allowBackup="true"
    android:name=".mApplication" <!-- This is the important line -->
    android:label="@string/app_name"
    android:theme="@style/AppTheme"
    android:icon="@drawable/app_icon"
    >

Теперь вы должны иметь возможность использовать ApplicationContextSingleton.getInstance().getApplicationContext().getResources() из любого места, а также из очень немногих мест, где подклассы приложений не могут.

Пожалуйста, дайте мне знать, если вы видите что-то не так, спасибо. :)

person Versa    schedule 25.09.2015

Другое решение:

Если у вас есть статический подкласс в нестатическом внешнем классе, вы можете получить доступ к ресурсам внутри подкласса через статические переменные во внешнем классе, которые вы инициализируете при создании внешнего класса. Нравится

public class Outerclass {

    static String resource1

    public onCreate() {
        resource1 = getString(R.string.text);
    }

    public static class Innerclass {

        public StringGetter (int num) {
            return resource1; 
        }
    }
}

Я использовал его для функции getPageTitle(int position) статического FragmentPagerAdapter в моей FragmentActivity, что полезно из-за I8N.

person Stephan Brunker    schedule 21.10.2016

Думаю, можно и по-другому. Но иногда я использую это решение. (полный глобальный):

    import android.content.Context;

    import <your package>.R;

    public class XmlVar {

        private XmlVar() {
        }

        private static String _write_success;

        public static String write_success() {
            return _write_success;
        }


        public static void Init(Context c) {
            _write_success = c.getResources().getString(R.string.write_success);
        }
    }
//After activity created:
cont = this.getApplicationContext();
XmlVar.Init(cont);
//And use everywhere
XmlVar.write_success();
person user2684935    schedule 13.08.2014

Я загружаю шейдер для openGL ES из статической функции.

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

public class MyGLRenderer implements GLSurfaceView.Renderer {

    ...

    public static int loadShader() {
        //    Read file as input stream
        InputStream inputStream = MyGLRenderer.class.getResourceAsStream("/res/raw/vertex_shader.txt");

        //    Convert input stream to string
        Scanner s = new Scanner(inputStream).useDelimiter("\\A");
        String shaderCode = s.hasNext() ? s.next() : "";
    }

    ...

}
person user2174870    schedule 31.08.2018

Я использую уровень API 27 и нашел лучшее решение после двухдневной борьбы. Если вы хотите прочитать XML-файл из класса, который не является производным от Activity или Application, сделайте следующее.

  1. Поместите файл testdata.xml в каталог с ресурсами.

  2. Напишите следующий код, чтобы разобрать документ testdata.

        InputStream inputStream = this.getClass().getResourceAsStream("/assets/testdata.xml");
    
        // create a new DocumentBuilderFactory
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        // use the factory to create a documentbuilder
        DocumentBuilder builder = factory.newDocumentBuilder();
        // create a new document from input stream
        Document doc = builder.parse(inputStream);
    
person Jnana    schedule 12.02.2019

Получение изображения как InputStream без контекста:

Class<? extends MyClass> aClass = MyClass.class;
URL r = aClass.getResource("/res/raw/test.png");
URLConnection urlConnection = r.openConnection();
return new BufferedInputStream(urlConnection.getInputStream());

Если вам нужно дерево каталогов для ваших файлов, оно также будет работать (активы поддерживают подкаталоги):

URL r = aClass.getResource("/assets/images/base/2.png");
person Yuliia Ashomok    schedule 12.09.2020

почему ты не пытаешься

Resources.getSystem().getString(R.string.foo);
person nico    schedule 30.06.2021

В вашем классе, где вы реализуете функцию static, вы можете вызвать метод private\public из этого класса. Метод private\public может получить доступ к getResources.

например:

public class Text {

   public static void setColor(EditText et) {
      et.resetColor(); // it works

      // ERROR
      et.setTextColor(getResources().getColor(R.color.Black)); // ERROR
   }

   // set the color to be black when reset
   private void resetColor() {
       setTextColor(getResources().getColor(R.color.Black));
   }
}

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

Text.setColor('some EditText you initialized');
person Maor Cohen    schedule 13.04.2015

если у вас есть контекст, я имею в виду внутри;

public void onReceive(Context context, Intent intent){

}

вы можете использовать этот код для получения ресурсов:

context.getResources().getString(R.string.app_name);
person Eren    schedule 26.05.2015
comment
Название вопроса говорит в статическом контексте. Что ваш ответ не распространяется. - person Rune Schjellerup Philosof; 14.12.2016

person    schedule
comment
Ну, проблема в том, что getResources() нужен контекст. Так что это, вероятно, не решение без объекта активности (в котором вы разместили метод onCreate()) - person Tobias Reich; 26.09.2018