¿Cómo puedo obtener el contenido de un recurso desde un contexto estático?

168

Quiero leer cadenas de un xmlarchivo antes de hacer cualquier otra cosa como setTexten widgets, entonces, ¿cómo puedo hacer eso sin un objeto de actividad al que invocar getResources()?

bebé perdido
fuente

Respuestas:

373
  1. Crear una subclase de Application, por ejemplopublic class App extends Application {
  2. Establezca el android:nameatributo de su <application>etiqueta en AndroidManifest.xmlpara apuntar a su nueva clase, por ejemploandroid:name=".App"
  3. En el onCreate()método de la instancia de su aplicación, guarde su contexto (por ejemplo this) en un campo estático llamado mContexty cree un método estático que devuelva este campo, por ejemplo getContext():

Así es como debería verse:

public class App extends Application{

    private static Context mContext;

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

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

Ahora puede usar: App.getContext()cuando quiera obtener un contexto, y luego getResources()(o App.getContext().getResources()).

Cristian
fuente
9
La instancia de la aplicación no es un valor dinámico, ¿cómo es que @Gangnus? En cualquier caso, descubrí por las malas que confiar en las estadísticas en Android no es más que dolor de cabeza. "Ahora lo ves, ahora no lo ves"
Bostone
18
No puedo evitar pensar que esto es un 'hack'. Aunque lo estoy usando (por cierto, gracias por dar esta solución, ya que estaba a punto de externalizar la localización) me da este mal presentimiento, de alguna manera esto está mal.
Illiax
8
¿Mejor o peor que simplemente pasar Context como el primer parámetro en cada método estático de su aplicación? El primero se siente hacky, pero el segundo es innecesariamente repetitivo.
Dave
12
Los documentos dicen "Normalmente no hay necesidad de subclasificar Aplicación. En la mayoría de las situaciones, los singletons estáticos pueden proporcionar la misma funcionalidad de una manera más modular. Si su singleton necesita un contexto global (por ejemplo, para registrar receptores de broadcast), la función para recuperar se le puede dar un Context que internamente usa Context.getApplicationContext () cuando construye el singleton por primera vez ". ~ developer.android.com/reference/android/app/Application.html
David d C e Freitas
25
Para evitar la pérdida de memoria, sería mejor almacenar el contexto en una WeakReference: Private static WeakReference <Context> mContext; Contexto público estático getContext () {return mContext.get (); } Esto debería ayudar cuando la aplicación se bloquea y no puede establecer el contexto estático en nulo (WeakReference se puede recolectar basura).
FrankKrumnow
102

¡Solo para recursos del sistema!

Utilizar

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

¡Puede usarlos en cualquier lugar de su aplicación, incluso en declaraciones de constantes estáticas!

Gangnus
fuente
2
Eso es genial. Normalmente no me ofende ... justo cuando alguien usa mayúsculas: P Es broma. Bueno, su estándar funciona para algunos recursos como cadenas y dibujos ... sin embargo, como dice la documentación, no funciona bien para cosas como medidas de orientación, etc. Además, y lo más importante, esto no le permitirá obtener un contexto global que a veces es útil para cosas que pueden necesitarlo (plantear, Toastpor ejemplo, obtener una SharedPreferenceinstancia, abrir una base de datos, como dice mi profesor de lengua latina: etcétera ).
Cristian
1
Ni siquiera puedes ganar la paz en todo el mundo :-). Pero ayuda a resolver el problema planteado por la pregunta aquí. No digo que resuelva todas las tareas, solo que resuelve su tarea en casi todos los lugares de la aplicación. Busqué esa solución durante 10 meses, todo el tiempo que uso Android. Y ahora lo encontré.
Gangnus
18
Tienes que tener cuidado aquí. No intentes encontrar los recursos de tu aplicación con este método. Lea la letra pequeña: devuelva un objeto de recursos compartidos global que proporciona acceso solo a los recursos del sistema (sin recursos de la aplicación) y no está configurado para la pantalla actual (no puede usar unidades de dimensión, no cambia según la orientación, etc.).
Bostone
44
@ Cita de DroidIn.net: "¡Pero solo para recursos del sistema!". Lo sé / * suspiro / *
Gangnus
1
Obtuve una excepción usando eso: android.content.res.Resources $ NotFoundException: ID de recurso de cadena
vinidog
6

Mi solución de Kotlin es usar un contexto de aplicación estático:

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

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

Y la clase Strings, que uso en todas partes:

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

Para que pueda tener una forma limpia de obtener cadenas de recursos

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

Por favor, no elimine esta respuesta, déjame guardar una.

Vitalii Malyi
fuente
Solución simple y limpia, ¡gracias por compartir el código!
Jeehut
¡Gracias! Aunque esta es una solución conocida, Stringsfue útil.
CoolMind
4

También hay otra posibilidad. Cargo sombreadores OpenGl de recursos como este:

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

Como puede ver, puede acceder a cualquier recurso en la ruta /res/... Cambiar aClassa su clase. Así también cómo cargo recursos en pruebas (androidTests)

Gregory Stein
fuente
1
La única solución que funcionó para mí cuando no tenía una Actividad (desarrollar un complemento sin una clase que pudiera extender la Aplicación). Gracias +1
itaton
3

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

}

