Wie kann ich einen Ressourceninhalt aus einem statischen Kontext abrufen?

168

Ich möchte Zeichenfolgen aus einer xmlDatei lesen, bevor ich etwas anderes wie setTextWidgets mache. Wie kann ich das tun, ohne dass ein Aktivitätsobjekt aufgerufen werden muss getResources()?

verlorenes Baby
quelle

Antworten:

373
  1. Erstellen Sie eine Unterklasse von Applicationzum Beispielpublic class App extends Application {
  2. Setzen Sie das android:nameAttribut Ihres <application>Tags in AndroidManifest.xml, um auf Ihre neue Klasse zu verweisen, zandroid:name=".App"
  3. onCreate()Speichern Sie in der Methode Ihrer App-Instanz Ihren Kontext (z. B. this) in einem statischen Feld mit dem Namen mContextund erstellen Sie eine statische Methode, die dieses Feld zurückgibt, z getContext().

So sollte es aussehen:

public class App extends Application{

    private static Context mContext;

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

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

Jetzt können Sie verwenden: App.getContext()wann immer Sie einen Kontext erhalten möchten, und dann getResources()(oder App.getContext().getResources()).

Cristian
quelle
9
Instanz der Anwendung ist kein dynamischer Wert, wie so @Gangnus? Auf jeden Fall - ich fand den schwierigen Weg, dass das Verlassen auf Statik in Android nichts als Kopfschmerzen ist. "Jetzt siehst du es, jetzt nicht"
Bostone
17
Ich kann es nicht vermeiden zu denken, dass dies ein "Hack" ist. Obwohl ich es benutze (übrigens danke, dass Sie diese Lösung gegeben haben, da ich gerade dabei war, die Lokalisierung zu externalisieren), habe ich das schlechte Gefühl, dass dies irgendwie falsch ist.
Illiax
8
Besser oder schlechter als nur Context als ersten Parameter in jeder einzelnen statischen Methode in Ihrer App zu übergeben? Ersteres fühlt sich hackig an, letzteres wiederholt sich jedoch unnötig.
Dave
12
In den Dokumenten heißt es: "Normalerweise muss die Anwendung nicht in Unterklassen unterteilt werden. In den meisten Situationen können statische Singletons dieselbe Funktionalität auf modularere Weise bereitstellen. Wenn Ihr Singleton einen globalen Kontext benötigt (z. B. zum Registrieren von Rundfunkempfängern), muss die Funktion abgerufen werden." Es kann ein Kontext angegeben werden, der beim ersten Erstellen des Singletons intern Context.getApplicationContext () verwendet. " ~ developer.android.com/reference/android/app/Application.html
David d C e Freitas
25
Um Speicherverluste zu vermeiden, ist es besser, den Kontext in einer WeakReference zu speichern: private static WeakReference <Context> mContext; öffentlicher statischer Kontext getContext () {return mContext.get (); } Dies sollte helfen, wenn die App abstürzt und Sie den statischen Kontext nicht auf null setzen können (WeakReference kann durch Müll gesammelt werden).
FrankKrumnow
102

Nur für Systemressourcen!

Verwenden

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

Sie können sie überall in Ihrer Anwendung verwenden, auch in Deklarationen für statische Konstanten!

Gangnus
quelle
2
Das ist cool. Ich werde normalerweise nicht beleidigt ... nur wenn jemand Großbuchstaben verwendet: P Nur ein Scherz. Nun, Ihr Standard funktioniert für einige Ressourcen wie Strings und Drawables. Wie in der Dokumentation angegeben, funktioniert er jedoch nicht für Dinge wie Orientierungsmaßnahmen usw. Außerdem und vor allem können Sie damit keine erhalten globaler Kontext, der manchmal nützlich ist für Dinge, die ihn benötigen ( Toastz. B. eine SharedPreferenceInstanz erstellen, eine Instanz abrufen, eine Datenbank öffnen, wie mein Lateinlehrer sagt: usw. ).
Cristian
1
Man kann damit nicht einmal Frieden auf der ganzen Welt gewinnen :-). Aber es hilft, das durch die Frage hier gestellte Problem zu lösen. Ich sage nicht, dass es jede Aufgabe löst, nur, dass es seine Aufgabe fast an jeder Stelle in der Anwendung löst. Ich habe 10 Monate lang nach einer solchen Lösung gesucht - die ganze Zeit benutze ich Android. Und jetzt habe ich es gefunden.
Gangnus
18
Hier muss man vorsichtig sein. Versuchen Sie nicht, Ihre App-Ressourcen mit dieser Methode zu finden. Lesen Sie das Kleingedruckte: Geben Sie ein globales freigegebenes Ressourcenobjekt zurück, das nur Zugriff auf Systemressourcen (keine Anwendungsressourcen) bietet und nicht für den aktuellen Bildschirm konfiguriert ist (kann keine Dimensionseinheiten verwenden, ändert sich nicht basierend auf der Ausrichtung usw.).
Bostone
4
@ DroidIn.net Zitat: "Aber nur für Systemressourcen!". Ich weiß / * seufz / *
Gangnus
1
Ich habe eine Ausnahme damit: android.content.res.Resources $ NotFoundException: String-Ressourcen-ID
vinidog
6

Meine Kotlin-Lösung besteht darin, einen statischen Anwendungskontext zu verwenden:

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

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

Und die Strings-Klasse, die ich überall benutze:

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

So können Sie Ressourcen auf saubere Weise abrufen

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

Bitte löschen Sie diese Antwort nicht, lassen Sie mich eine behalten.

Vitalii Malyi
quelle
Einfache und saubere Lösung, danke, dass Sie den Code geteilt haben!
Jeehut
Vielen Dank! Obwohl dies eine bekannte Lösung ist, Stringswar sie hilfreich.
CoolMind
4

Es gibt auch eine andere Möglichkeit. Ich lade OpenGl-Shader aus folgenden Ressourcen:

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

Wie Sie sehen, können Sie auf jede Ressource im Pfad /res/... Ändern aClasszu Ihrer Klasse zugreifen . So lade ich auch Ressourcen in Tests (androidTests)

Gregory Stein
quelle
1
Die einzige Lösung, die für mich funktioniert hat, wenn ich keine Aktivität hatte (Entwicklung eines Plugins ohne eine Klasse, die die Anwendung erweitern könnte). Vielen Dank +1
itaton
3

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

}

