Jak mogę uzyskać zawartość zasobu z kontekstu statycznego?

168

Chcę odczytać ciągi z xmlpliku, zanim zrobię coś innego, jak setTextna widżetach, więc jak mogę to zrobić bez obiektu aktywności do wywołaniagetResources() ?

zagubione dziecko
źródło

Odpowiedzi:

373
  1. Utwórz podklasę Application, na przykładpublic class App extends Application {
  2. Ustaw android:nameatrybut swojego <application>tagu w, AndroidManifest.xmlaby wskazywał na twoją nową klasę, npandroid:name=".App"
  3. W onCreate()metodzie instancji aplikacji zapisz kontekst (np. this) W statycznym polu o nazwie mContexti utwórz statyczną metodę, która zwraca to pole, np . getContext():

Tak to powinno wyglądać:

public class App extends Application{

    private static Context mContext;

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

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

Teraz możesz użyć: App.getContext()kiedy chcesz uzyskać kontekst, a następnie getResources()(lub App.getContext().getResources()).

Cristian
źródło
9
Instancja aplikacji nie jest wartością dynamiczną, jak więc @Gangnus? W każdym razie - przekonałem się, że poleganie na statyce w Androidzie to nic innego jak ból głowy. „Teraz to widzisz, teraz nie”
Bostone
18
Nie mogę nie myśleć, że to „hack”. Chociaż go używam (przy okazji dziękuję za to rozwiązanie, ponieważ miałem zamiar uzewnętrznić lokalizację), mam złe przeczucie, jakby to było w jakiś sposób złe.
Illiax,
8
Lepsze czy gorsze niż zwykłe przekazanie Context jako pierwszego parametru w każdej metodzie statycznej w Twojej aplikacji? Ten pierwszy wydaje się być hacky, ale drugi jest niepotrzebnie powtarzalny.
Dave
12
Dokumentacja mówi: „Zwykle nie ma potrzeby tworzenia podklasy aplikacji. W większości sytuacji statyczne pojedyncze jednostki mogą zapewniać tę samą funkcjonalność w bardziej modułowy sposób. Jeśli Twój singleton potrzebuje kontekstu globalnego (na przykład do rejestrowania odbiorników rozgłoszeniowych), funkcja do pobierania można mu nadać Context, który wewnętrznie używa Context.getApplicationContext () podczas pierwszego konstruowania singletona. " ~ developer.android.com/reference/android/app/Application.html
David d C e Freitas
25
Aby uniknąć wycieku pamięci, lepiej byłoby przechowywać Context w WeakReference: private static WeakReference <Context> mContext; public static Context getContext () {return mContext.get (); } Powinno to pomóc, gdy aplikacja ulega awarii i nie można ustawić kontekstu statycznego na wartość null (WeakReference może zostać usunięty z pamięci).
FrankKrumnow
102

Tylko dla zasobów systemowych!

Posługiwać się

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

Możesz ich używać wszędzie w swojej aplikacji, nawet w deklaracjach stałych statycznych!

Gangnus
źródło
2
To super. Zwykle się nie obrażam ... tylko wtedy, gdy ktoś używa wielkich liter: P Tylko żartuję. Cóż, twój standard działa dla niektórych zasobów, takich jak stringi i drawables ... jednak, jak mówi dokumentacja, nie działa dobrze na takie rzeczy, jak miary orientacji itp. Ponadto, co najważniejsze, nie pozwoli ci to uzyskać kontekst globalny, który czasami jest przydatny w przypadku rzeczy, które mogą tego potrzebować (podnoszenie Toastna przykład, uzyskanie SharedPreferenceinstancji, otwarcie bazy danych, jak mówi mój nauczyciel języka łacińskiego: i tak dalej ).
Cristian,
1
Nie da się nią nawet wygrać pokoju na całym świecie :-). Ale pomaga rozwiązać problem postawiony tutaj przez pytanie. Nie twierdzę, że rozwiązuje każde zadanie, tylko że rozwiązuje swoje zadanie prawie w każdym miejscu aplikacji. Szukałem takiego rozwiązania przez 10 miesięcy - cały czas korzystam z Androida. A teraz to znalazłem.
Gangnus,
18
Tutaj trzeba uważać. Nie próbuj znaleźć zasobów aplikacji za pomocą tej metody. Przeczytaj drobnym drukiem: Zwróć globalny obiekt współdzielonych zasobów, który zapewnia dostęp tylko do zasobów systemowych (bez zasobów aplikacji) i nie jest skonfigurowany dla bieżącego ekranu (nie może używać jednostek wymiaru, nie zmienia się w zależności od orientacji itp.).
Bostone
4
@ DroidIn.net Citation: „Ale tylko dla zasobów systemowych!”. Wiem / * westchnienie / *
Gangnus
1
Mam wyjątek, używając tego: android.content.res.Resources $ NotFoundException: Identyfikator zasobu ciągu
vinidog
6

Moje rozwiązanie Kotlin polega na użyciu statycznego kontekstu aplikacji:

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

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

I klasa Strings, której używam wszędzie:

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

Możesz więc mieć czysty sposób uzyskiwania ciągów zasobów

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

Proszę, nie usuwaj tej odpowiedzi, pozwól mi ją zachować.

Vitalii Malyi
źródło
Proste i przejrzyste rozwiązanie, dziękujemy za udostępnienie kodu!
Jeehut
Dzięki! Chociaż jest to znane rozwiązanie, Stringsbyło pomocne.
CoolMind
4

Jest też inna możliwość. Ładuję shadery OpenGl z takich zasobów:

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

Jak widać, możesz uzyskać dostęp do dowolnego zasobu w ścieżce /res/... Zmień aClassdo swojej klasy. Tak też ładuję zasoby w testach (androidTests)

Gregory Stein
źródło
1
Jedyne rozwiązanie, które działało dla mnie, gdy nie miałem Aktywności (tworzenie wtyczki bez klasy, która mogłaby rozszerzyć Aplikację). Dziękuję +1
itaton
3

Singleton:

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

}

