I've been googling this day and I can't find a simple solution that answers my question. I have the Lint warning "This Handler class should be static or leaks might occur" So I follow the logic as proposed by Romain Guy and many others and constructed my Handler as follows..
public static class MyHandler extends Handler {
private final WeakReference<CustomTrophyCreateActivity> mActivity;
MyHandler(CustomTrophyCreateActivity activity) {
mActivity = new WeakReference<CustomTrophyCreateActivity>(activity);
}
#Override
public void handleMessage(Message msg) {
if (mActivity != null) {
Activity activity = mActivity.get();
if (activity != null) {
// Call non static method in enclosing activity.. CANT DO THIS
startUploadPhoto();
}
}
}
So the basic question is.. how to I call this non-static method from a Handler that has been made static to avoid memory leaks??
I really would like to avoid making startUploadPhoto() static, because 1) It doesn't need to be and 2) It would require a lot of work changing variables. I fully understand why this lint warning is popping up.. I just can't seem to find a simple solution to rid myself of it. Thank you.
You need to use your reference to invoke the method, not the the implicit enclosing class. Try activity.startUploadPhoto();
Related
I'm using a weak reference inside a static Handler to avoid memory leaks, however, sometimes this reference is being nullified, I cannot understand why.
The static handler is defined inside a repository class that has a method to perform an operation in the background, receives a callback to notify the caller when it's done:
public class MyRepository {
public void performOperation(ContentResolver cr, RepositoryCallback callback) {
MyHandler handler = new MyHandler(cr, callback);
handler.startQuery(...)
}
interface RepositoryCallback {
void onSuccess(MyModel model);
}
// Handler class code here
}
The code of the handler is the following:
private static class MyHandler extends AsyncQueryHandler {
private final WeakReference<RepositoryCallback> weakCallback;
public MyHandler(ContentResolver cr, RepositoryCallback callback) {
super(cr);
this.weakCallback = new WeakReference<>(callback);
}
#Override
protected void onQueryComplete(int token, Object cookie, Cursor cursor) {
RepositoryCallback callback = this.weakCallback.get();
if (callback != null) { // --> Here sometimes it is null
// Do some stuff with the cursor to create MyModel
callback.onSuccess(model);
}
}
}
For some reason, this.weakCallback.get() sometimes is null, and I'm trying to understand why.
The activity code looks like this:
public class MyActivity extends AppCompatActivity {
public void loadModel() {
showLoadingView();
myRepository.performOperation(context.getContentResolver(), new RepositoryCallback() {
#Override
public void onSuccess(MyModel model) {
hideLoadingView();
// Do something with model
}
});
}
}
As you can see I'm creating an anonymous class for the callback, but nobody is holding a reference to it.
Is this the cause of the weak reference being nullified?
Thanks.
That's the "classical" bug associated with weak references.
If the Observable holds the only reference to the Observer, and this reference is weak, then it can be cleared and Observer be garbage collected.
Since you're using anonymous class, Observable will hold the only reference to it, therefore it will be cleared.
As a side note - in my entire experience of Android development, whenever I saw devs using weak references, it always was a code smell. Usually it indicates that either devs don't understand how weak references work, or they don't trust their own code.
A good rule of thumb is that you should never use weak references.
EDIT:
I think that Handler is an anti-pattern in general. You can read more about this in this Reddit thread. There is also a thread there in which I helped one dev to see how he can get rid of HandlerThread in his codebase.
On the other hand, Jake Wharton disagreed with my statements.
Take what you'd like from there, but, in general, I would say that having a static Handler is anti-pattern for sure.
If you are worried about AndroidStudion warnings, then just remember that Google are responsible for AsyncTask and Loaders. This warning is not just useless, but actually bad. They should've made it you should not use static Hadlers.
If all you need is to offload work to BG thread and then get a callback on UI thread then you would be much better off with something like RxJava. Or even the evil AsyncTask.
I guess you're using AsyncQueryHandler in order to access ContentProvider. This is too a very controversial approach. If you don't need to share data with other apps, you might be better off by using some ORM that handles the multithreading for you.
So from reading/research about memory leaks it suggests to make all inner classes static to avoid memory leaks. However, by looking at the SDK samples (specifically TicTacToeLib) they implement their callbacks without the use of static inner classes. Will this cause a memory leak? If not, why?
private Handler mHandler = new Handler(new MyHandlerCallback());
private class MyHandlerCallback implements Callback {
public boolean handleMessage(Message msg) {
if (msg.what == MSG_COMPUTER_TURN) {
// Pick a non-used cell at random. That's about all the AI you need for this game.
State[] data = mGameView.getData();
int used = 0;
while (used != 0x1F) {
int index = mRnd.nextInt(9);
if (((used >> index) & 1) == 0) {
used |= 1 << index;
if (data[index] == State.EMPTY) {
mGameView.setCell(index, mGameView.getCurrentPlayer());
break;
}
}
}
finishTurn();
return true;
}
return false;
}
}
Yes, this sample will cause a leak in case it keeps a Message in the queue. But it's not a very severe leak since it is usually limited to a rather short amount of time.
But there is a rather simple way to prevent the leak:
Put the following two classes into your project
/** Callback that decouples the wrapped Callback via WeakReference */
public class SafeCallback implements Handler.Callback {
private final WeakReference<Handler.Callback> mCallback;
public SafeCallback(Handler.Callback callback) {
mCallback = new WeakReference<Handler.Callback>(callback);
}
#Override
public boolean handleMessage(Message msg) {
Handler.Callback callback = mCallback.get();
if (callback != null)
return callback.handleMessage(msg);
// else warn, return true, ..?
return false;
}
}
/** replacement for anonymous inner Handler implementations */
public abstract class SafeHandler implements Handler.Callback {
#Override
public abstract boolean handleMessage(Message msg);
public final Handler get() {
return new Handler(new SafeCallback(this));
}
public final Handler get(Looper looper) {
return new Handler(looper, new SafeCallback(this));
}
}
And now you can use Handler / Callback almost as you used to do but it's no longer leaking.
So either like
public class TestActivity extends Activity {
private Handler mHandler;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mHandler = new SafeHandler() { // << instead of new Handler() {
#Override
public boolean handleMessage(Message msg) {
// handle message
return false;
}
}.get(); // << Notice this added .get()
}
}
or like
public class TestActivity2 extends Activity implements Handler.Callback {
private Handler mHandler;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mHandler = new Handler(new SafeCallback(this)); // << wrapped in SafeCallback
}
#Override
public boolean handleMessage(Message msg) {
// handle message
return false;
}
}
The leak problem with Handler is that each Message / Runnable (which is actually wrapped in a Message) knows it's target, i.e. has a hard reference to the Handler or Callback. And if that target is a non-static inner class, it will have an implicit hard reference to the outer class which is typically an Activity.
That means that as long as there are Messages enqueued for your Handler, your whole Activity can't be garbage collected.
To solve this issue that chain of hard references from Message to Activity has to be broken. The SafeCallback class does exactly that by keeping just a WeakReference towards your Activity.
That means, the Message has now a hard reference to SafeCallback but the part bind there can now be garbage collected. In case that happens Handler.Callback callback = mCallback.get(); will turn out null and the Message is simply discarded. There is no more useful target anyways. It is still leaking the SafeCallback itself but that's a pretty much empty class so it won't lead to problems.
I would approach it from the standpoint of what use case are you trying to solve, and not what the language itself is doing. If you "nested class" (not inner class because inner classes can't be static) needs to be able to call non-static methods on its parent class, or read non-static members, then you don't have much choice but to make it non-static. If you can get away with not accessing any of the parent class's non-static resources, then by all means do so (you'll save some memory that way anyways). However, if you're worried about memory leaks and you're going to make the nested class private as in your example, then you really shouldn't have anything to worry about because instances of that class can only be created locally to the parent class (unless you create a static member of the parent class that holds a reference to an instance of the nested class, in which case that object would be around until the parent class gets unloaded by the VM).
In summary, I wouldn't personally worry too much about whether or not your nested class is declared as static or non-static, but focus more on the lifecycle of instances of that class, if you're worried about leaking memory.
The following message Handler works fine receiving messages from my service...
private Handler handler = new Handler()
{
public void handleMessage(Message message)
{
Object path = message.obj;
if (message.arg1 == 5 && path != null) //5 means its a single mapleg to plot on the map
{
String myString = (String) message.obj;
Gson gson = new Gson();
MapPlot mapleg = gson.fromJson(myString, MapPlot.class);
myMapView.getOverlays().add(new DirectionPathOverlay(mapleg.fromPoint, mapleg.toPoint));
mc.animateTo(mapleg.toPoint);
}
else
{
if (message.arg1 == RESULT_OK && path != null)
{
Toast.makeText(PSActivity.this, "Service Started" + path.toString(), Toast.LENGTH_LONG).show();
}
else
{
Toast.makeText(PSActivity.this,"Service error" + String.valueOf(message.arg1), Toast.LENGTH_LONG).show();
}
}
};
};
However, even though it tests out alright in the AVD (I'm feeding it a large KML file via DDMS) the "object path = message.obj;" line has a WARNING saying "this Handler class should be static else leaks might occur".
But if I say "static Handler handler = new Handler()" it won't compile complaining that I "cannot make a static reference to a non-static field myMapView. If I can't make such references, I can't do anything useful.
This led me into several hours of googling around on this issue and learning more about weakReferences than I ever wanted to know. The often found reccomendation I find is that I should replace...
private Handler handler = new Handler()
with
static class handler extends Handler
{
private final WeakReference<PSActivity> mTarget;
handler(PSActivity target)
{
mTarget = new WeakReference<PSActivity>(target);
}
But this won't compile still complaining that I can't make a static reference to a non-dtatic field. So, my question a week or to ago was "how can I write a message handler for android so my service can send data to my activity. Even though I have working code, the question still stands with the suffix "without leaking memory".
Thanks, Gary
I got the same warning message when I tried to use handler in a Service, and finally resolved it by taking the advice from this thread, see the code snippet from my project.
public class MyService extends Service {
...
private MyHandler mHandler;
public static class MyHandler extends Handler {
private final WeakReference<MyService> mService;
MyHandler(MyService service) {
mService = new WeakReference<MyService>(service);
}
#Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
MyService service = mService.get();
if (service!=null) {
if (msg.what==MSG_RESUME_CHECKING) {
service.pause();
} else if (msg.what==MSG_PAUSE_CHECKING) {
service.resume();
}
}
}
}
...
#Override
public void onCreate() {
super.onCreate();
...
mHandler = new MyHandler(this);
...
}
}
I know I'm a little late to the party, but hopefully this helps further answer the question for future inquirers.
As you discovered through your Googling (something I've done quite a bit of myself to solve a similar issue) you need to turn your Handler instance into a static inner class (nested class) which takes the target Activity in its constructor. It then converts this Activity reference into a WeakReference and that is what can be used to interact with things in your target Activity. In your case:
Toast.makeText(mTarget.get().this, "Service Started" + path.toString(), Toast.LENGTH_LONG).show();
Since you're changing to a nested class you'll also need to create an instance of that class for your Thread to access in its run() method. For more help on this (as well as on how to make sure your app works even after configuration changes) see this question.
Hope this helps!
I am trying to send the events from one Java class to Activity.
Scenario is, Will be having some data in the native, native will call the callback function which is in java code, This class processes data, after the processing i need to update the UI. I want to update the UI at one place in the Activity handler. (Dont want to use runOnUiThread() everywhere).
I was not able to send the events properly with the below approaches.
1st Approach:
1) Define functions for posting messages in to the queue and call these functions.
2) To call the above mentioned functions (point 1) we need context, if i maintain the static variable for maintaining the context and returning it, if the activity is created twice we wont able to get the write context for the first activity.
public class Activity1 {
protected static Context myContext = null;
protected Handler myHandler = null;
#override
public void onCreate() {
myContext = this;
myHandler = new Handler();
}
public static Context getMyContext() {
return myContext;
}
public void postEvent1() {
myHandler.sendMessage();
}
}
2nd Approach:
1) Making the handler as a static variable and returning this with the help of static function. - Not a good design to expose the internal variables.
2) Cons will be like above, when a second activity is created.
public class Activity1 {
protected static Handler myHandler = null;
#override
public void onCreate() {
myHandler = new Handler();
}
public static Context getMyHandler() {
return myHandler;
}
}
Is it possible to get the activity context without using the static variables and static functions?
Please share the knowledge if anyone knows. :)
Thanks & Regards,
SSuman185
I used a container class HashMap for storing the contexts with the key.
I used the name of the class as the key.
When the second activity is trying to register with class containing hashmap, it will reply with context of the already stored activity (null if not).
So like this I am able to store the contexts' of the classes and avoid loosing of the first activity context if I am creating the second one.
Please add if any one gets better solution.
I am newbie to both Java and Android, and currently I am confused about "memory leak" in Android, for example: I have 01 Class, 01 Activity and 01 Interface as following:
Class BackGroundWorker is a singleton, which lives as long as the application lives:
public class BackGroundWorker {
private IOnEventOccurListener listener = null;
private static BackGroundWorker instance;
// ....
public void setListener(IOnEventOccurListener pListener) {
this.listener = pListener;
}
// ....
public static BackGroundWorker getInstance() {
//...
return instance;
}
}
The Listener Interface:
public interface IOnEventOccurListener {
public void onEventOccur();
}
And the Listener itself (An activity):
public class ShowSomething extends Activity implements IOnEventOccurListener{
BackGroundWorker bgWorker;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
bgWorker = BackGroundWorker.getInstance();
bgWorker.setListener(this);
}
#Override
public void onEventOccur() {
// TODO do something
}
}
Now, according to what Romain Guy mentioned here:
It’s a memory leak, because there’s a reference to the listener (Activity). So Java GC cannot collect the Activity, even when it’s not in use.
I was able to solve that problem by WeakReference – but still wonder:
In this case, when the device needs more memory, according to Android Dev document, it will “kill” the activity if needed - assuming that the Activity ShowSomething is “killed” – then what happens ? (It’s still leak according to Romain Guy, and still “killed” )
I am really confused. Could anybody please explain this ?
Thank you in advanced,
Son
Android will destroy activities that are not on the screen to try to free up memory. However, GC rules still apply, and hence your static reference to the activity prevents the memory from being freed.
Eventually, Android will terminate the whole process. At that point, your leaked memory will be freed. However, in between your activity being destroyed and the process being terminated, you are wasting RAM.
Rather than use WeakReference, please null out the static reference when the activity is destroyed.
I think it would have the leak if we will use:
static BackGroundWorker bgWorker;
instead:
BackGroundWorker bgWorker;