getString Outside of a Context or Activity
Asked Answered
A

18

335

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?

Arbuthnot answered 23/11, 2010 at 6:23 Comment(1)
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 useBarrister
E
572

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.

Enchiridion answered 6/1, 2012 at 23:21 Comment(15)
what is meant by system resources? the strings.xml is system resource or not? For me it does not work, says cannot find resource.Inhumane
The system resources belong to Android on the device. strings.xml belong to your application only. Look for https://mcmap.net/q/75865/-how-can-i-get-a-resource-content-from-a-static-context solutionEnchiridion
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.Schulman
gracias for letting me, to not to leave android programming when we are used to of Helper classesBismarck
is this more effiecient than passing the context to a class and using it ??Physoclistous
@Physoclistous 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.Enchiridion
@Physoclistous 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?Enchiridion
i get this error android.content.res.Resources$NotFoundException: String resource ID #0x7f0f0061Inearth
@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.Enchiridion
It does not work in static contexts. I get a "exception in initializer error" when using it within a static method.Gravy
@Gravy 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.Enchiridion
didn't work for me :( workaround was passing a context to a utility classZeitgeist
@San Askaruly It's easier to save the application instance in a static variable. Works. No leak. No problems.Nguyetni
@TheincredibleJan yeah, I think I ended up doing something like you suggest, by creating a class of constants: https://mcmap.net/q/77035/-how-to-store-separate-constant-class-for-constant-variables-in-android-developmentZeitgeist
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] worksAspirant
M
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.

Manful answered 23/11, 2010 at 6:37 Comment(9)
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);Arbuthnot
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.Proteiform
You don't need the ctx.R.string.blah, just use R.string.blahRadiate
@Pentium10: It gave me a symbol not found error for R.string.blah because the class is not a context. :(Arbuthnot
I suppose I could pass in the R as well, but that seems very clunky.Arbuthnot
I am not sure from where the symbol not found error comes, but make sure you have R imported on top of the class.Radiate
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.Arbuthnot
The answer is FALSE. Vis the next one. :-)Enchiridion
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 @Enchiridion 's answer.Amasa
H
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.

Hoarse answered 10/3, 2015 at 17:14 Comment(2)
Would it work through service? (Especially when Android kills app and starts only service)Ilonailonka
Yes, Android begins to execute your code by calling Application.onCreate and after that it runs your service.Hoarse
L
34

##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.

Leash answered 11/7, 2018 at 7:32 Comment(3)
This has been already suggested in 2010/2014 in https://mcmap.net/q/75865/-how-can-i-get-a-resource-content-from-a-static-contextChamber
@Chamber Should it be deleted?Leash
Actually, no. There is slight difference. Your sollution is less prone to mem leaks in opposite to the original one. I didn't noticed it at the first glance.Chamber
O
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);
}
Ovipositor answered 24/5, 2012 at 6:30 Comment(0)
M
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
Melbourne answered 14/2, 2019 at 8:53 Comment(1)
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.Jarrell
B
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);
Blare answered 13/3, 2016 at 7:14 Comment(0)
V
4

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))
Valentinevalentino answered 23/6, 2022 at 19:20 Comment(0)
B
3

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)
Brodie answered 29/12, 2019 at 6:5 Comment(1)
This won't work if you change the locale programmatically during runtime because application context is a singleton and initialized when your process started.Kisangani
A
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()
Aristides answered 23/8, 2022 at 15:13 Comment(0)
B
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. :)

Brimmer answered 25/9, 2015 at 12:16 Comment(0)
T
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)
    }
  }
}
Turner answered 21/7, 2022 at 12:44 Comment(0)
M
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.

Marishamariska answered 19/12, 2019 at 5:23 Comment(0)
M
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();
Monorail answered 23/12, 2021 at 18:49 Comment(0)
D
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)
Dykstra answered 7/7, 2023 at 6:10 Comment(0)
E
0

I resolve this problem with this code

holder.btnAddFriend.setOnClickListener(v -> {
        // Obtener el nombre del usuario del elemento actual

        String username = user.getUsername();
        String id = v.getContext().getResources().getString(R.string.idusd);
 }
Entrain answered 17/11, 2023 at 15:4 Comment(0)
C
-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)
Capitation answered 2/3, 2021 at 16:22 Comment(1)
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.Trundle
H
-11

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

Hafler answered 16/2, 2015 at 15:14 Comment(2)
Do you think getContext() is available everywhere?!Defective
This does not provide an answer to the question because getContext() only available in activites and fragments classesBrede

© 2022 - 2024 — McMap. All rights reserved.