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

168

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

потерянный ребенок
источник

Ответы:

373
  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()).

Cristian
источник
9
Экземпляр приложения не является динамическим значением, как же так @Gangnus? В любом случае - я обнаружил, что полагаться на статику в Android - это не что иное, как головная боль. «Теперь ты видишь это, а теперь нет»,
Бостоне,
18
Я не могу не думать, что это «взлом». Несмотря на то, что я использую его (кстати, спасибо за то, что предоставил это решение, так как я собирался экстернализовать локализацию), у меня возникает такое плохое предчувствие, как будто это как-то неправильно.
Illiax
8
Лучше или хуже, чем просто передать Context как первый параметр в каждом статическом методе в вашем приложении? Первый чувствует себя хакером, а второй бесполезно повторяется.
Дэйв
12
В документах говорится: «Обычно нет необходимости создавать подклассы Application. В большинстве случаев статические синглтоны могут предоставлять те же функциональные возможности более модульным способом. Если вашему синглтону необходим глобальный контекст (например, для регистрации широковещательных приемников), функция извлечения ему может быть задан контекст, который внутренне использует Context.getApplicationContext () при первом создании синглтона. " ~ developer.android.com/reference/android/app/Application.html
Дэвид Д и Фрейтас,
25
Чтобы избежать утечки памяти, было бы лучше сохранить Контекст в WeakReference: private static WeakReference <Context> mContext; открытый статический контекст getContext () {return mContext.get (); } Это должно помочь, когда приложение вылетает, и вы не можете установить статический контекст на ноль (WeakReference может собираться мусором).
FrankKrumnow
102

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

использование

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

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

Гангнус
источник
2
Это классно. Я обычно не обижаюсь ... просто когда кто-то использует прописные буквы: P Шучу. Ну, ваш стандарт работает для некоторых ресурсов, таких как строки и элементы рисования ... однако, как говорится в документации, он не работает хорошо для таких вещей, как меры ориентации и т. Д. Кроме того, и, самое главное, это не позволит вам получить глобальный контекст, который иногда полезен для вещей, которые могут ему понадобиться ( Toastнапример, поднять , получить SharedPreferenceэкземпляр, открыть базу данных, как говорит мой учитель латинского языка: и так далее ).
Кристиан
1
Вы даже не можете завоевать мир во всем мире этим :-). Но это помогает решить проблему, поставленную здесь вопросом. Я не говорю, что он решает каждую задачу, только то, что он решает свою задачу почти на каждом месте приложения. Я искал такое решение в течение 10 месяцев - все время использую Android. И теперь я нашел это.
Гангнус
18
Вы должны быть осторожны здесь. Не пытайтесь найти ресурсы вашего приложения, используя этот метод. Прочитайте мелкий шрифт: вернуть глобальный объект общих ресурсов, который обеспечивает доступ только к системным ресурсам (без ресурсов приложения) и не настроен для текущего экрана (не может использовать единицы измерения, не изменяется в зависимости от ориентации и т. Д.).
Бостон,
4
@ DroidIn.net Образец цитирования: "Но только для системных ресурсов!" Я знаю / * вздох / *
Гангнус
1
Я получил исключение, используя это: android.content.res.Resources $ NotFoundException: строковый идентификатор ресурса
vinidog
6

Мое решение 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")

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

Виталий Малый
источник
Простое и понятное решение, спасибо, что поделились кодом!
Jeehut
Спасибо! Хотя это известное решение, Stringsбыло полезно.
CoolMind
4

Есть и другая возможность. Я загружаю шейдеры 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)

Грегори Стейн
источник
1
Единственное решение, которое работало для меня, когда у меня не было Activity (разработка плагина без класса, который мог бы расширить Application). Спасибо +1
итатон
3

Синглтон:

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

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

Не забудьте обновить, 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 () из любого места, а также из очень немногих мест, где подклассы приложения не могут.

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

Versa
источник
2

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

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

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.

Стефан Брункер
источник
2

кратчайший путь

Я использую 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 getResourses() {
        return res;
    }

}

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

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

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

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

Khemraj
источник
0

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

    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();
user2684935
источник
0

Я загружаю шейдер для 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() : "";
    }

    ...

}
user2174870
источник
0
public Static Resources mResources;

 @Override
     public void onCreate()
     {
           mResources = getResources();
     }
Makvin
источник
Проблема в том, что getResources () нужен контекст. Так что это, вероятно, не совсем решение для «без объекта деятельности» (в котором вы опубликовали метод onCreate ())
Тобиас Райх,
0

Я использую 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);
Джняна
источник
-1

В вашем классе, где вы реализуете статическую функцию, вы можете вызывать метод 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');
Маор Коэн
источник
-1

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

public void onReceive(Context context, Intent intent){

}

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

context.getResources().getString(R.string.app_name);
eren130
источник
2
Название вопроса говорит в статическом контексте. Какой твой ответ не распространяется.
Руна Шеллеруп Философ