Can an Android Context inside an activity be static? - java

I have a static function in which I need to access my color resources. In order access color resources I need context variable which is static. I am confused if I can make context static. Is there any side effects to it? or, is there any other way I can access my resources without using context
Here is the function
private static SpannableStringBuilder setTextColor(
SpannableStringBuilder Text, int spanLength, boolean isSuggestion) {
addressText.setSpan(
new ForegroundColorSpan(
context
.getResources()
.getColor(
isSuggestion ? R.color.blur: R.color.red)),
addressText.length() - 1 - spanLength,
addressText.length(),
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
return Text;
}

I am confused if I can make context static. Is there any side effects
to it?
You can declare a context as static but it is not recommended in Android, because it might lead to a memory leak in your app.
is there any other way I can access my resources without using
context?
No, you need a context instance to access resources in your app.
Back to your case, the easiest way is passing a context as param of the method.
private static SpannableStringBuilder setTextColor(Context context, SpannableStringBuilder Text, int spanLength, boolean isSuggestion) {
int color = context.getResources().getColor(isSuggestion ? R.color.blur : R.color.red);
addressText.setSpan(new ForegroundColorSpan(color),
addressText.length() - 1 - spanLength,
addressText.length(),
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
return Text;
}
Inside your activity, pass this as context when calling setTextColor, for example.
setTextColor(this, new SpannableStringBuilder(), 0, false);

In Kotlin, you can achieve this by creating a class that extends Application and storing the application context in the companion object.
Usually it will look similar to this:
class App : Application() {
override fun onCreate() {
super.onCreate()
instance = this
}
companion object {
private var instance: App? = null
val context: Context?
get() = instance?.applicationContext
}
}
And you can access the context anywhere via App.context
In regards to your concerns about storing a static context, if you were storing an activity or fragment context then you risk creating memory leaks, but since we're storing the application context which is tied to the lifecycle of the entire application, there won't be any issues with memory leaks.
You may run into issues if you want to write testable code that depends on a static context, in which case I would recommend you pass the context into the function rather than access it directly.

1) Create your App class which extend Application
private static Context context;
#Override
public void onCreate() {
super.onCreate();
context = getApplicationContext();
2) Create getApplicationContext() method which return context
public static Context getApplicationContext() {
return context;
}
3) Now you can get context anywhere in your class like
Context context = App.getContext().getApplicationContext();

Related

"Cannot resolve method getResources" when trying to execute getResources in non-activity method using method widely advocated here

I have been trying to use getResources in a non-activity class. I found some advice on how to do so here. To use one of the suggested ways, by Lilzilala, (there are multiple, but mostly suggest the same thing), I have created a special class, used this to specify the resources as "res", and then instantiated this class using "new" in a line which invokes "getResources".
However, I'm getting a "cannot resolve method getResources" error on "getResources". I'm a bit of a noob, but don't know why this is happening. From what I can tell, this error happens when there simply isn't a resource with that name available. Which makes me think maybe Resources doesn't contain getResources() by default?
class executeTrimmer<Resdefine> {
public class ResDefine {
private Resources res;
public ResDefine(Resources res)
{
this.res = res;
}}
Bitmap img1 = BitmapFactory.decodeResource(new ResDefine(getResources()),
R.drawable.bmpname);
}
EDIT - following suggestions that I add context, I have tried this:
class executeTrimmer<Resdefine> {
private static Context context;
public executeTrimmer(Context context){
this.context = context;
}
public class ResDefine {
private Resources res;
public ResDefine(Resources res)
{
this.res = res;
}}
Bitmap img1 = BitmapFactory.decodeResource(new ResDefine(executeTrimmer.context.getResources),
R.drawable.bmpname);
But this still brings up error "cannot resolve symbol getResources". I've tried multiple different ways to pass context to it, and consistently faced the same error.
As you can see in the official documentation, "getResources" is Context's method, therefore you can't call it out from nowhere, neither statically. This method requires a context instance.
In your case you must at least pass a context to your class to be able to invoke it as next:
context.getResources()
I think you got confused from seen it being directly called inside Activities without a prefixed context, but as all Activities are actually a context, this is why there is no prefix.
To clarify. When called inside an activity, this:
getResources()
is the same as this:
this.getResources()
where the prefix "this." refers to the activity, which in turn is a context by itself.
On the other hand your code should be like next, without the ResDefine class. And notice that the decodeResource call is required to be inside a method and not at class level scope (this is not allowed in Java). And in fact you don't even need to use a context, so pass instead the Resources instance from the caller's class which is supposed to hold the context:
public class executeTrimmer {
private final Resources res;
public executeTrimmer(final Resources res) {
this.res= res;
}
public void loadBitmap()
Bitmap img1 = BitmapFactory.decodeResource(this.res, R.drawable.bmpname);
........
}
}
And for the caller, next a very naive example, so may get an idea:
public class MainActivity extends Activity {
#Override
protected void onCreate(#Nullable final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
new executeTrimmer(this.getResources()).loadBitmap();
}
}

How to access Room database from different fragments?

