I want to understand when a memory leak happens. For instance if i run this runnable in the activity, all the activity's context will be capture and if a rotation happens, the activity wont get released until the runnable terminates.
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
executors.diskIO().execute(new Runnable() {
#Override
public void run() {
//CODE HERE
});
}
});
}
}
Lets say i put the runnable inside a class in a seperate file not within the MainActivity and initiate it from the activity. When a rotation happens, is there a memory leak in this case?. I mean the runnable captures the data in every rotation right?
public class A{
Data ....
public A() {}
functionB(){
executors.diskIO().execute(new Runnable() {
#Override
public void run() { }
});
});
}
}
Whenever you make an innerclass, it retains the reference of the outer class. If your runnable is inside an activity it will retain an instance to the activity and hence will result in memory leak whereas if you put it in class A it will hold reference of class A not of your activity
If you don't want to access members of the enclosing class it is preferable to make your class static as it wont hold the object of enclosing class.
Related
I have a question about memory leak.I have two classes.
The first one is:
public class Utility {
private static Utility instance = null;
private UpdateListener listener;
//Make it a Singleton class
private Utility(){}
public static Utility getInstance() {
if (instance == null)
instance = new Utility();
return instance;
}
public void setListener(UpdateListener listener) {
this.listener = listener;
}
//Long running background thread
public void startNewTread() {
new Thread (new Runnable() {
#Override
public void run() {
try {
Thread.sleep(1000 * 10);
if (listener != null)
listener.onUpdate();
} catch (InterruptedException e) {
Log.d("Utility", e.getMessage());
}
}
}).start();
}
//Listener interface
public interface UpdateListener {
public void onUpdate();
}
}
Thesecond class is:
public class ListenerLeak extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//Setting the listener
Utility.getInstance().setListener(new Utility.UpdateListener() {
#Override
public void onUpdate() {
Log.d("ListenerLeak", "Something is updated!");
}
});
//Starting a background thread
Utility.getInstance().startNewTread();
}
#Override
protected void onDestroy() {
super.onDestroy();
}
}
in this activity.May new Utility.UpdateListener create a memory leak?
when the activity destoroyed , only Updatelistener can be alive.does activity can be alive?
Create an inner class inside a Utility class like below. Then move the thread to that class.
public void startNewTread() {
new MyThread().start();
}
private static class MyThread extends Thread {
#Override
public void run() {
try {
Thread.sleep(1000 * 10);
if (listener != null)
listener.onUpdate();
} catch (InterruptedException e) {
Log.d("Utility", e.getMessage());
}
}
}
Reason: After each configuration change, the Android system creates a new Activity and leaves the old one behind to be garbage collected. However, the thread holds an implicit reference to the old Activity and prevents it from ever being reclaimed. As a result, each new Activity is leaked and all resources associated with them are never able to be reclaimed. https://www.androiddesignpatterns.com/2013/04/activitys-threads-memory-leaks.html will help to understand it.
This is probably a bit late, and others have had their input as well, but I'd like to have my shot as well :). Memory leak simply means that GC is not able to release a memory used by an instance of an object because it can't be sure that whether it is being used or not.
And in your case, simply put: The Utility class is defined as Singletone, It has a static instance of itself in the class. So It will be there as long as the application is alive. When you set a listener from the activity using setListener() function you are passing an instance created in the activity to it that has a limited lifecycle and is bound to activity's lifecycle. So one can say that the static Utility class can outlive the listener instance passed to utility and leak the activity. So no matter if you're using thread or not, This leaks the activity instance because it can outlive the listener instance which has an implicit reference to parent activity class.
How to prevent leaks here?
I think using a WeakReference for the listener is a good starting point, Also making sure to release or remove the listener as soon as the onDestroy() method of activity is called. but as documentations state, there's no guaranty that onDestroy() is always called. So in my opinion going with something like onPause() or onStop() is a better idea.
I have this code example below and i want to make sure that no memory will happen. By passing the whole activity to an async task will lead to a memory leak if the task itself will have a bigger lifecycle than activity's. But if i declare a weakReference, means that if a rotation is about to happen, the activity will be destroyed without memory leak. Am right guys? Do i have to set something to null also?
Main .java
public class Main extends Activity {
private TextView mMessageView;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView();
new Background(this /*getBaseContext()*/ ).execute();
}
}
Background.java
public class Background extends AsyncTask<String, Void, String>
{
private WeakReference activity;
public void Background(Activity act)
{
activity = new WeakReference(act);
}
#Override
protected String doInBackground(String... params)
{
}
#Override
protected void onPostExecute(String result)
{
if(activity.get()!=null)
//ToDo
}
}
There is no need for explicit nulling when using WeakReference. Regarding the use of AsyncTask inside an Activity, just be careful not to create an anonymous instance because it will have an implicit reference to the enclosing class which can lead to memory leaks.
The code you posted seems ok, don't forget to terminate the AsyncTask gracefully when the activity is recreated.
I have an Activity which implements a listener. My concern is that the
activity can get re-created and the callback will then have a reference
to an object that is null.
This means we must update the controller with a new reference that references
the newly created activity.
What pattern is best to use even if the callbacks are async?
Is there perhaps a safe way to update the controllers reference in a thread > safe way.
OR
Should one rather use a Headless fragment and use the onAttach method get the
updated reference.
OR
Should one rather not use these patterns and use a Handler for
all your callbacks?
I suspect that my updateListener method will not work in all cases e.g.
1) init is busy and is just about to call the callback, line marked with
*10*
2) the activity gets recreated and updates the controller with
a new reference but the updateListener method is blocked because the callback is about to take place.
3) the callback executes and fails as the listener reference variable is stale.
public class Controller {
UserActionListener listener
static Controller instance;
public static synchronized Controller getInstance(UserActionListener listener) {
if (instance == null) {
instance = new Controller();
}
this.listener = listener;
return instance;
}
private Controller() {
//empty, enforce getInstance
}
private init() {
// do some very long running operation in a separate thread.
//.... on completion we update the UI
synchronized(Controller.class) {
/*10*/ listener.handle("SHOW DIALOG");
}
}
public void updateListener(UserActionListener listener) {
synchronized(Controller.class) {
this.listener = listener;
}
}
public class MainActivity extends AppCompatActivity implements UserActionListener {
static Controller controller;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
controller = Controller.getInstance(this);
if (savedInstanceState == null) {
//do not run on re-create
controller.init();
}
}
#Override
protected void onPostResume() {
super.onPostResume();
controller.updateListener(this);
}
#Override
public void handleAction(String userAction) {
switch (userAction) {
case "SHOW DIALOG" :
Toast.makeText(getActivity(),"Hello",Toast.LENGTH_LONG).show();
}
the direct answer to your question is a simple subscription pattern.
on the activity you call:
#Override
public void onStart(){
controller.updateListener(this);
}
#Override
public void onStop(){
controller.updateListener(null);
}
and inside the controller check for null before calling anything on the listener.
But there's a fundamental flaw on the logic.
With the following code:
static Controller controller;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
controller = new Controller(this);
}
the static controller having a reference to the activity is leaking the activity, avoiding it to be garbage collected.
also, even thou the controller is static, you're creating a new one every time the activity is created, also inside the controller init() you have the following:
// do some very long running operation
//....
that means this very long running operation is:
running in the UI thread. This will block your app initialisation, the user will think it's broken and the system will probably show a message to the user asking to close it.
there's nothing to guarantee that your process won't be killed either by the user or by the system before the "very long operation" finishes. If you want to run a long operation you MUST user a Service instead.
Very sample, Use WeakReference to activity
INTRODUCTION
I have a sub-class inside my main activity's class, which extends thread and is started every time the camera detectecs movement.
Inside this thread, when it dectects movement continuosly, it must start another thread which belongs to the main Activity's class.
I now it can be a bit messy but i'l explain it now in detail
CODE
This is a simplified version of my code that shows exactly what I mean:
public class MainActivity extends Activity {
//...
public Runnable SpeechWhenMotion = new Runnable() {
#Override
public void run() {
// Do stuff here
}
}
private static final class DetectionThread extends Thread {
//...
#Override
public void run() {
//...
//START "SpeechWhenMotion" HERE!
}
}
}
QUESTION
So the doubt I have is, how do I start the Runnable inside the thread of the DetectionThread class?
I've tryed using a handler but I think I'm not doing it right cause it doesn't get started.
If you really need SpeechWhenMotion runnable to be nester class of MainActivity you need to provide link of MainActivity or SpeechWhenMotion instance to DetectionThread class:
private static final class DetectionThread extends Thread {
private Runnable mSpeechWhenMotionRunnable;
//...
}
then, when you create DetectionThread assign SpeechWhenMotion to it from main activity
DetectionThread detectionThread = new DetectionThread();
detectionThread.mSpeechWhenMotionRunnable = SpeechWhenMotion;
And finally, call start new thread inside DetectionThread:
//START "SpeechWhenMotion" HERE!
new Thread(mSpeechWhenMotionRunnable).start();
I tried it out and this works rather smoothly:
new Thread(SpeechWhenMotion).start();
I have an Activity in whose onCreate() method i call a Utility function.
This utility functions requires a callback class instance as a parameter, in which it returns the info that i need. this is:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Utility.functionA(new functionACallBack() {
/**
*
*/
private static final long serialVersionUID = -7896922737679366614L;
#Override
public void onResponse(String error) {
((MyActivity) AppClass.getAppContext()).finish();
}
});
}
Once I have obtained that info, I want to close the activity. so i called finish() from inside the anonymous class that i created for the callback.
But the activity is not getting finished. I thought maybe i need to call finish() from UI thread so i did runOnUiThread(), in inside it also i tried calling finish(). But it just doesn't work.
Could someone please help me with this issue?
UPDATE:
I am storing APP context and then trying to use that but to no avail.
public class AppClass extends Application {
private static Context mContext;
#Override
public void onCreate() {
super.onCreate();
AppClass.mContext = getApplicationContext();
}
public static Context getAppContext(){
return AppClass.mContext;
}
}
Simply call something like this:
#Override
public void onResponse(String error) {
((Activity) context).finish();
}
As this is a static function, you'll have to be able to access your Context in a static way. You can save that as a Class variable, but you'll have to be aware about its handling as it might lead to memory leaks.
To avoid them, you can declare a class that extends Application and save here your context, so this way you won't ever have a memory leak.
Try using this code:
((Activity) ActivityClass.this).finish();
Remember, use the Activity class, not the Application one.