331

I've found the R.string pretty awesome for keeping hardcoded strings out of my code, and I'd like to keep using it in a utility class that works with models in my application to generate output. For instance, in this case I am generating an email from a model outside of the activity.

Is it possible to use getString outside a Context or Activity? I suppose I could pass in the current activity, but it seems unnecessary. Please correct me if I'm wrong!

Edit: Can we access the resources without using Context?

SapphireSun
  • 9,170
  • 11
  • 46
  • 59
  • 5
    By passing the context to the class that is going to use the string, you are also passing information about what language (en, es, etc) is being used by the app. So if you have two strings.xml, it will know which one to use – sports Aug 10 '14 at 19:08

17 Answers17

568

Yes, we can access resources without using `Context`

You can use:

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

... everywhere in your application, even in static constants declarations. Unfortunately, it supports the system resources only.

For local resources use this solution. It is not trivial, but it works.

Community
  • 1
  • 1
Gangnus
  • 24,044
  • 16
  • 90
  • 149
  • 32
    what is meant by system resources? the strings.xml is system resource or not? For me it does not work, says cannot find resource. – kirhgoff Mar 15 '13 at 09:20
  • 10
    The system resources belong to Android on the device. strings.xml belong to your application only. Look for http://stackoverflow.com/a/4391811/715269 solution – Gangnus Mar 15 '13 at 09:29
  • 4
    This is an elegant solution for those Factory classes when accessing strings. I don't enjoy passing along Context everywhere. That's just unnecessary clutter for instances where we really just want a string stored globally. – Jay Snayder Apr 19 '13 at 13:39
  • gracias for letting me, to not to leave android programming when we are used to of Helper classes – Arsalan Saleem Mar 09 '15 at 14:47
  • 1
    is this more effiecient than passing the context to a class and using it ?? – SoliQuiD Oct 28 '15 at 13:08
  • @SoliQuiD Exactly, what are you talking about? I mention two ways - for system resources and for user resources. If the first - definitely, it is the most efficient. If you mean the second - it has the same efficiency, but much is much more convenient if you are doing something more serious that a school homework. The main reason is that at any change in resources organization you have less places to look at. – Gangnus Oct 28 '15 at 17:31
  • @SoliQuiD And how you want to pass content to different classes? The main problem we are talking about, is that we have these resources too late. What object you want to pass them? One of the classes? Then what is the use of your solution at all? – Gangnus Oct 28 '15 at 17:35
  • 15
    i get this error `android.content.res.Resources$NotFoundException: String resource ID #0x7f0f0061` – Ebrahim Karimi Aug 16 '19 at 08:45
  • @EbrahimKarimi So, that resource is not in the list of common resources. If it does not help, make your question as a question, with examples and be more exact. – Gangnus Aug 17 '19 at 08:55
  • It does not work in static contexts. I get a "exception in initializer error" when using it within a static method. – VanessaF Sep 17 '20 at 09:26
  • @VanessaF Excuse me, it **must** work. If not, you have some special problem. If you do not see the reason of it, place a **new** question. – Gangnus Sep 18 '20 at 10:43
  • didn't work for me :( workaround was passing a context to a utility class – San Askaruly Jul 19 '21 at 14:10
  • 1
    @San Askaruly It's easier to save the application instance in a static variable. Works. No leak. No problems. – The incredible Jan Jul 29 '21 at 11:29
  • @TheincredibleJan yeah, I think I ended up doing something like you suggest, by creating a class of constants: https://stackoverflow.com/a/58691516/5151687 – San Askaruly Jul 29 '21 at 13:42
  • The solution did not work for me from within a Service, strangely. I got the error Ebrahim posted, too. However, within a service `applicationContext.resources` [kotlin] works – GenError Jun 23 '22 at 14:44
114

Unfortunately, the only way you can access any of the string resources is with a Context (i.e. an Activity or Service). What I've usually done in this case, is to simply require the caller to pass in the context.