In my app I have a TabLayout and each of the tabs is represented by a fragment. I have several tables in a database. And for each table I want to have a tab that would display a list of table's contents. To access a database I need to pass in a context but it's only available from the MainActivity. How to access a database instance from each fragment?
Here's some code:
ElectronicsDatabase.java
#Database(entities = {Smartphone.class, Tablet.class,
Laptop.class, VideoGameConsole.class}, version = 1)
public abstract class ElectronicsDatabase extends RoomDatabase {
public abstract SmartphoneDao getSmartphoneDao();
public abstract TabletDao getTabletDao();
public abstract LaptopDao getLaptopDao();
public abstract VideoGameConsoleDao getVideoGameConsoleDao();
private static final String DB_NAME = "products.db";
private static ElectronicsDatabase db;
public static ElectronicsDatabase getInstance(Context context)
{
if (db == null)
{
db =buildDatabaseInstance(context);
}
return db;
}
private static ElectronicsDatabase buildDatabaseInstance(Context context)
{
return Room.databaseBuilder(context, ElectronicsDatabase.class,
DB_NAME).allowMainThreadQueries().build();
}
}
And in the main activity I access it like this:
db = ElectronicsDatabase
.getInstance(getApplicationContext());
In your fragments you can use getActivity() to acccess context of your parent activity.
but i suggest you to use viewModel for accessing to your database.
Try to use ViewModel to access database in any activity or Fragment
ViewModel is a class that is responsible for preparing and managing the data for a UI component (activity or Fragment)
In your case which is needing a Context for DB access via Room, it is better to pass a non-UI Context as to avoid unnecessary information being passed around for no reason leading to possible memory leaks.
You can get access to a non-UI Context which will be called ApplicationContext from your base activity, or your main activity. Simply like this:
Context appContext= getApplicationContext();
Then store it in a Repo class, so you can simply use it anytime you need it again anywhere without worrying about it.
However, if you need a context for something related to drawing on the screen, like inflating an XML for example, then in that case you will need a UI Context as not to lose UI details like your theme for example. In that case you can get the context from inside your fragment using:
getContext() Or getActivity().
I won't go to further details about contexts but,
if you want to learn more about what Context really is you can start from here:
https://medium.freecodecamp.org/mastering-android-context-7055c8478a22
That might be a very late answer, but I believe it might help some in the future:
If you are using multiple fragments and DI, you can create a viewModel for your activity, then inject the desired value into it
#HiltViewModel
class MainActivityViewModel #Inject constructor(
private val repository: PlantsRepository
): ViewModel() {
private val mutableLiveData = MutableLiveData<List<Plant>>()
val liveData: LiveData<List<Plant>> = mutableLiveData // object to observe
init {
viewModelScope.launch {
mutableLiveData.postValue(repository.getAllPlants())
fillExampleData()
}
}
}
(sample for reference)
then you simply use it in your fragments:
class FragmentExampleScreen : Fragment() {
private val sharedViewModel: MainActivityViewModel by activityViewModels()
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View {
...
sharedViewModel.liveData.observe(viewLifecycleOwner) { idk ->
Log.i("hello", idk.toString())
}
...
}
}

Wrong 1st argument type. Found: 'com.example.sunshine.FetchData', required: 'android.content.Context'

