Message Handlers and the WeakReference issue - android

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!

Related

Can this code avoid the Android handler memory leak?

handler1 is a leak.
I want to convert handler1 code to handler2 code. Is that OK?
What is the difference between the two codes?
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// leaks!
Handler handler1 = new Handler()
{
#Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
Log.e("LOG", "Hello~1");
}
};
Handler handler2 = new Handler(new Handler.Callback() {
#Override
public boolean handleMessage(Message msg) {
Log.e("LOG", "Hello~2");
return false;
}
});
handler1.postDelayed(new Runnable() {
#Override
public void run() { }
}, 60000);
handler2.postDelayed(new Runnable() {
#Override
public void run() { }
}, 60000);
finish();
}
}
Why Leak Warning for handler1?
For the reason on leak warning, this article explains very well.
Quoting from the article
In Java, non-static inner and anonymous classes hold an implicit reference to their outer class. Static inner classes, on the other hand, do not.
So when you created handler1 by anonymous class, it will holds a reference to the MainActivity instance and MainActiviy can not be garbage collected.
Solution
Quoting from the article again
To fix the problem, subclass the Handler in a new file or use a static inner class instead. Static inner classes do not hold an implicit reference to their outer class, so the activity will not be leaked. If you need to invoke the outer activity’s methods from within the Handler, have the Handler hold a WeakReference to the activity so you don’t accidentally leak a context. To fix the memory leak that occurs when we instantiate the anonymous Runnable class, we make the variable a static field of the class (since static instances of anonymous classes do not hold an implicit reference to their outer class):
Following the article, update your code as follows:
public class MainActivity extends AppCompatActivity {
private static class MyHandler extends Handler {
#Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
Log.e("LOG", "Hello~1");
}
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Handler handler1 = new MyHandler();
handler1.postDelayed(new Runnable() {
#Override
public void run() { }
}, 60000);
finish();
}
}
Can handler2 solve the problem?
The answer from #Michael This Handler class should be static or leaks might occur: IncomingHandler
provides the solution.
Quoting from #Michael answer
As I understand it, this will not avoid the potential memory leak. Message objects hold a reference to the mIncomingHandler object which holds a reference the Handler.Callback object which holds a reference to the Service object. As long as there are messages in the Looper message queue, the Service will not be GC. However, it won't be a serious issue unless you have long delay messages in the message queue.
In your case, handler2 will hold a reference to Handler.Callback object.And since Handler.Callback is created by anonymous class, hence it will hold a reference to MainActiviy instance too. So MainActiviy instance can not be garbage collected also.
I try this code on emulator(android 28) and dump the memory, the profiler shows nothing to leak, I also try Leakcanary and shows same result. This makes me doubt the accuracy of the articles on the web. Then I noticed the difference,if I use Kotlin write this logic, the memory will not leak, but use the Java code will leak.
Later I found something interesting. In java, whether use outer class method or not,the anonymous inner class will hold outer object reference by constructor. In kotlin,if inner logic is not use outer class method,the anonymous inner class dose not hold the outer object reference.
I used Handler in Kotlin code that catches the incoming Bluetooth incoming message. This code has no lint leaks warnings:
private val incomingMsgHandler: Handler = Handler { msg ->
msg.obj?.let {
if (it is ByteArray) {
val msgStr = String(it)
setIncomingMessage(msgStr)
}
}
true
}
In short description, this uses Handler constructor with lambda callback.

Calling a non static method from a static Handler in Android

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();

Will this code from Android SDK sample cause a memory leak of the parent Activity?

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.

Which structure for my Android Application?

I did a lot of research, but I didn't get through it, so that i don't know how to realize my App. The App consists of 2+ Activities, that contain content, that should be updated by a service in the background. So I dont know how to do the connection, some say i should do ipc, but others say thats too much of work, as long as service and activites run within the same process. I concerned to easily create methods like ActivityOne.RefreshData(Data data) and call those within the service, but i did not manage to get it work until now. I hope you have some suggestions to me and sorry for my bad english!
cheers
If you only need to provide data/updates to your own activities then IPC is most certainly not needed.
To achieve this, I would reverse the orientation you seem to be describing and rather than have the service calling methods on the activity, have it pushing messages to a Handler provided to it by the Activity when/if it starts.
See:
http://developer.android.com/reference/android/os/Handler.html
http://mobileorchard.com/android-app-developmentthreading-part-1-handlers/
Note that if what you need to send from the service to activites is always the same type of object, you can simplify your implementation of handleMessage() by using the Message.obj field to hold your type and not bother with Bundles or parcelling. As in:
Handler impl in activity where NotificationModel is the type that the service always sends:
private Handler mNotificationListener = new Handler(){
#Override
public void handleMessage(Message msg) {
handleIncomingNotification((NotificationModel)msg.obj);
}
};
The service side of posting msgs to this handler looks like:
public class NotificationRouter {
private Application mContext;
private SparseArray<Handler> mListeners = new SparseArray<Handler>();
public NotificationRouter (Application app){
this.mContext = app;
}
public void registerListener(Handler handler){
mListeners.put(handler.hashCode(), handler);
}
public void unRegisterListener(Handler handler){
mListeners.remove(handler.hashCode());
}
public void post(NotificationModel notice){
Message m = new Message();
m.obj = notice;
for (int i = 0; i < mListeners.size(); i++){
Handler h = mListeners.valueAt(i);
h.sendMessage(m);
}
}
}

