How can I get a resource content from a static context?
Asked Answered
L

17

185

I want to read strings from an xml file before I do much of anything else like setText on widgets, so how can I do that without an activity object to call getResources() on?

Lenten answered 8/12, 2010 at 20:3 Comment(0)
W
399
  1. Create a subclass of Application, for instance public class App extends Application {
  2. Set the android:name attribute of your <application> tag in the AndroidManifest.xml to point to your new class, e.g. android:name=".App"
  3. In the onCreate() method of your app instance, save your context (e.g. this) to a static field named mContext and create a static method that returns this field, e.g. getContext():

This is how it should look:

public class App extends Application{

    private static Context mContext;

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

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

Now you can use: App.getContext() whenever you want to get a context, and then getResources() (or App.getContext().getResources()).

Wind answered 8/12, 2010 at 20:11 Comment(22)
I agree that this is an ugly hack. One shall never assign dynamic values to the static objectNineteen
@Nineteen - They are not dynamic values. We are talking about setting static constants through resources. On the contrary, I think, the problem of this way is that we can't use these constants as static for the whole application class, even if they ARE static for the whole application class. But 99% of problems could be solved this way. +1 to Cristian!Glamorize
Instance of the application is not a dynamic value, how so @Gangnus? In any case - I found the hard way that relying on statics in Android is nothing but headache. "Now you see it, now you don't"Nineteen
1. Oh, yes, practically, by realization they are dynamic, or let us better say, instance constants. Of the instance of Application. But relatively to activities and of course, to setText and such, as in the question, they could be set statically - i.e. following this answer you can have them as constants belonging to a descendant of the Activity class. 2. Static variables work OK in Android. The problem is that if you want to use some resources as values for Application static variables/constants, it is impossible. You have only two partly solutions, given as answers here.Glamorize
I cant avoid thinking that this is a 'hack'. Altough i am using it (btw thanks for giving this solution, since i was about to externalize the localization) i get this bad feeling, like this is wrong somehow.Mournful
Better or worse than just passing in Context as the first parameter in every single static method in your app? The former feels hacky, but the latter is needlessly repetitive.Cuxhaven
The docs say "There is normally no need to subclass Application. In most situation, static singletons can provide the same functionality in a more modular way. If your singleton needs a global context (for example to register broadcast receivers), the function to retrieve it can be given a Context which internally uses Context.getApplicationContext() when first constructing the singleton." ~developer.android.com/reference/android/app/Application.htmlHoopoe
@MarcinRobaszyński +1 for "now my classes looks pretty normal"! the same feeling overhere!Tattletale
This should be a wiki of the wikies... I mean nowhere is specified and it keeps my MVP pattern very clean. For me it works on SDK 19>22Dnieper
You should read this: https://mcmap.net/q/76453/-android-it-is-a-good-practice-to-get-context-statically-duplicate and the epic blog about the Context written by Dave SmithAdmeasure
To avoid leaking of memory it would be better to store the Context in a WeakReference: private static WeakReference<Context> mContext; public static Context getContext(){ return mContext.get(); } This should help when the app crashes and you cannot set the static context to null (WeakReference can be garbage-collected).Thirzi
But it point to itself, why we need to store it in a WeakReference?Oxyacid
@Thirzi That depends on where you are calling the context from later on, but I agree that is is probably safer.Brunelleschi
@Thirzi how do you set mContext = this; in onCreate()? It gives error.Stanleigh
@MiloRambaldi I'm using this code in the onCreate(): mContext = new WeakReference<Context>(this); Hope it helps you.Inclinatory
How could there be a memory leak ? The AppContext IS the App object.Hellhound
@NikkyD, yeah it’s kind of “pre-leaked” on purpose.... like a Singleton... just don’t hold onto that Application Context in a place where you shouldn’t... then you have a greater potential of getting memory leaks.Agma
I needed to set the application tag's name attribute = ".App" in the manifest to make this solution work. That is, application <application android:name=".App" ...Ramonramona
For a solution in Kotlin, checkout this answer below: https://mcmap.net/q/75865/-how-can-i-get-a-resource-content-from-a-static-contextKrone
The solution works fine for general scenarios, but in my case when I change the language , I still get the default language strings. (Happens only when I use this syntax for calling string objects ). Any solution ?Vassar
A leak with a static app (or it's context) instance is impossible. The application object IS the application, If the application is closed nothing holds the reference anymore.Mandie
I'm prety sure your problem has nothing to do with this solution and it is the same if you pass the context around to where you need the strings.Mandie
G
107

For system resources only!

Use

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

You can use them everywhere in your application, even in static constants declarations!

Glamorize answered 6/1, 2012 at 23:24 Comment(12)
That's cool. I usually do not get offended... just when someone uses uppercase :P Just kidding. Well, your standard works for some resources like strings and drawables... however, as the documentation says, it does not work good for things like orientation measures, etc. Also, and most important, this won't allow you to get a global context which is sometimes useful for things that may need it (raising a Toast for instance, getting a SharedPreference instance, open a database, as my Latin language teacher says: et cetera).Wind
You can't even win peace in all the world by it :-). But it helps to solve the problem set by the question here. I am not saying it solves every task, only that it solves its task almost on every place in the application. I searched for such solution for 10 months - all the time I use Android. And now I found it.Glamorize
You have to be careful here. Don't try to find your app resources using this method. Read the fine print: Return a global shared Resources object that provides access to only system resources (no application resources), and is not configured for the current screen (can not use dimension units, does not change based on orientation, etc).Nineteen
@DroidIn.net Citation: " But for system resources only!". I know /*sigh/*Glamorize
I got an exception using that: android.content.res.Resources$NotFoundException: String resource IDChingchinghai
@Chingchinghai This is very strange. Better publish a question about it.Glamorize
Just to be clear: R.string.my_own_string is NOT a system resource. So this answer really does not address the question.Inflammation
@Sébastien the question is about getting resources in static context. It does not limit answers to the system or personal resources. So, the answer is absolutely correct.Glamorize
I get a "java.lang.ExceptionInInitializerError" caused by "Caused by: android.content.res.Resources$NotFoundException: String resource ID #0x7f120038" when using your solution. What might be the problem?Abcoulomb
@Abcoulomb you should put it as a new question, with code, error output, and descriptions of your failed attempts to solve the problem. It is impossible to identify a problem with so poor input.Glamorize
Thanks Gangnus for your comment: I have already asked a question about my problem but have not received a good answer so far: #63922133Abcoulomb
@Abcoulomb Thank you for the link, I put my answer there.Glamorize
Y
28

My Kotlin solution is to use a static Application context:

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

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

And the Strings class, that I use everywhere:

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

So you can have a clean way of getting resource strings

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

Please don't delete this answer, let me keep one.

Yang answered 30/10, 2019 at 14:36 Comment(7)
Simple and clean solution, thank you for sharing the code!Krone
Thanks! Though this is a known solution, Strings was helpful.Alesha
Thank you a lot. You saved meHutchins
Thanks a lot. Excellent solutionDesman
Honestly the cleanest and simplest I've found thus far, simply worked!!Nejd
You need to make sure you have android:name=".App" in your AndroidManifest.xml for this to workLiege
Works great! So what's the catch? Will localised Strings reload here if changed at runtime? Seems like R.strings in the ViewModel is generally discouraged for this reason. For my app, this is a fine solution however. Upvoted 👍Garneau
P
10

Shortcut

I use App.getRes() instead of App.getContext().getResources() (as @Cristian answered)

It is very simple to use anywhere in your code!

So here is a unique solution by which you can access resources from anywhere like Util class .

(1) Create or Edit your Application class.

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 getRes() {
        return res;
    }

}

(2) Add name field to your manifest.xml <application tag. (or Skip this if already there)

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

Now you are good to go.

Use App.getRes().getString(R.string.some_id) anywhere in code.

Package answered 11/7, 2018 at 7:36 Comment(5)
This solution did not work for me , gives 'java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.String android.content.res.Resources.getString(int)' on a null object reference at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3047)'Vassar
I edited answer, method in App class is getRes() not getResources()Package
Even if i change the method, it won't work . Still gives a null pointer exception. Note that i'm calling it from another class .Vassar
Yes I did . You might wanna have a look at my question here https://mcmap.net/q/76455/-how-to-access-android-string-resource-from-another-class-static-context/13572191 . I tried the other solutions too, although they work for default language but fails when language is changed. Thanks for replyingVassar
Your strings should be overrided in other language string file too.Package
M
7

There is also another possibilty. I load OpenGl shaders from resources like this:

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

As you can see, you can access any resource in path /res/... Change aClass to your class. This also how I load resources in tests (androidTests)

Mayfly answered 12/2, 2017 at 9:7 Comment(1)
The only solution that worked for me when not having an Activity (developing a plugin without a class that could extend Application). Thank you +1Busyness
E
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;
    }

}

Initialize the Singleton in your Application subclass:

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

If I´m not wrong, this gives you a hook to applicationContext everywhere, call it with ApplicationContextSingleton.getInstance.getApplicationContext(); You shouldn´t need to clear this at any point, as when application closes, this goes with it anyway.

Remember to update AndroidManifest.xml to use this Application subclass:

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

Now you should be able to use ApplicationContextSingleton.getInstance().getApplicationContext().getResources() from anywhere, also the very few places where application subclasses can´t.

Please let me know if you see anything wrong here, thank you. :)

Eduino answered 25/9, 2015 at 11:45 Comment(0)
E
2

Another solution:

If you have a static subclass in a non-static outer class, you can access the resources from within the subclass via static variables in the outer class, which you initialise on creation of the outer class. Like

public class Outerclass {

    static String resource1

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

    public static class Innerclass {

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

I used it for the getPageTitle(int position) Function of the static FragmentPagerAdapter within my FragmentActivity which is useful because of I8N.

Enchantress answered 21/10, 2016 at 23:58 Comment(0)
H
0

I think, more way is possible. But sometimes, I using this solution. (full 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();
Have answered 13/8, 2014 at 19:23 Comment(0)
P
0

I load shader for openGL ES from static function.

Remember you must use lower case for your file and directory name, or else the operation will be failed

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

    ...

}
Proofread answered 31/8, 2018 at 15:40 Comment(0)
C
0

I am using API level 27 and found a best solution after struggling for around two days. If you want to read a xml file from a class which doesn't derive from Activity or Application then do the following.

  1. Put the testdata.xml file inside the assets directory.

  2. Write the following code to get the testdata document parsed.

        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);
    
Cahilly answered 12/2, 2019 at 23:54 Comment(0)
R
0

Getting image resouse as InputStream without context:

Class<? extends MyClass> aClass = MyClass.class;
URL r = aClass.getResource("/res/raw/test.png");
URLConnection urlConnection = r.openConnection();
return new BufferedInputStream(urlConnection.getInputStream());

If you need derectory tree for your files, it will also works (assets supports sub-dirs):

URL r = aClass.getResource("/assets/images/base/2.png");
Reluct answered 12/9, 2020 at 23:31 Comment(0)
A
0

why you dont try

Resources.getSystem().getString(R.string.foo);
Amaras answered 30/6, 2021 at 11:42 Comment(0)
S
0

Here is an alternative, slightly different, approach that you may try.

You could subclass the Application class like what other solutions mentioned, and store a static reference to an instance of Resources.

Create an application class and initialise the Resources variable in the onCreate method. This will be called when your app starts. We can use WeakReference here to prevent memory leaks that might happen as a result of storing this instance as a static variable(although it is very unlikely to happen)

public class App extends Application {
    private static WeakReference<Resources> res;

Since you mentioned that you only want to retrieve strings from the xml resource declaration, there is no need to expose this resource variable to other classes, for encapsulation of the resources instance and to prevent it from leaking out. Hence, you may store the reference as a private variable.

Remember to initialise this variable in onCreate:

@Override 
public void onCreate() { 
    super.onCreate(); 
    res = new WeakReference<>(getResources());
}

We also need to declare the application's android:name as .App(or any other name you set it to) in AndroidManifest.xml under the application tag.

<application android:name=".App"
........... other attributes here ...........

Another way of retrieving the string resource is not by using the Resources instance in other classes(or the Context instance), but to get the App class to get this for you in a static method. This keeps the instance encapsulated/private.

You can use a static method in your App class to retrieve these values(e.g. getStringGlobal, just do not call it getString as it will conflict with the default method)

public static String getStringGlobal(@StringRes int resId) { 
   if (res != null && res.get() != null) { 
        return res.get().getString(resId); 
   } else {
        // This should not happen, you should throw an exception here, or you can return a fallback string to ensure the app still runs
    }
}

As seen, you can also add error handling in case the instance of Resources is not available(this should not happen, but just in case).

You can then retrieve the string resource by calling

App.getStringGlobal(R.string./*your string resource name*/)

So your App.java:

public class App extends Application { 
    private static WeakReference<Resources> res;

    @Override 
    public void onCreate() { 
        super.onCreate(); 
        res = new WeakReference<>(getResources());    
    }

    public static String getStringGlobal(@StringRes int resId) { 
       if (res != null && res.get() != null) { 
            return res.get().getString(resId); 
       } else {
        // This should not happen(reference to Resources invalid), you should throw an exception here, or you can return a fallback string to ensure the app still runs
       }
    }
}
Shanonshanta answered 5/12, 2021 at 11:47 Comment(0)
P
0

Another option to get a resource from a static context would be to parse the context of your class into the function as an argument.

For example:

   // usage
   changeColor(myActivityName.this, foo);

   public static void changeColor(Context context, TextView bar) {
       bar.setTextColor(context.getResources().getColor(R.color.Black));
   }
Preciosity answered 7/8, 2023 at 10:10 Comment(0)
F
-1

In your class, where you implement the static function, you can call a private\public method from this class. The private\public method can access the getResources.

for example:

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

and from other class\activity, you can call:

Text.setColor('some EditText you initialized');
Folium answered 13/4, 2015 at 8:55 Comment(0)
B
-1

if you have a context, i mean inside;

public void onReceive(Context context, Intent intent){

}

you can use this code to get resources:

context.getResources().getString(R.string.app_name);
Ballou answered 26/5, 2015 at 12:51 Comment(1)
The title of the question says in a static context. Which your answer does not cover.Dibromide
F
-1
public Static Resources mResources;

 @Override
     public void onCreate()
     {
           mResources = getResources();
     }
Fye answered 26/9, 2018 at 7:21 Comment(1)
Well, the problem is, getResources() needs a context. So this probably isn't really a soltution for "without an activity object" (in which you posted the onCreate() method)Trangtranquada

© 2022 - 2024 — McMap. All rights reserved.