I guess this question is more about understanding context and how to use it properly.
After having googled and "stackoverflowed" a lot I could not find the answer.
Problem:
when using DateUtils.formatDateTime I cannot use "this" as a context. The error message is as described in the title.
Application Info:
This is a simple weather app retrieving weather information via JSON and displaying it on the screen.
Activities:
- MainActivity.java
- FetchData.java
MainActivity: displaying the info
FetchData: getting JSON info from the API, formatting it and sending it back to MainActivity
I am using DateUtils.formatDateTime in the FetchData.java activity and using "this" as a context does not work.
As from my understanding Context provided the "environment" (?) of where the method is being called.
Why is the "environment" of FetchData not valid?
What content should be provided instead?
Help is much appreciated.
Thank you :)
Code:
private ArrayList<String> getWeatherDataFromJson(String forecastJsontStr) throws JSONException {
ArrayList<String> dailyWeatherInfo = new ArrayList<>();
int dataCount;
DateUtils tempDate = new DateUtils();
JSONObject weatherData = new JSONObject(forecastJsontStr);
JSONArray threeHourWeatherData = weatherData.getJSONArray(JSON_LIST);
dataCount = weatherData.getInt("cnt");
JSONObject tempJSONWeatherData;
for (int i = 0; i < dataCount; i++) {
tempJSONWeatherData = threeHourWeatherData.getJSONObject(i);
tempDate.formatDateTime(this,tempJSONWeatherData.getLong("dt"),
DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_SHOW_WEEKDAY |
DateUtils.FORMAT_ABBREV_ALL);
[more code here]
return dailyWeatherInfo;
}
Edit: I just realized I left out an important detail, namely this activity extends AsyncTask. After some further research apparently you provide the context bei adding WeakReference and then adding context in the constructor.
I added the following code:
private WeakReference<Context> contextWeakReference;
public FetchData (Content context) {
contextWeakReference = new WeakReference<>();
}
tempDate.formatDateTime(contextWeakReference.get(),tempJSONWeatherData.getLong("dt"),
DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_SHOW_WEEKDAY |
DateUtils.FORMAT_ABBREV_ALL);
This made the error disappear but I still don't understand why "this" doesn't work.
I am using DateUtils.formatDateTime in the FetchData.java activity and
using "this" as a context does not work. As from my understanding
Context provided the "environment" (?) of where the method is being
called.
You're incorrect, Context is Android context which is (from documentation):
Interface to global information about an application environment. This
is an abstract class whose implementation is provided by the Android
system. It allows access to application-specific resources and
classes, as well as up-calls for application-level operations such as
launching activities, broadcasting and receiving intents, etc.
DateUtils.formatDateTime() needs Context as one of its parameter. So, you need to pass a context.
Android Activity is sub class of Context, so you can use this (which refer to itself) as the context like the following:
public class MyActivity extends Activity {
...
protected void doSomething() {
// this refer to the MyActivity instance which is a Context.
DateUtils.formatDateTime(this, ...);
}
...
}
You need to pass the Context for every class that is not a Context subclass.
You can't use this in AsyncTask because it's not a Context subclass. So, you need to pass the Context using WeakReference to avoid Context leaking, like the following:
private class AsyncTaskRunner extends AsyncTask<String, String, String> {
private WeakReference<Context> contextWeakReference;
public FetchData (Content context) {
contextWeakReference = new WeakReference<>();
}
private void doSomething() {
// We have the context from the WeakReference
Context context = contextWeakReference.get();
DateUtils.formatDateTime(context, ...);
}
}
Last, you don't need to create a DateUtils object when calling DateUtils.formatDateTime(), so this isn't necessary:
DateUtils tempDate = new DateUtils();
tempDate.formatDateTime(...);
You can directly call it because it's a static method:
DateUtils.formatDateTime(...);
tempDate.formatDateTime(this,tempJSONWeatherData.getLong("dt"), instead of this you can pass context of application, this refers on class FetchData

Static context in App class - memory leak

To be able to get app context anywhere in my app, I created App class like this:
public class App extends Application
{
private static Context mContext;
public static Context getContext()
{
return mContext;
}
#Override
public void onCreate()
{
super.onCreate();
mContext = this
}
}
It works and also it's used in many places in my app where I need to use context (for example, to load resources) and I am not able to inject any other context to use.
However, Android Studio throws warning this approach (static context fields) causes memory leak.
Do you have any idea how to avoid static context field, but get similar functionality?
Never place static Context in your application since it will cause unexcepted memory leaks, however if you still want to use static Context in your application you can wrap the context in a WeakReference so change
private static Context mContext;
to
private static WeakReference<Context> mContext;
and on create change it to
mContext = new WeakReference<>(Context);
and finally get the Context using
public static Context getContext() {
return mContext.get();
}
if you want to research more about WeakRef use the link below,
https://developer.android.com/reference/java/lang/ref/WeakReference
Its not necessary use static for access context ,you can use get context ,get application context or get activity any where.
as far as possible you should avoid from passing context.
like this in fragments :DividerItemDecoration dividerItemDecoration = new DividerItemDecoration(getContext(), layoutManager.getOrientation());
and in this (if the OP wanted to use Context in where the class does not host a Context method) case you can pass context without define it as a static.
for example :
public class DashboardWalletSpinnerAdapter extends ArrayAdapter<Wallet> {
private LayoutInflater mLayoutInflater;
private static final int CLOSE = 0;
private static final int OPEN = 1;
public DashboardWalletSpinnerAdapter(Context mContext, List<Wallet> walletList) {
super(mContext, R.layout.spinneritemclose_dashbaord, walletList);
mLayoutInflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}

Is it good idea use static class AppContext?

I found in one of github project class:
public class AppContext {
private static Context sContext;
private static Application sApplication;
public static Application getApplication() {
if (sApplication == null) {
throw new IllegalStateException("AppContext.setApplication was not called in Application.onCreate() method. " +
"Please inherit your application from the com.blandware.android.atleap.BaseApplication class.");
}
return sApplication;
}
public static void setApplication(Application application) {
sApplication = application;
}
public static Context getContext() {
if (sContext == null) {
throw new IllegalStateException("AppContext.setContext was not called in Application.onCreate() method. " +
"Please inherit your application from the com.blandware.android.atleap.BaseApplication class.");
}
return sContext;
}
public static void setContext(Context context) {
sContext = context;
}
}
It seams create, don't need more pass context to static function etc. But I'm worried about memory leaks. Can AppContext make it? When i shoud use Aplication context when activity context or view?
The Application object can not leak. There is always exactly one Application object for every app. It looks like the author is just using this class to make it easy to access in places where another Context is not available to be used to call getApplicationContext() to get the Application object.
Context, on the other hand, could be an Activity or a Service, and those really should not be stored beyond their lifetime. You will have to look at exactly which Context objects are being stored here to find out if there is a leak.

Categories