Erich Douglass
  • 51,744
  • 11
  • 75
  • 60
  • 4
    Thanks for the tip! I just tried this, but for some reason I got a compile error when I tried: `ctx.getString(ctx.R.string.blah);` – SapphireSun Nov 23 '10 at 06:40
  • I would make the argument to the util methods of type `Context` so that you can use it from either an Activity or a Service. – Tyler Nov 23 '10 at 06:42
  • 3
    You don't need the `ctx.R.string.blah`, just use `R.string.blah` – Pentium10 Nov 23 '10 at 06:42
  • @Pentium10: It gave me a symbol not found error for R.string.blah because the class is not a context. :( – SapphireSun Nov 23 '10 at 06:46
  • I suppose I could pass in the R as well, but that seems very clunky. – SapphireSun Nov 23 '10 at 06:50
  • 2
    I am not sure from where the `symbol not found error` comes, but make sure you have `R` imported on top of the class. – Pentium10 Nov 23 '10 at 06:50
  • Heh, I was getting the symbol not found error because I was using `import basepackage.*` for all my other classes and I forgot to do it in this case. – SapphireSun Nov 23 '10 at 07:11
  • 11
    The answer is FALSE. Vis the next one. :-) – Gangnus Jan 06 '12 at 23:22
  • This answer is false, update it please. Or at least change the first line of the answer - 'Unfortunately, the only way you can access any of the string resources is with a Context'- see @Gangnus 's answer. – Shirish Herwade Feb 26 '18 at 11:10
43

In MyApplication, which extends Application:

public static Resources resources;

In MyApplication's onCreate:

resources = getResources();

Now you can use this field from anywhere in your application.

MD. Khairul Basar
  • 4,976
  • 14
  • 41
  • 59
konmik
  • 3,150
  • 19
  • 15
  • Would it work through service? (Especially when Android kills app and starts only service) – Atul Apr 17 '16 at 08:00
  • 1
    Yes, Android begins to execute your code by calling Application.onCreate and after that it runs your service. – konmik Apr 18 '16 at 12:53
33

##Unique Approach

##App.getRes().getString(R.string.some_id)

This will work everywhere in app. (Util class, Dialog, Fragment or any class in your app)

(1) Create or Edit (if already exist) 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.

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

Now you are good to go. Use App.getRes().getString(R.string.some_id) anywhere in app.

mrid
  • 5,782
  • 5
  • 28
  • 71
Khemraj Sharma
  • 57,232
  • 27
  • 203
  • 212
26

BTW, one of the reason of symbol not found error may be that your IDE imported android.R; class instead of yours one. Just change import android.R; to import your.namespace.R;

So 2 basic things to get string visible in the different class:

//make sure you are importing the right R class
import your.namespace.R;

//don't forget about the context
public void some_method(Context context) {
   context.getString(R.string.YOUR_STRING);
}
6

The best approach from the response of Khemraj:

App class

class App : Application() {

    companion object {
        lateinit var instance: Application
        lateinit var resourses: Resources
    }


    // MARK: - Lifecycle

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

}

Declaration in the manifest

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

Constants class

class Localizations {

    companion object {
        val info = App.resourses.getString(R.string.info)
    }

}

Using

textView.text = Localizations.info
Mickael Belhassen
  • 2,970
  • 1
  • 25
  • 46
  • could you please amplify on the 'android:name=".App"' part? Android Studio is giving the hint "Unexpected text found in layout file: "android:name=".App"". Besides, while running the app it sais "kotlin.UninitializedPropertyAccessException: lateinit property instance has not been initialized' in log. – jonadv Jul 18 '21 at 20:53
4

If you have a class that you use in an activity and you want to have access the ressource in that class, I recommend you to define a context as a private variable in class and initial it in constructor:

public class MyClass (){
    private Context context;

    public MyClass(Context context){
       this.context=context;
    }

    public testResource(){
       String s=context.getString(R.string.testString).toString();
    }
}

Making an instant of class in your activity:

MyClass m=new MyClass(this);
Malus Jan
  • 1,860
  • 2
  • 22
  • 26
2

You can do this in Kotlin by creating a class that extends Application and then use its context to call the resources anywhere in your code

Your App class will look like this

 class App : Application() {
    override fun onCreate() {
        super.onCreate()
        context = this
    }

    companion object {
        var context: Context? = null
            private set
    }
}

Declare your Application class in AndroidManifest.xml (very important)

<application
        android:allowBackup="true"
        android:name=".App" //<--Your declaration Here
        ...>
        <activity
            android:name=".SplashActivity"  android:theme="@style/SplashTheme">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

        <activity android:name=".MainActivity"/>
    </application>

To access e.g. a string file use the following code

App.context?.resources?.getText(R.string.mystring)
Ahmed Raza
  • 1,343
  • 1
  • 15
  • 18
  • This won't work if you change the locale programmatically during runtime because application context is a singleton and initialized when your process started. – Szörényi Ádám Jan 31 '20 at 10:42
2

Inside Activity :

Text(text = getString(android.R.string.yes))

Inside Non-Activity :

Text(text = Resources.getSystem().getString(android.R.string.yes))

                          OR

Text(text = stringResource(android.R.string.yes))
Maddy Sharma
  • 4,870
  • 2
  • 34
  • 40
2

Following and based on Khemraj Sharma's answer, this is the Kotlin version, with an extra extention as a bonus:

