I have different Activities where the catch{} block mostly should do the same, eg. show a Toast message, finish() the app, or something else. So I made one static class to handle them. It works till now, but I want to know that it's save.
To finish an Activity I have to call finish() which is in the Activity class, so I have to pass it.
I made a static function like this:
public static void handleException(Exception e, Context c, Activity a) {
// handle the exception, for example if there is an authorisation error:
a.finish();
}
In a catch{} block I do the following: (in this case Activity LoginActivity)
ExceptionHandler.handleException(e, _context, (Activity) LoginActivity.this);
Is this appropriate? Or does this cause memory leaks?
If you dont save any link to Context or Activity there will not be any memory leaks or problems with GC.
Related
Can using context in helper class cause memory leak in android
I have a helper class with the following method
public class HelperClass {
private Context context;
public HelperClass(Context context) {
this.context = context;
}
public void Addfiles(Context context, String Filename) {
try {
Intent mediaScanIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
File f = new File(Filename);
Uri contentUri = Uri.fromFile(f);
mediaScanIntent.setData(contentUri);
context.sendBroadcast(mediaScanIntent);
} catch (Exception e) {
e.printStackTrace();
}
}
}
In my MainActivity I want to call it like this
public class MyActivity extends Activity {
public void onCreate(Bundle savedInstanceState) {
HelperClass h = new HelperClass(this);
h.Addfiles(this,Filename);
}
}
I want to know can using context like this cause memory leaks , and if so , how to deal with it.
TL;DR - strictly in the case above, no, since MyActivity won't be destroyed while in "onCreate"
Long answer:
Typically memory leaks happen when an object cannot be garbage collected because something still references it, when the garbage collector runs.
In your case, the activity is just being created, and your primary concern is whether leaks can occur, since HelperClass holds a reference to it.
First of all, in normal conditions (excepting when AOS might kill your app) MainActivity is guaranteed to not get garbage collected before its onDestroy method will be called. This is because until then (and possibly for some time after that - not really relevant) it will be referenced by the Android Framework itself.
HelperClass on the other hand, is a local variable inside onCreate. After onCreate will finish, HelperClass might be garbage collected at any point, since it won't have any object referencing it (see here). So the chances of HelperClass being GC'd before onDestroy is called are quite high. If this happens then there will be no reference to MainActivity --> no leaks.
Event if HelperClass won't be garbage collected until onDestroy is called on MainActivity, you still won't have leaks since the garbage collector is smart enough to be able to clean up cyclic references.
What needs to happen for leaks to occur is that an object which outlives your Activity will hold a reference to it, or its context.
Assume your code would be like this:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
final HelperClass h = new HelperClass(this);
//does some work on a background thread, gets notified at some
//point in the future, through the callback, when the work
//has completed
longLivedComponent.doSomeBackgroundWork(new OnWorkFinishedListener(){
#Override
public void onWorkFinished(){
h.Addfiles(this, filename);
}
});
}
Now, let's also assume the following sequence of events:
User opens app, MainActivity#1 is created, its onCreate is called
doSomeBackgroundWork is called, background work is started. When the background work finishes, your helper (which has a reference to MainActivity#1) will have to do some work.
User rotates device, an orientation change occurs.
MainActivity is recreated by Android. MainActivity#1 is destroyed (i.e. onDestroy will be called), and a new MainActivity#2 will be created
Steps 1,2 will happen for MainActivity#2 i.e. doSomeBackgroundWork will be called again, from MainActivity#2. (strictly speaking, this step isn't really relevant, but nevertheless)
The Garbage Collector runs.
The background work started by MainActivity#1 at step #2 finishes.
Normally, at step #6 the memory occupied by MainActivity#1 would be freed, as MainActivity#1 was destroyed.
However, in situation described here, the OnWorkFinishedListener needs to be persisted by longLivedComponent until the background work finishes. The listener has a reference to both your MainActivity#1 (implicit anonymous inner class reference to its parent class), and to your HelperClass instance, which also contains an instance to MainActivity#1.
When the garbage collector sees this at step #6, it assumes that MainActivity#1 is still alive & used since there stil are objects pointing to it. Because of this, it doesn't reclaim its memory, you'll end up with two instances of MainActivity in memory and a leak occurs.
To prevent this, you could:
Use the application context instead of the activity context, in classes that will be used by long-lived objects. Also, in your case you don't need anything specific to the activity itself
Make sure that any long-lived objects (like longLivedComponent in our case), or objects shared between activities, won't store references to an Activity or its Context, after onDestroy() has been called on said Activity
Hope this helps
How I get a Application Context:
private static OBDApplication mInstance;
public static OBDApplication getInstance () {
return mInstance;
}
#Override
public void onCreate () {
super.onCreate();
mInstance = this;
}
I know that application Context can't be used everywhere, for example, Dialog and startActivity. And I know, I should always let the reference with activity Conetxt finish when activity destroyed. But I notice that,Toast and SharedPreferences can use application Context, and it seems to be very innocent that would not lead to a memory leak.
So what am I suppose to do? If the problems above does not exist, then things can be cool:
public static void toast ( #StringRes int resID){
Toast.makeText(OBDApplication.getInstance().getApplicationContext(), resID, Toast.LENGTH_SHORT).show();
}
But I know easiest way, so I took some time to search but I get some opposite opinions.
Someone said: "it's bad to use application Context, cause while activity destroy, the toast of this activity should release too, but if u use application Context which means it's life is as long as the application. So is kind of waste".
However, someone said: "it's good to use application Context, cause in this case, you always use a same object , a same toast ,that means it will not produce new toast again and again".
Yes definitely you can use application context for show toast and sharedprefrence also , Because they both are use without interact with activity.
Simple example is like service, you can use both of this from service and service have no any activity reference but it's working fine..
You can Use Application Context Throught Application even in
Activity and Service mean you can use in all Android Components .
Also Showing Toast messages , SharedPreferences , Catering DataBase Object ,LayoutInflater .
Application – is a singleton instance running in your application
process. It can be accessed via methods like getApplication() from an
Activity or Service, and getApplicationContext() from any other object
that inherits from Context. Regardless of where or how it is
accessed, you will always receive the same instance from within your
process.
As far as i know, Toast and shared preferences doesn't having parent view of the application, it will accept all the application context and it will not demand for the application context of your application. it will work even if your application is not in active.
whereas , in our application we may create some view which will run only in our application context. when you save it in static and try to use when that particular context is not exist, it will trough an exception.
So, the save border is , We should not save the instances of our application context anywhere. you should retrieve it when you need it from your application using getActivity() from fragment, this from Activity.
I need to show some dialogs as debug in my app.
The structure of the app itself is written so that it is easier to actually call and show the dialog from a static class with static methods. These methods all points toward a bigger method which eventually take care of the requests.
What I'd like to achieve is to call an eventual Dialog (I'm using the Material Dialog library by afollestad on github) which needs a reference to the current activity.
I actually have a private static Activity sActivity; field in the class, and the relative setActivity(Activity activity) method.
Currently, I've got my own CustomApplication from which I call this:
registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacks() {
#Override
public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
MyStaticClass.setActivity(activity);
}
[...]
}
which is not working as intended because this
try {
Utils.showSimpleDialog(sActivity, "Error", message);
} catch (MaterialDialog.DialogException d) {
d.printStackTrace();
}
is always calling the catch case.
My question is, is it possible to avoid the setActivity call from every single Activity? If yes, how? Thank in advance!
It's not a good idea to keep a static reference to an Activity as it can cause memory leaks with leaked contexts.
Edit to answer if it would still be dangerous if setting static activity to null in onDestroy as previously asked in a comment under this answer
Setting to null in onDestroy doesn't always serve as a workaround to this because if you run out of memory you can get into a state where Android can actually stop at the onPause stage of the lifecycle and not even hit onDestroy. Keeping static contexts is generally to be avoided.
It looks like showSimpleDialog already takes an Activity parameter. When you are calling it from an Activity, simply pass this , or from a fragment, pass getActivity(). If this call to showSimpleDialog is called from another utility method you've implemented, just pass an activity to that method also rather than setting a static Activity on the class.
I have a couple of Activities both of which send files up to the internet.
I would like to put the posting procedure into a separate class file so that the same procedure is called for each activity.
I pass the context of the calling activity to the main AsyncTask method which, in turn calls preExecute, doInbackground, Postexecute.
I have an AlertDialog, built using the context passed to the AsyncTask, in the postexecute method that shows the message returned by the server, with an OnClickListener to dismiss it. When the button is clicked the alertdialog is closed but the underlying activity screen is not.
I've tried several different combinations of finish() including:-
dialog.cancel();
finish();
and
dialog.cancel();
MyActivity ma = new MyActivity();
ma.this.finish();
nothing, though, will close the Activity.
What am I doing wrong and what do I need to do to get it to close?
So far the only way I can get it to work is to embed the exact same code in both Activities which seems to me to be both inefficient and susceptible to error.
Any help would be very welcome.
I pass the context of the calling activity to the main AsyncTask
instead of only passing context of Activity you will need to pass Activity instance to class in which extending AsyncTask class because finish method is from Activity class instead of from Context .so try it as:
public class Networkasynctask extends AsyncTask<...>{
Activity activity;
Context context;
public Networkasynctask(Context context,Activity activity,...){
this.activity=activity;
.....
}
....
}
and pass Activity instance as from Activity:
Networkasynctask networktaskobj=new Networkasynctask(this);
now use Networkasynctask.this.activity.finish() for closing Activity from non Activity class
I'm confused on this. Just started android and have a long form that needs multiple activities to bring together an object. I would like to pass the object from activity to activity to build it. After reading the many posts and blogs and the Android Dev pages, it seems for non-persistent data, the best bet is to subclass application or create a singleton. I reviewed this post openFileOutput not working properly inside a singleton class - ideas/workarounds? and now my question is this, Why doesn't a singleton ever get recycled? If we createSingleton() in Activity A, then move to Activity B and we are never passing a reference to the singleton, how does the garbage recycler know that we are going to come back to it again? it seems to me that when Activity A is recycled and we have moved to Activity B that the singleton would die..
If we look at the following singleton..
public final class SomeSingleton implements Cloneable {
private static final String TAG = "SomeSingleton";
private static SomeSingleton someSingleton ;
private static Context mContext;
/**
* I'm private because I'm a singleton, call getInstance()
* #param context
*/
private SomeSingleton(){
// Empty
}
public static synchronized SomeSingleton getInstance(Context context){
if(someSingleton == null){
someSingleton = new SomeSingleton();
}
mContext = context.getApplicationContext();
return someSingleton;
}
public void playSomething(){
// Do whatever
mContext.openFileOutput("somefile", MODE_PRIVATE); // etc...
}
public Object clone() throws CloneNotSupportedException {
throw new CloneNotSupportedException("I'm a singleton!");
}
}
And we create an instance of it through getInstance(), the class places a single instance of the class into static field, someSingleton. Why is this instance never recycled? If the answer is, "Static fields are never recycled?" What keeps us from using up all of our memory if we have many of them? Simple design considerations? This seems risky if we are using lots of contributed libraries that we have no idea how many static fields are out there. I just have this feeling that there is some fundamental rule that I am missing in OOP as a newb.
The general pattern is to put a reference to your singleton class in a static field. Static fields are not tied to a particular instance so they stick around until the JVM process is alive. It doesn't really matter how many activities access it. If you need to 'recycle' the singleton, maye you don't really need to use a singleton? Or provide an explicit close()/open() etc. methods.
I think the reason that your Singleton's are not getting recycled is because activities in Android aren't destroyed when you think they are.
You pose a question like 'what happens when we move from activity A to B'. But when you do that in Android, Activity A is very rarely destroyed. It usually just goes into the onPause() state. Thus, your Activity A will still be (mostly) intact if and when a user decided to hit the back button enough times to get back to activity A.