This Handler class should be static or leaks may occur (null)
Is the 'class' this message referring to 'MyActivity' here, since Handler is an object and i did declare it static. Should I ignore it or there something I should add, like 'static' somewhere in the 'MyActivity' declaration (I tried this and got errors). I notice 'WeakReference' is often suggested for this lint warning.
public class MyActivity extends Activity{
...
static Handler handler;
...
handler = new Handler()
{
public void handleMessage(Message msg) {
since Handler is an object and i did declare it static
You declared the data member to be static. However, you are using anonymous inner class, and therefore your subclass of Handler is not static.
Instead of:
handler = new Handler() {
public void handleMessage(Message msg) {
// do cool stuff
}
};
use:
handler=new MyVeryOwnHandler();
where MyVeryOwnHandler is either a regular Java class or a static inner class:
private static class MyVeryOwnHandler extends Handler {
public void handleMessage(Message msg) {
// do cool stuff
}
};
Note that the error is that the class needs to be static; it does not say that the object needs to be static.
To avoid leaks I also migrated to a static nested class. The explanation from Android Studio says this, it might help:
Since this Handler is declared as an inner class, it may prevent the
outer class from being garbage collected. If the Handler is using a
Looper or MessageQueue for a thread other than the main thread, then
there is no issue. If the Handler is using the Looper or MessageQueue
of the main thread, you need to fix your Handler declaration, as
follows: Declare the Handler as a static class; In the outer class,
instantiate a WeakReference to the outer class and pass this object to
your Handler when you instantiate the Handler; Make all references to
members of the outer class using the WeakReference object.
Before migration I used the implementation of a Looper thread as suggested by developer.android.com , which in the end lead to the warning. *scratch
class LooperThread extends Thread {
public Handler mHandler;
public void run() {
Looper.prepare();
mHandler = new Handler() {
public void handleMessage(Message msg) {
// process incoming messages here
}
};
Looper.loop();
}
}
Related
When using a background thread to do work, typically you'd update the UI through a Handler.
One way to do this was defining a handler at the class level as outlined in this answer, and this answer
final Handler handler = new Handler(){
#Override
public void handleMessage(Message msg) {
//update UI or call class methods here
}
};
However, this construct would result in the following warning
Handler class should be static otherwise memory leaks might occur
Another way to do this was to use a static inner class as outlined in this answer, and this answer
static class MyHandler extends Handler {
private final WeakReference<Type> myWeakReference;
MyHandler(Type reference) {
myWeakReference = new WeakReference<Type>(reference);
}
#Override
public void handleMessage(Message msg)
{
//Update UI or call class methods here using weak reference
}
}
However, this form of constructor has been Deprecated according to the Android docs.
public Handler ()
This constructor is deprecated. Implicitly choosing a Looper during
Handler construction can lead to bugs where operations are silently
lost (if the Handler is not expecting new tasks and quits), crashes
(if a handler is sometimes created on a thread without a Looper
active), or race conditions, where the thread a handler is associated
with is not what the author anticipated. Instead, use an Executor or
specify the Looper explicitly, using Looper#getMainLooper, {link
android.view.View#getHandler}, or similar. If the implicit thread
local behavior is required for compatibility, use new
Handler(Looper.myLooper()) to make it clear to readers.
How should updating the UI from a Handler be done currently, and should a Handler still be used for this purpose.
As you stated the docs, it says to use Looper.getMainLooper(), just change your code to:
MyHandler(Type reference) {
super(Looper.getMainLooper());
myWeakReference = new WeakReference<Type>(reference);
}
to update the UI from the main/UI thread.
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.
I know handler declared in a class may leak memory since it holds a reference to its outer class. In this case, we should use static nested class with weak reference.
But What if a handler is declared inside a method. I faced below case and not sure is it a correct implementation. Could someone please explain or give me a hint? I even don't know what I should search for.
private void methodA(){
Handler handler = new Handler();
handler.postDelayed(new Runnable() {
#Override
public void run() {
methodB();
}
}, 10*1000);
private void methodB(){
//textView holds a reference to a activity
textView.setText("hello");
}
It can under certain conditions. If the runnable passed is an anonymous or inner class, as in your example, it holds an implicit reference to 'this' and prevents 'this' from being garbage collected until the runnable is processed off the queue (so if your method never runs, like if your handler thread gets stopped without clearing the queue, it will leak).
In the case where you are worried about the conditions for a memory leak occurring or hanging onto objects too long, then you need to make your runnable a static class that has a weak reference initialized in the constructor, something like:
private static MyRunnable implements Runnable
{
private final WeakReference<MyClass> myClass_weakRef;
public MyRunnable(MyClass myClassInstance)
{
myClass_weakRef = new WeakReference(myClassInstance);
}
#Override
public void run()
{
MyClass myClass = myClass_weakRef.get();
if(myClass != null)
myClass.methodB();
}
}
private void MethodA()
{
Handler handler = new Handler();
handler.postDelayed(new MyRunnable(this), 10*1000);
}
Creating a Handler inside your method isn't a special case. It falls under the same circumstances, in that the Message you post will live in the message queue until it's processed.
I understand that the Handler may leak memory in Android, but I am not very sure what I did is correct:
public class MyActivity extends Activity
{
Handler handler;
HashMap<String, String> bigData = new HashMap<String, String>();
protected void onCreate(undle savedInstanceState) {
super.onCreate(savedInstanceState);
handler = new WeakHandler(){
public void handlerMessage(Message msg, Context conetxt)
{
// i think this usage is wrong, still leak, am I right?
Log.v("MyActivity", bigData.get("abc"))
}
}
};
static class WeakHandler extends Handler {
private final WeakReference<Context> ref;
public WeakHandler(Context t) {
super();
ref = new WeakReference<Context>(t);
}
#Override
public final void handleMessage(Message msg) {
final Context context = ref.get();
if (context != null) {
handleMessage(msg, context);
}
}
public void handlerMessage(Message msg, Context conetxt)
{
}
}
Also, If I just want to use a Handler to "post", "postDelayed", "removeCallbacks", I do not care about handleMessage, is that safe to just create a new Handler instance to do that, like this:
new Handler().postDelayed(some Runnable);
When you instantiate the WeakHandler, it won't be an actual WeakHandler class, but an Anonymous class which extends WeakHandler class. An Anonymous class is never static so it will have an implicit reference to the containing class (namely the MyActivity), besides the explicit WeakReference.
Since the onCreate() runs on the main thread, the Handler - that is created inside the onCreate() - will be associated with the main tread (actually the main Looper that is created when the Application starts). If you post a Message (or a Runnable, since the Runnable will be wrapped around with a Message) to the Handler, that Message/Runnable will have a reference to the Handler.
So given you postDelayed something with 5 mins delay. After destroying the Activity (eg. because of rotation), the main Looper will have a reference to the Message/Runnable, the Message/Runnable will have a reference to the Handler, Handler will have a reference to the Activity for 5 mins. This is a leak.
And yes, the "Log.v("MyActivity", bigData.get("abc"))" line leaks the activity as well.
Use MAT Analyzer to check for memory leaks. http://www.youtube.com/watch?v=_CruQY55HOk.
The talk is about memory leak.
My guess the reference to the context is causing leaks. Use getApplicationContext();
This context will live as long as your application is alive.
http://android-developers.blogspot.in/2009/01/avoiding-memory-leaks.html.
I making Service with Handler and I need Handler to use some Service methods. As Handler must be static, I can access Service methods without Service reference in Handler.
So I did this way:
private static class ServiceHandler extends Handler {
MyService service;
public ServiceHandler(MyService service) {
this.service = service;
}
#Override
public void handleMessage(Message msg) {
...
}
}
But also found that this is the right way to do the job:
private static class ServiceHandler extends Handler {
private final WeakReference<MyService> mMyService;
public ServiceHandler(MyService service) {
mMyService = new WeakReference<MyService>(service);
}
#Override
public void handleMessage(Message msg) {
MyService service = mMyService.get();
...
}
}
What is the difference in this two ways to use Service in Handler?
The first code example sets a member variable to a reference of the calling Service (which I assume is the outer class of this inner class). This is exactly the same as if you had removed the static keyword for the inner class, because now the inner class is holding a reference to an instance of the Service class (which is what you are usually trying to avoid when you use the static keyword).
The second code example uses a weak reference, which means that the garbage collector can clean up (destroy) the instance of the Service class, even though you are holding a reference to it in your Handler. If that occurs, the call to mMyService.get() will return null, so you had better check for that in your code.
In practice, there are no differences between these 2 code examples since you won't be using the Handler once the Service has been destroyed.