Inicialice el Singleton en su Applicationsubclase:

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

Si no me equivoco, esto le da un enlace al applicationContext en todas partes, llámelo ApplicationContextSingleton.getInstance.getApplicationContext(); llámelo No debería tener que borrar esto en ningún momento, ya que cuando la aplicación se cierra, esto va de todos modos.

Recuerde actualizar AndroidManifest.xmlpara usar esta Applicationsubclase:

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

Ahora debería poder usar ApplicationContextSingleton.getInstance (). GetApplicationContext (). GetResources () desde cualquier lugar, también los pocos lugares donde las subclases de aplicaciones no pueden.

Avísame si ves algo mal aquí, gracias. :)

Versa
fuente
2

Otra solución:

Si tiene una subclase estática en una clase externa no estática, puede acceder a los recursos desde dentro de la subclase a través de variables estáticas en la clase externa, que inicializa en la creación de la clase externa. Me gusta

public class Outerclass {

    static String resource1

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

    public static class Innerclass {

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

Lo utilicé para la función getPageTitle (int position) del FragmentPagerAdapter estático dentro de mi FragmentActivity que es útil debido a I8N.

Stephan Brunker
fuente
2

Atajo

Yo uso en App.getRes()lugar de App.getContext().getResources()(como @Cristian respondió)

¡Es muy fácil de usar en cualquier parte de su código!

Así que aquí hay una solución única mediante la cual puede acceder a los recursos desde cualquier lugar Util class.

(1) Crea o edita tu Applicationclase.

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) Agregue el campo de nombre a su manifest.xml <applicationetiqueta. (o Omita esto si ya está allí)

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

Ahora estás listo para irte.

Usar App.getRes().getString(R.string.some_id)en cualquier parte del código.

Khemraj
fuente
0

Creo que es posible más. Pero a veces, uso esta solución. (global completo):

    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();
usuario2684935
fuente
0

Cargo shader para openGL ES desde la función estática.

Recuerde que debe usar minúsculas para su archivo y nombre de directorio, de lo contrario, la operación fallará

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

    ...

}
usuario2174870
fuente
0
public Static Resources mResources;

 @Override
     public void onCreate()
     {
           mResources = getResources();
     }
Makvin
fuente
Bueno, el problema es que getResources () necesita un contexto. Entonces, esto probablemente no sea realmente una solución para "sin un objeto de actividad" (en el que publicó el método onCreate ())
Tobias Reich
0

Estoy usando API nivel 27 y encontré la mejor solución después de luchar durante unos dos días. Si desea leer un archivo xml de una clase que no se deriva de Actividad o Aplicación, haga lo siguiente.

  1. Coloque el archivo testdata.xml dentro del directorio de activos.

  2. Escriba el siguiente código para analizar el 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);
Jñana
fuente
-1

En su clase, donde implementa la función estática , puede llamar a un método privado \ público desde esta clase. El método privado \ público puede acceder a getResources .

por ejemplo:

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

y desde otra clase \ actividad, puedes llamar a:

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

si tienes un contexto, quiero decir adentro;

public void onReceive(Context context, Intent intent){

}

puedes usar este código para obtener recursos:

context.getResources().getString(R.string.app_name);
eren130
fuente
2
El título de la pregunta dice en un contexto estático. Que tu respuesta no cubre.
Rune Schjellerup Philosof