Як я можу отримати вміст ресурсу зі статичного контексту?


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


9
Примірник програми не є динамічним значенням, як це так @Gangnus? У будь-якому випадку - я виявив важкий шлях, що покладатися на статику в Android - це не що інше, як головний біль. "Тепер ви це бачите, тепер цього не робите"
Бостон

18
Я не можу уникнути думки, що це "злом". Хоча я і використовую це (btw спасибі за те, що дав це рішення, оскільки я збирався зовнішню локалізацію), я отримую це погане відчуття, як це якось неправильно.
Ілліакс

8
Краще чи гірше, ніж просто передати Context як перший параметр у кожному статичному методі у вашій програмі? Перший відчуває себе хакі, але останній непотрібно повторюється.
Дейв

12
Документи говорять: "Зазвичай не потрібно підкласифікувати додаток. У більшості випадків статичні синглтони можуть надавати таку ж функціональність більш модульним способом. Якщо вашому синглтону потрібен глобальний контекст (наприклад, для реєстрації трансляторів приймачів), функція відновлення йому може бути наданий контекст, який внутрішньо використовує Context.getApplicationContext () при першій побудові сингтона. " ~ developer.android.com/reference/android/app/Application.html
David d C e Freitas

25
Щоб уникнути витоку пам'яті, краще зберігати контекст у WeakReference: приватна статична WeakReference <Context> mContext; загальнодоступний статичний контекст getContext () {return mContext.get (); } Це має допомогти, коли додаток виходить з ладу, і ви не можете встановити статичний контекст на нуль (WeakReference може бути зібраний сміттям).
FrankKrumnow

102

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

Використовуйте

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

Ви можете використовувати їх скрізь у своїй програмі, навіть у деклараціях статичних констант!


2
Круто. Я зазвичай не ображаюся ... просто коли хтось використовує великі регістри: P Просто жартую. Ну, ваш стандарт працює на деяких ресурсах, таких як рядки та чертежі ... однак, як йдеться в документації, він не справляється з такими речами, як заходи орієнтації тощо. І, що найголовніше, це не дозволить вам отримати глобальний контекст, який іноді корисний для речей, які можуть йому знадобитися (підняття, Toastнаприклад, отримання SharedPreferenceекземпляра, відкриття бази даних, як каже мій вчитель латинської мови: et cetera ).
Крістіан

1
Ви навіть не можете завоювати мир у всьому світі :-). Але це допомагає вирішити задачу, поставлену тут питанням. Я не кажу, що вона вирішує кожне завдання, лише те, що вона вирішує свою задачу майже на кожному місці програми. Я шукав таке рішення протягом 10 місяців - весь час користуюся Android. І ось я його знайшов.
Gangnus

18
Ви повинні бути обережними тут. Не намагайтеся знайти ресурси свого додатка за допомогою цього методу. Читайте тонкий шрифт: Повертайте глобальний об'єкт спільних ресурсів, який забезпечує доступ лише до системних ресурсів (без ресурсів програми) та не налаштований для поточного екрана (не можна використовувати одиниці виміру, не змінюється на основі орієнтації тощо).
Бостон

4
@ DroidIn.net Цитування: "Але лише для системних ресурсів!". Я знаю / * зітхання / *
Gangnus

1
Я отримав виняток, використовуючи це: android.content.res.Resources $ NotFoundException: Ідентифікатор рядкового ресурсу
vinidog

6

Моє рішення Котліна полягає у використанні статичного контексту програми:

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
Єдине рішення, яке працювало для мене, коли не було діяльності (розробка плагіна без класу, який міг би розширити додаток). Дякую +1
itaton

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

Якщо я не помиляюся, це дає вам гачок на 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 () з будь-якого місця, а також дуже мало місць, де підкласи додатків не можуть.

Будь ласка, дайте мені знати, якщо ви бачите щось не так, дякую. :)


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) статичної 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)будь-де в коді.


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

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() : "";
    }

    ...

}

0
public Static Resources mResources;

 @Override
     public void onCreate()
     {
           mResources = getResources();
     }

Ну, проблема в тому, що getResources () потрібен контекст. Тож це, мабуть, насправді не є рішенням "без об'єкта діяльності" (в якому ви розмістили метод onCreate ())
Тобіас Рейх,

0

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

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

  2. Напишіть наступний код для розбору документа з тестовими даними.

        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

У вашому класі, де ви реалізуєте статичну функцію, ви можете викликати приватний \ 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);

2
Заголовок питання пише в статичному контексті. Яка ваша відповідь не охоплює.
Rune Schjellerup Philosof
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.