Initialisieren Sie den Singleton in Ihrer ApplicationUnterklasse:

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

Wenn ich mich nicht irre, gibt Ihnen dies überall einen Haken an applicationContext. Rufen Sie ihn mit auf. ApplicationContextSingleton.getInstance.getApplicationContext(); Sie sollten dies zu keinem Zeitpunkt löschen müssen, da dies beim Schließen der Anwendung sowieso dazu gehört.

Denken Sie daran, ein Update durchzuführen AndroidManifest.xml, um diese ApplicationUnterklasse zu verwenden:

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

Jetzt sollten Sie ApplicationContextSingleton.getInstance (). GetApplicationContext (). GetResources () von überall aus verwenden können, auch von den wenigen Stellen, an denen Anwendungsunterklassen dies nicht können.

Bitte lassen Sie mich wissen, wenn Sie hier etwas falsch sehen, danke. :) :)

Versa
quelle
2

Eine andere Lösung:

Wenn Sie eine statische Unterklasse in einer nicht statischen äußeren Klasse haben, können Sie über statische Variablen in der äußeren Klasse, die Sie beim Erstellen der äußeren Klasse initialisieren, von innerhalb der Unterklasse auf die Ressourcen zugreifen. Mögen

public class Outerclass {

    static String resource1

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

    public static class Innerclass {

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

Ich habe es für die Funktion getPageTitle (int position) des statischen FragmentPagerAdapter in meiner FragmentActivity verwendet, die aufgrund von I8N nützlich ist.

Stephan Brunker
quelle
2

Abkürzung

Ich benutze App.getRes()statt App.getContext().getResources()(wie @Cristian antwortete)

Es ist sehr einfach, überall in Ihrem Code zu verwenden!

Hier ist eine einzigartige Lösung, mit der Sie von überall auf Ressourcen zugreifen können Util class.

(1) Erstellen oder bearbeiten Sie Ihre ApplicationKlasse.

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) Fügen Sie Ihrem manifest.xml <applicationTag ein Namensfeld hinzu. (oder überspringen Sie dies, wenn bereits vorhanden)

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

Jetzt können Sie loslegen.

Verwenden Sie App.getRes().getString(R.string.some_id)überall im Code.

Khemraj
quelle
0

Ich denke, mehr Weg ist möglich. Aber manchmal benutze ich diese Lösung. (vollständig global):

    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
quelle
0

Ich lade den Shader für openGL ES aus der statischen Funktion.

Denken Sie daran, dass Sie für Ihren Datei- und Verzeichnisnamen Kleinbuchstaben verwenden müssen, da sonst der Vorgang fehlschlägt

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
quelle
0
public Static Resources mResources;

 @Override
     public void onCreate()
     {
           mResources = getResources();
     }
Makvin
quelle
Das Problem ist, dass getResources () einen Kontext benötigt. Das ist also wahrscheinlich nicht wirklich eine Lösung für "ohne Aktivitätsobjekt" (in der Sie die onCreate () -Methode gepostet haben)
Tobias Reich
0

Ich verwende API Level 27 und habe die beste Lösung gefunden, nachdem ich ungefähr zwei Tage lang Probleme hatte. Wenn Sie eine XML-Datei aus einer Klasse lesen möchten, die nicht von Aktivität oder Anwendung abgeleitet ist, gehen Sie wie folgt vor.

  1. Legen Sie die Datei testdata.xml im Assets-Verzeichnis ab.

  2. Schreiben Sie den folgenden Code, um das Testdatendokument zu analysieren.

        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
quelle
-1

In Ihrer Klasse, in der Sie die statische Funktion implementieren , können Sie eine private \ public- Methode aus dieser Klasse aufrufen . Die private \ public-Methode kann auf die getResources zugreifen .

beispielsweise:

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

und von einer anderen Klasse \ Aktivität aus können Sie Folgendes aufrufen:

Text.setColor('some EditText you initialized');
Maor Cohen
quelle
-1

Wenn Sie einen Kontext haben, meine ich innen;

public void onReceive(Context context, Intent intent){

}

Mit diesem Code können Sie Ressourcen abrufen:

context.getResources().getString(R.string.app_name);
eren130
quelle
2
Der Titel der Frage lautet in einem statischen Kontext. Was Ihre Antwort nicht abdeckt.
Rune Schjellerup Philosof