Handlers and memory leaks in Android

Please have a look at the code below:
public class MyGridFragment extends Fragment{
Handler myhandler = new Handler() {
#Override
public void handleMessage(Message message) {
switch (message.what) {
case 2:
ArrayList<HashMap<String,String>> theurls = (ArrayList<HashMap<String,String>>) message.obj;
urls.addAll(theurls);
theimageAdapter.notifyDataSetChanged();
dismissBusyDialog();
break;
}
}
}
}
When I use handler like this I get a warning "handler should be static, else it is prone to memory leaks." Can someone tell me what is the best way to do this?
I recently updated something similar in my own code. I just made the anonymous Handler class a protected inner class and the Lint warning went away. See if something like the below code will work for you:
public class MyGridFragment extends Fragment{
static class MyInnerHandler extends Handler{
WeakReference<MyGridFragment> mFrag;
MyInnerHandler(MyGridFragment aFragment) {
mFrag = new WeakReference<MyGridFragment>(aFragment);
}
#Override
public void handleMessage(Message message) {
MyGridFragment theFrag = mFrag.get();
switch (message.what) {
case 2:
ArrayList<HashMap<String,String>> theurls = (ArrayList<HashMap<String,String>>) message.obj;
theFrag.urls.addAll(theurls);
theFrag.theimageAdapter.notifyDataSetChanged();
theFrag.dismissBusyDialog();
break;
}//end switch
}
}
MyInnerHandler myHandler = new MyInnerHandler(this);
}
You may have to change where I put "theFrag." as I could only guess as to what those referenced.
Here's a somewhat useful little class I made that you can use. Sadly it's still quite verbose because you can't have anonymous static inner classes.
import java.lang.ref.WeakReference;
import android.os.Handler;
import android.os.Message;
/** A handler which keeps a weak reference to a fragment. According to
* Android's lint, references to Handlers can be kept around for a long
* time - longer than Fragments for example. So we should use handlers
* that don't have strong references to the things they are handling for.
*
* You can use this class to more or less forget about that requirement.
* Unfortunately you can have anonymous static inner classes, so it is a
* little more verbose.
*
* Example use:
*
* private static class MsgHandler extends WeakReferenceHandler<MyFragment>
* {
* public MsgHandler(MyFragment fragment) { super(fragment); }
*
* #Override
* public void handleMessage(MyFragment fragment, Message msg)
* {
* fragment.doStuff(msg.arg1);
* }
* }
*
* // ...
* MsgHandler handler = new MsgHandler(this);
*/
public abstract class WeakReferenceHandler<T> extends Handler
{
private WeakReference<T> mReference;
public WeakReferenceHandler(T reference)
{
mReference = new WeakReference<T>(reference);
}
#Override
public void handleMessage(Message msg)
{
if (mReference.get() == null)
return;
handleMessage(mReference.get(), msg);
}
protected abstract void handleMessage(T reference, Message msg);
}
Per the ADT 20 Changes, it looks like you should make it static.
New Lint Checks:
Check to make sure that Fragment classes are instantiatable. If you accidentally make a
fragment innerclass non-static, or forget to have a default constructor, you can hit runtime
errors when the system attempts to reinstantiate your fragment after a configuration change.
Look for handler leaks: This check makes sure that a handler inner class does not hold an
implicit reference to its outer class.
If you read docs about AccountManager or PendingIntent, you will see that some methods take Handler as one of arguments.
For example:
onFinished - The object to call back on when the send has completed, or null for no callback.
handler - Handler identifying the thread on which the callback should happen. If null, the callback will happen from the thread pool of the process.
Imagine the situation. Some Activity calls PendingIntent.send(...) and put the non-static inner subclass of Handler. And then activity is destroyed. But inner class lives.
Inner class still holds a link to destroyed activity, it cannot be garbage-collected.
If you're not planning to send your handler to such methods, you have nothing to worry about.
I run into the same issue and I find that it is one of this topics with many questions and few answeres. My solution is simple and I hope it can help someone:
/* BEFORE */
private Handler mHandler= new Handler() {
#Override public void handleMessage(Message msg) {
this.doSomething();
};
};
We can create a static Handler subclass that simply runs a Runnable. The actual handler instance will know what to do through the runnable that will have access to instance variables.
/* AFTER */
static class RunnableHandler extends Handler {
private Runnable mRunnable;
public RunnableHandler(Runnable runnable) {
mRunnable = runnable;
}
#Override public void handleMessage(Message msg) {
mRunnable.run();
};
}
private RunnableHandler mHandler = new RunnableHandler(new Runnable() {
#Override public void run() {
this.doSomething();
} });
The warning is gone while the funcionality is the same.
A simple solution for this case might be:
Handler handler=new Handler(new Handler.Callback() {
#Override
public boolean handleMessage(Message message) {
//do your stuff here
return false;
} });

Categories

Resources