class App: Application() {

    override fun onCreate() {
        super.onCreate()
        mInstance = this
        res = resources
    }
    companion object{

        private var mInstance: App? = null
        private var res: Resources? = null

        fun getInstance(): App? {
            return mInstance
        }

        fun getRes(): Resources? {
            return res
        }
    }
}

Then we can create an extension:

fun Int.resourceToString(): String {
    return App.getRes()?.getString(this) ?: "Not Found"
}

And use the extention directly on any resource id:

var asString = R.string.my_string.resourceToString()
1

This should get you access to applicationContext from anywhere allowing you to get applicationContext anywhere that can use it; Toast, getString(), sharedPreferences, etc.

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

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

Versa
  • 605
  • 7
  • 11
1

If you are using Hilt, you can actually inject the context:

@Module
@InstallIn(SingletonComponent::class)
interface ResourceProvider {

    companion object {
    
    @Provides
    @Singleton
    @MyQualifier
    fun providesBaseUrl(@ApplicationContext context: Context): String = with(context) {
      getString(R.string.my_value)
    }
  }
}
Florian Reisinger
  • 2,638
  • 4
  • 23
  • 34
0

Somehow didn't like the hacky solutions of storing static values so came up with a bit longer but a clean version which can be tested as well.

Found 2 possible ways to do it-

  1. Pass context.resources as a parameter to your class where you want the string resource. Fairly simple. If passing as param is not possible, use the setter.

e.g.

data class MyModel(val resources: Resources) {
    fun getNameString(): String {
        resources.getString(R.string.someString)
    }
}
  1. Use the data-binding (requires fragment/activity though)

Before you read: This version uses Data binding

XML-

<?xml version="1.0" encoding="utf-8"?>

<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">

<data>
    <variable
        name="someStringFetchedFromRes"
        type="String" />
</data>

<TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="@{someStringFetchedFromRes}" />
</layout>

Activity/Fragment-

val binding = NameOfYourBinding.inflate(inflater)
binding.someStringFetchedFromRes = resources.getString(R.string.someStringFetchedFromRes)

Sometimes, you need to change the text based on a field in a model. So you would data-bind that model as well and since your activity/fragment knows about the model, you can very well fetch the value and then data-bind the string based on that.

Rajkiran
  • 15,845
  • 24
  • 74
  • 114
0

If you want to use getString Outside of a Context or Activity you should have context in constructor or method parameter so that you can access getstring() method. Specially in Fragment You shoud ensure that getActivity() or getContext() are not providing null value. To avoid null from getActivity() or getContext() in a Fragment try this : Declare a variable :

Context mContext;

now override onAttach and onDetach method of Fragment :

@Override
public void onAttach(Context context) {
    super.onAttach(context);
    mContext = context;
}

@Override
public void onDetach() {
    super.onDetach();
    mContext = null;
}

Now Use mContext whereever you are using getString() method. ex:

        Toast.makeText(mContext,  mContext.getString(R.string.sample_toast_from_string_file), Toast.LENGTH_SHORT).show();
MANISH
  • 2,883
  • 4
  • 11
  • 30
0

To access string resources without a context, you can create a separate utility class and pass the application context to it. Here's an example:

Create a class named ResourceUtils:

   object ResourceUtils {
        private var applicationContext: Context? = null
    
        fun initialize(context: Context) {
            applicationContext = context.applicationContext
        }
    
        fun getString(resId: Int): String {
            applicationContext?.let {
                return it.resources.getString(resId)
            }
            throw IllegalStateException("ResourceUtils not initialized with application context")
        }
    }

In your Application class, initialize the ResourceUtils with the application context:

class MyApplication : Application() {
    override fun onCreate() {
        super.onCreate()
        ResourceUtils.initialize(this)
    }
}

Now, you can use ResourceUtils.getString() to access string resources without a context:

val appName = ResourceUtils.getString(R.string.app_name)
Mohsinali
  • 543
  • 7
  • 14
-4
  • AppCompatActivity
  • Companion object

Activity code:

class MainActivity : AppCompatActivity() {

    companion object {
        lateinit var instance: AppCompatActivity
            private set
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        instance = this
    }
}

Getting a resource from AnyWhere:

val text = MainActivity.instance.getString(R.string.task_1)
Dharman
  • 30,962
  • 25
  • 85
  • 135
  • 5
    This solution causes memory leak, as saving Activity (or any other system component) into a static variable disallows garbage collector to properly release it. This is considered a bad practice. – r4zzz4k Mar 02 '21 at 17:25
-11

I used getContext().getApplicationContext().getString(R.string.nameOfString); It works for me.

vivynz
  • 5
  • 1
  • 4