this problem is not always there, I can't find the cause of the problem, ask for help, thank you.
My code is
private static Toast systemToast;
public static Toast getSystemToast(Object resId) {
if (null == systemToast) {
// Apps is the Application.java
systemToast = Toast.makeText(Apps.getAppContext(), R.string.me_empty,
Toast.LENGTH_SHORT);
}
String res = String.valueOf(resId);
if (resId.getClass() == Integer.class) {
systemToast.setText(Integer.valueOf(res));
} else if (resId.getClass() == String.class) {
systemToast.setText(res);
}
systemToast.setDuration(Toast.LENGTH_SHORT);
return systemToast;
}
/** Apps.java **/
public class Apps extends Application {
private static Apps sContext;
#Override
protected void attachBaseContext(Context base) {
sContext = this;
}
public static Apps getAppContext() {
return sContext;
}
In some Android equipment, Error occurred, The error Log is:
android.content.res.Resources$NotFoundException: File res/layout
/transient_notification.xml from xml type layout resource ID #0x10900ef at
android.content.res.Resources.loadXmlResourceParser(Resources.java:2720) at
android.content.res.Resources.loadXmlResourceParser(Resources.java:2675) at
android.content.res.Resources.getLayout(Resources.java:1096) at
android.view.LayoutInflater.inflate(LayoutInflater.java:422) at
android.view.LayoutInflater.inflate(LayoutInflater.java:368) at
android.widget.Toast.makeText(Toast.java:282)
This error might be occurred, when pass context object not proper initialize or might be its referencing to null.
1.if you are using Fragment than, you can find Context in onAttach Method. And pass your getSystemToast Method.
**#Override
public void onAttach(Context context) {
super.onAttach(context);
}**
2. If you are using Activity, than get Context using getBaseContext() Method or ActivityName.this both will return you context
You no need to defined function for get Context. Android provide Following Method for get Context.
1.getApplicationContext() Application context is associated with the Applicaition and will always be the same throughout the life cycle.
2.getBaseContext()
3.onAttach() in Fragment.
Related
I am trying to access application resources, (string resources to be specific) from a Singleton class. Being Singleton, this class cannot hold any reference to Context objects (to prevent memory leak). While I was looking for other implementations on the net, I came across this two implementation:
Create a static context in Application class and use it across the app.
Pass context as a parameter to the method that requires it.
I don't want to use the fist one as it also uses a static reference to Context object. I understand that it's ok to have it statically in the Application class of android, but still it looks like a hack.
The second implementation is useless since i don't have any instance of context which I can pass to the someOtherMethod of the singleton.
So I came up with following implementation where I make my Singleton abstract to override its context requiring methods (for ex. getString(int resId) in the code below) when I initialize the singleton instance.
I am curious to know if this can lead to any memory leaks now?
Where am I confused with this approach:
--> The reference to context in the Overridden getString is final. I am not sure if that can cause a memory leak or not.
public abstract class SingletonClass{
.
.
.
private static SingletonClass sInstance;
private SingletonClass(Context paramContext) {
// constructor code
}
public static SingletonClass getInstance(final Context context) {
if (sInstance == null) {
sInstance = new SingletonClass(context){
#Override
public String getString(int resId) {
return context.getString(resId);
}
};
}
return sInstance;
}
public abstract String getString(int resId);
.
.
.
private void someOtherMethod(){
//uses above getString()
}
}
Your approach does have a memory leak. The first context passed into getInstance will never be garbage collected, since your anonymous class holds a reference to it. (and there is a static reference to the anonymous class). e.g., if you call getInstance(Activity), that activity will remain in memory until the process is killed!
Fortunately there is a pretty easy fix to get rid of the memory leak. You can safely hold onto the application context (context.getApplicationContext), which is basically a singleton context for lifetime of the app.
public static SingletonClass getInstance(Context c) {
if (sInstance == null) {
sInstance = new SingletonClass(c.getApplicationContext());
}
return sInstance;
}
You can depend on activity lifecycle, and require activities to pass reference to your singleton object in onResume method, and clean it in onPause.
protected void onResume() {
super.onResume();
Singleton.getInstance().onResume(this);
}
protected void onPause() {
super.onResume();
Singleton.getInstance().onPause();
}
Also, you can refresh the instance of Context and hold it in WeakReference:
class Singleton {
private WeakReference<Context> mContext;
private boolean hasContext() {
return mContext != null && mContext.get() != null;
}
public static Singleton getInstance(Context c) {
//do your singleton lazy
if (!sInstance.hasInstance()) {
sInstance.mContext = new WeakReference<>(c);
}
return sInstance;
}
}
Second case could hold a reference to finishing activity, so i don't suggest it.
I've implemented a singleton class to avoid pass Activity as parameter to every method/constructor.
public class ApplicationContext {
private static class Holder {
private static final ApplicationContext INSTANCE = new ApplicationContext();
}
private ApplicationContext() {}
private Context mContext;
public static ApplicationContext getInstance(){
return Holder.INSTANCE;
}
public Context getContext(){
return Holder.INSTANCE.mContext;
}
public void setContext(Context mContext){
Holder.INSTANCE.mContext = mContext;
}
}
And in my initial activity, i set the context:
private void initializeObjects(){
ApplicationContext.getInstance().setContext(getApplicationContext());
}
The problem happens when i try to use context in a ProgressDialog:
#Override
protected void onPreExecute() {
pd = ProgressDialog.show(context, "", "Salvando Usuário");
}
Error message:
07-05 20:29:46.413 30930-30930/com.test.app E/AndroidRuntime﹕ FATAL EXCEPTION: main
Process: com.test.app, PID: 30930
android.view.WindowManager$BadTokenException: Unable to add window -- token null is not for an application
at android.view.ViewRootImpl.setView(ViewRootImpl.java:690)
at android.view.WindowManagerGlobal.addView(WindowManagerGlobal.java:289)
at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:85)
at android.app.Dialog.show(Dialog.java:311)
at android.app.ProgressDialog.show(ProgressDialog.java:116)
Is a good practice to use a singleton like i've tried? What's wrong? If it's not, what is a good practice instead pass context/activity to every call?
Thank in advance.
For AlertDialogs you cannot use any context that isn't an Activity so use something like Activity.this or getActivity() if you are in a Fragment.
You can still use the Application global context to get colors and drawables and other stuff, but for example creating a view with this context the theme is ignored so try to use the Activity context as much as possible.
By the way if you want to have a singleton where you can access the context, you should extend the Application class in the ApplicationContext and override the method onCreate, along with adding this ApplicationContext class to your manifest Application element name. If you use that initializeObjects method and call it from the first activity what would happen to that context when you exit that activity?
So as an example:
public class ApplicationContext extends Application {
private Context mContext;
public static Context getContext(){
return mContext;
}
#Override
public void onCreate() {
mContext = this;
// you can initialize other stuff here if you want
}
}
// manifest
<application
android:name="com.example.ApplicationContext"
When the app is run, it always goes through the onCreate method of the Application class and it lives through the entire lifecycle of the app. But don't abuse it too much.
The code is followed:
Context c = getContext().createPackageContext("com.master.schedule",
Context.CONTEXT_INCLUDE_CODE|Context.CONTEXT_IGNORE_SECURITY);
int id = c.getResources().getIdentifier("layout_main", "layout","com.master.schedule");
LayoutInflater inflater = LayoutInflater.from(c);
piflowView = inflater.inflate(id, null);
com.master.schedule is my package project.The upside code presents how the other project(his package name is different to mine) inflate my project, in my project there is only a ViewGroup(I don't have an activity); and when I invoke "context.getApplicationContext", it returns null... my project code is below:
public class CascadeLayout extends RelativeLayout {
private Context context;
public CascadeLayout(Context context) {
super(context);
this.context=context.getApplicationContext();
}
#Override
protected void onFinishInflate() {
super.onFinishInflate();
//here: context == null;
}
}
I find the createPackageContext() give the "Context c" to me is a ContextImpl type; I think that's caused return null;
So how can I get an ApplicationContext which not null?
BTW:please don't persuade me don't invoke getApplicationContext();As I must use an .jar, the jar need to invoke getApplicationContext() in it;
thanks very very much.
The documentation for createPackageContext() states:
Return a new Context object for the given application name. This
Context is the same as what the named application gets when it is
launched, containing the same resources and class loader.
Reading a bit between the lines, this context is apparently supposed to be "the same" as an application context. However, you are seeing that its getApplicationContext() returns null, so we can attempt to fix that by using a ContextWrapper (see below). Hopefully, this approach will be good enough for your needs.
In the following code, WrappedPackageContext is used to wrap the "package context" returned by createPackageContext(), overriding the getApplicationContext() implementation so that it returns itself.
class WrappedPackageContext extends ContextWrapper {
WrappedPackageContext(Context packageContext) {
super(packageContext);
}
#Override
public Context getApplicationContext() {
return this;
}
}
Context createApplicationContext(Context packageContext) {
return new WrappedPackageContext(packageContext);
}
I have an app on Android 4.0. It uses the PreferenceManager class to -- among other things -- let the user specify how many decimal places of a number to show.
mPreferences = PreferenceManager.getDefaultSharedPreferences(this);
Generally I have no problem getting the app context in order to access the Preference Manager. My problem is that I have a class (let's call it Record) that isn't subclassing anything that has the app context; it's just a storage class, but it does have a field "NumDecPlaces". Right now, when I instantiate the class from within my app I just pass in the user's #dec places preference. It would be nice if Record could access the Preference manager directly. I suppose I could always instantiate Record with a pointer to the context from which it was created, but that's a lot to remember ;-)
So right now Record subclasses nothing. Any recommendations on what I can do to it to allow it to see the app context?
Thanks!
You could pass the Context object in the constructor. So whenever you try to use that class it will ask you pass a Context object and then use that to get SharedPreferences
For eg.
public Record(Context context)
{
mContext = context;
mPreferences = PreferenceManager.getDefaultSharedPreferences(mContext)
}
You can also extend a class with Application, which will be global to the whole application and you can set the context in that class as a member variable and that context will be global to the whole application
Eg. class A extends Application{......}
You can do #Apoorv's suggestion or you can create another class that specifically stores the application context.
public class ContextResolver {
private static Context context;
public static void setContext(Context context) {
if (context == null) {
throw new IllegalArgumentException("Context must not be null");
} else if (context instanceof android.app.Activity) {
context = androidContext.getApplicationContext();
} else if (context instanceof android.content.Context) {
context = androidContext;
}
}
public Context getContext() {
return context;
}
}
Now you need to call setContext() in the first activity that will be launched once.
public class MyFirstActivity extends Activity {
public void onCreate() {
ContextResolver.setContext(this);
}
}
Now you can retrieve the Context from any part of your code. So in your Record class you can just do this:
mPreferences = PreferenceManager.getDefaultSharedPreferences(ContextResolver.getContext());
I have an app that uses custom Exceptions, such as this:
public class SomeException extends Exception{
private int iCode;
private String iMessage;
public SomeException(){
iCode = 201;
iMessage = **//Get the localized string R.string.error_201??**
}
#Override
public String getMessage() {
return iMessage;
}
#Override
public int getCode() {
return iCode;
}
}
Obviously, I want lo localize the error message. I have possible solutions but non of them satisfy me.
1) Pass "Context" to the constructor, and do ctx.getString(R.string.error_201)
--> Fail, as this Exceptions are sometimes thrown from MODEL classes, so they don't have a Context
2) Pass "Context" when retriveing the message in getMessage() function,
--> Fail, It's necesary to override the super method, to work as all other Exceptions.
Solution I have now: All activities in my app have this onCreate:
public void onCreate(...){
Utils.RESOURCES = getResources();
...
}
Very dirty code... I don't like the solution. My question is then,: is there a way to access the resources without the Context? And most important, How would an application such as mine solve this problem?
What about
public class MyException extends Exception {
private int iCode;
public MyException(int code) {
this.iCode = code;
}
#Override
public String getMessage() {
return "MyException code " + String.valueOf(iCode);
}
public String getLocalizedMessage(Context ctx) {
String message;
if (iCode == 201)
message = ctx.getString(R.string.error_201);
else if (iCode == 202)
message = ctx.getString(R.string.error_202);
// ...
}
}
Even if there was way to access context in different way, you should not do it. If you need to emit exceptions where you cannot pass Context, you should be able to access context before you display such error. I cannot see reason why you should create localized error messages from constructor. You can log to logcat not localized versions if you need. And where you want to display something in UI, you should have context at hand.
You can access only system wide resources without Context.
You need a Context, so I would suggest You to get it as soon as possible, and make it available through a static method or variable. You do the same thing in every Activity, but there is a cleaner method. You should make a custom Application, and override its onCreate() to make the resources public:
public class App extends Application {
private static Resources myResources;
#Override
public void onCreate() {
myResources = getBaseContext().getResources();
super.onCreate();
}
public static Resources getMyResources(){
return myResources;
}
}
The other thing you have to do is to set the Application in your manifest:
<application
android:name="{your_package}.App"
...
Now you can access the resources in all of your Activity without any preparation. Your custom Exception class could also use the externalized resources.