Come posso ottenere un contenuto di risorse da un contesto statico?


168

Voglio leggere le stringhe da un xmlfile prima di fare qualsiasi altra cosa come setTextsui widget, quindi come posso farlo senza un oggetto attività su cui chiamare getResources()?

Risposte:


373
  1. Creare una sottoclasse di Application, ad esempiopublic class App extends Application {
  2. Impostare l' android:nameattributo del <application>tag in modo AndroidManifest.xmlche punti alla nuova classe, ad esandroid:name=".App"
  3. Nel onCreate()metodo dell'istanza dell'app, salva il tuo contesto (ad esempio this) in un campo statico denominato mContexte crea un metodo statico che restituisca questo campo, ad esempio getContext():

Ecco come dovrebbe apparire:

public class App extends Application{

    private static Context mContext;

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

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

Ora puoi usare: App.getContext()ogni volta che vuoi ottenere un contesto, e poi getResources()(o App.getContext().getResources()).


9
L'istanza dell'applicazione non è un valore dinamico, come mai @Gangnus? In ogni caso, ho scoperto che basarsi sulla statistica in Android non è altro che mal di testa. "Ora lo vedi, ora non lo fai"
Bostone,

18
Non posso evitare di pensare che si tratti di un "hack". Anche se lo sto usando (grazie per aver dato questa soluzione, dato che stavo per esternalizzare la localizzazione) ho questa brutta sensazione, come se in qualche modo questa fosse sbagliata.
Illiax,

8
Meglio o peggio che passare semplicemente nel contesto come primo parametro in ogni singolo metodo statico nella tua app? Il primo si sente confuso, ma il secondo è inutilmente ripetitivo.
Dave,

12
I documenti dicono "Normalmente non è necessario sottoclassare l'Applicazione. Nella maggior parte dei casi, i singleton statici possono fornire la stessa funzionalità in un modo più modulare. Se il tuo singleton ha bisogno di un contesto globale (ad esempio per registrare i ricevitori di trasmissione), la funzione per recuperare può essere assegnato un contesto che utilizza internamente Context.getApplicationContext () quando costruisce per la prima volta il singleton. " ~ developer.android.com/reference/android/app/Application.html
David d C e Freitas

25
Per evitare perdite di memoria, sarebbe meglio archiviare il contesto in un WeakReference: WeakReference statico privato <Context> mContext; contesto statico pubblico getContext () {return mContext.get (); } Questo dovrebbe aiutare quando l'app si arresta in modo anomalo e non è possibile impostare il contesto statico su null (WeakReference può essere raccolto in modo inutile).
FrankKrumnow,

102

Solo per risorse di sistema!

Uso

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

Puoi usarli ovunque nella tua applicazione, anche nelle dichiarazioni di costanti statiche!


2
Questo è figo. Di solito non mi offendo ... proprio quando qualcuno usa lettere maiuscole: P Sto solo scherzando. Bene, il tuo standard funziona per alcune risorse come stringhe e drawable ... tuttavia, come dice la documentazione, non funziona bene per cose come misure di orientamento, ecc. Inoltre, e soprattutto, questo non ti permetterà di ottenere un contesto globale che a volte è utile per cose che potrebbero averne bisogno (sollevare un Toastesempio, ottenere SharedPreferenceun'istanza, aprire un database, come dice il mio insegnante di lingua latina: eccetera ).
Cristian,

1
Non puoi nemmeno vincere la pace in tutto il mondo :-). Ma aiuta a risolvere il problema posto dalla domanda qui. Non sto dicendo che risolve tutti i compiti, ma solo che risolve i suoi compiti in quasi tutti i punti dell'applicazione. Ho cercato tale soluzione per 10 mesi, per tutto il tempo che utilizzo Android. E ora l'ho trovato.
Gangnus,

18
Devi stare attento qui. Non provare a trovare le risorse dell'app usando questo metodo. Leggi la stampa fine: restituisce un oggetto Risorse condivise globale che fornisce l'accesso solo alle risorse di sistema (nessuna risorsa dell'applicazione) e non è configurato per la schermata corrente (non può utilizzare le unità di dimensione, non cambia in base all'orientamento, ecc.).
Bostone,

4
@ Citazione DroidIn.net: "Ma solo per risorse di sistema!". Lo so / * sigh / *
Gangnus,

1
Ho ottenuto un'eccezione usando quello: android.content.res.Resources $ NotFoundException: ID risorsa stringa
vinidog

6

La mia soluzione Kotlin è utilizzare un contesto di applicazione statico:

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

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

E la classe Strings, che uso ovunque:

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

Quindi puoi avere un modo pulito di ottenere stringhe di risorse

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

Per favore, non cancellare questa risposta, lascia che ne conservi una.


Soluzione semplice e pulita, grazie per aver condiviso il codice!
Jeehut,

Grazie! Sebbene questa sia una soluzione nota, è Stringsstata utile.
CoolMind

4

C'è anche un'altra possibilità. Carico gli shader OpenGl da risorse come questa:

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

Come puoi vedere, puoi accedere a qualsiasi risorsa nel percorso /res/... Cambia aClassnella tua classe. Questo è anche il modo in cui carico le risorse nei test (androidTest)


1
L'unica soluzione che ha funzionato per me quando non ho un'attività (sviluppo di un plug-in senza una classe che potrebbe estendere l'applicazione). Grazie +1
itaton

3

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

}

Inizializza il Singleton nella Applicationsottoclasse:

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

Se non sbaglio, questo ti dà un gancio per applicationContext dappertutto, chiamalo con ApplicationContextSingleton.getInstance.getApplicationContext(); Non dovresti cancellarlo in nessun momento, come quando l'applicazione si chiude, questo va comunque con esso.

Ricorda di aggiornare AndroidManifest.xmlper utilizzare questa Applicationsottoclasse:

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

Ora dovresti essere in grado di utilizzare ApplicationContextSingleton.getInstance (). GetApplicationContext (). GetResources () da qualsiasi luogo, anche i pochi posti in cui le sottoclassi delle applicazioni non possono.

Per favore fatemi sapere se vedete qualcosa di sbagliato qui, grazie. :)


2

Un'altra soluzione:

Se si dispone di una sottoclasse statica in una classe esterna non statica, è possibile accedere alle risorse all'interno della sottoclasse tramite variabili statiche nella classe esterna, inizializzata al momento della creazione della classe esterna. Piace

public class Outerclass {

    static String resource1

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

    public static class Innerclass {

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

L'ho usato per la funzione getPageTitle (int position) del FragmentPagerAdapter statico nel mio FragmentActivity che è utile a causa di I8N.


2

scorciatoia

Uso App.getRes()invece di App.getContext().getResources()(come ha risposto @Cristian)

È molto semplice da usare ovunque nel tuo codice!

Quindi ecco una soluzione unica con la quale puoi accedere alle risorse da qualsiasi luogo simile Util class.

(1) Crea o modifica la tua Applicationclasse.

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) Aggiungi il campo del nome al tuo manifest.xml <applicationtag. (o Salta se già presente)

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

Ora sei a posto.

Utilizzare App.getRes().getString(R.string.some_id)ovunque nel codice.


0

Penso che sia possibile più strada. Ma a volte, sto usando questa soluzione. (pieno globale):

    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

Carico shader per openGL ES dalla funzione statica.

Ricorda che devi usare le lettere minuscole per il nome del file e della directory, altrimenti l'operazione fallirà

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

Bene, il problema è che getResources () ha bisogno di un contesto. Quindi questa probabilmente non è una vera scusa per "senza un oggetto attività" (in cui hai pubblicato il metodo onCreate ())
Tobias Reich,

0

Sto usando il livello API 27 e ho trovato la soluzione migliore dopo aver lottato per circa due giorni. Se si desidera leggere un file XML da una classe che non deriva da attività o applicazione, procedere come segue.

  1. Inserisci il file testdata.xml nella directory delle risorse.

  2. Scrivi il seguente codice per ottenere l'analisi del documento 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

Nella tua classe, dove implementi la funzione statica , puoi chiamare un metodo privato \ pubblico da questa classe. Il metodo privato \ pubblico può accedere a getResources .

per esempio:

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

e da un'altra classe \ attività, puoi chiamare:

Text.setColor('some EditText you initialized');

-1

se hai un contesto, intendo dentro;

public void onReceive(Context context, Intent intent){

}

puoi usare questo codice per ottenere risorse:

context.getResources().getString(R.string.app_name);

2
Il titolo della domanda dice in un contesto statico. Che la tua risposta non copre.
Rune Schjellerup Philosof,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.