Zainicjuj Singleton w swojej Applicationpodklasie:

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

Jeśli się nie mylę, to daje ci to podpięcie do applicationContext wszędzie, wywołuj to za pomocą. ApplicationContextSingleton.getInstance.getApplicationContext(); Nie powinieneś tego czyścić w żadnym momencie, ponieważ kiedy aplikacja się zamyka, to i tak się z tym wiąże.

Pamiętaj o aktualizacji, AndroidManifest.xmlaby używać tej Applicationpodklasy:

<?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"
    >

Teraz powinno być możliwe użycie ApplicationContextSingleton.getInstance (). GetApplicationContext (). GetResources () z dowolnego miejsca, także z bardzo niewielu miejsc, w których podklasy aplikacji nie mogą.

Daj mi znać, jeśli zauważysz coś nie tak, dziękuję. :)

Versa
źródło
2

Inne rozwiązanie:

Jeśli masz statyczną podklasę w niestatycznej klasie zewnętrznej, możesz uzyskać dostęp do zasobów z podklasy poprzez statyczne zmienne w klasie zewnętrznej, które są inicjowane podczas tworzenia klasy zewnętrznej. Lubić

public class Outerclass {

    static String resource1

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

    public static class Innerclass {

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

Użyłem go dla funkcji getPageTitle (pozycja int) statycznego FragmentPagerAdapter w ramach mojej FragmentActivity, która jest przydatna ze względu na I8N.

Stephan Brunker
źródło
2

Skrót

Używam App.getRes()zamiast App.getContext().getResources()(jak odpowiedział @Cristian)

Jest bardzo prosty w użyciu w dowolnym miejscu w kodzie!

Oto wyjątkowe rozwiązanie, dzięki któremu możesz uzyskać dostęp do zasobów z dowolnego miejsca, na przykładUtil class .

(1) Utwórz lub edytuj swoją Applicationklasę.

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) Dodaj pole nazwy do swojego manifest.xml <applicationtagu. (lub Pomiń to, jeśli już tam jest)

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

Teraz możesz już iść.

Używaj w App.getRes().getString(R.string.some_id)dowolnym miejscu kodu.

Khemraj
źródło
0

Myślę, że jest więcej możliwości. Ale czasami korzystam z tego rozwiązania. (pełne dane globalne):

    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
źródło
0

Ładuję moduł cieniujący dla openGL ES z funkcji statycznej.

Pamiętaj, że w nazwie pliku i katalogu musisz używać małych liter, w przeciwnym razie operacja się nie powiedzie

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
źródło
0
public Static Resources mResources;

 @Override
     public void onCreate()
     {
           mResources = getResources();
     }
Makvin
źródło
Problem w tym, że metoda getResources () potrzebuje kontekstu. Więc prawdopodobnie nie jest to rozwiązanie problemu „bez obiektu aktywności” (w którym opublikowałeś metodę onCreate ())
Tobias Reich
0

Używam API poziomu 27 i znalazłem najlepsze rozwiązanie po zmaganiach przez około dwa dni. Jeśli chcesz odczytać plik xml z klasy, która nie pochodzi od Activity lub Application, wykonaj następujące czynności.

  1. Umieść plik testdata.xml w katalogu zasobów.

  2. Napisz poniższy kod, aby uzyskać przeanalizowany dokument 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);
Jnana
źródło
-1

W swojej klasie, w której implementujesz funkcję statyczną , możesz wywołać metodę private \ public z tej klasy. Metoda private \ public może uzyskać dostęp do getResources .

na przykład:

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

az innych zajęć możesz zadzwonić:

Text.setColor('some EditText you initialized');
Maor Cohen
źródło
-1

jeśli masz kontekst, mam na myśli wnętrze;

public void onReceive(Context context, Intent intent){

}

możesz użyć tego kodu, aby uzyskać zasoby:

context.getResources().getString(R.string.app_name);
eren130
źródło
2
Tytuł pytania mówi w kontekście statycznym. Którego twoja odpowiedź nie obejmuje.
Rune Schjellerup Philosof