I read the article How to Leak a Context: Handlers & Inner Classes, and now I have got a question. If I pass mHandler as a parameter to another thread to send messages from that thread to the main thread, will it cause memory leaks?
SampleActivity
public class SampleActivity extends Activity {
/**
* Instances of static inner classes do not hold an implicit reference to
* their outer class.
*/
private static class MyHandler extends Handler {
private final WeakReference<SampleActivity> mActivity;
public MyHandler(SampleActivity activity) {
mActivity = new WeakReference<SampleActivity>(activity);
}
#Override
public void handleMessage(Message msg) {
SampleActivity activity = mActivity.get();
if (activity != null) {
// ...
}
}
}
private final MyHandler mHandler = new MyHandler(this);
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Run a thread (authentication, synchronization, etc.)
// Later the user might press the Home button, the Back button, or make a call
new MyThread(mHandler).start();
}
}
MyThread
public class MyThread extends Thread {
private final Handler handler;
public MyThread(Handler handler) {
this.handler = handler;
}
#Override
public void run() {
// A long operation
// I'm done
handler.sendEmptyMessage(1);
}
}
If MyThread is a static or external class, it can't cause leaks. There is no any non-weak references to activity.
It looks ok. I don't see any object holding link to Activity (WeakRef is ok as it can be GCed). I don't see potential leaks here ;)
Related
I know that if you initialize a Handler directly and use it in an Activity it will leak Context (courtesy of Alex Lockwood), for e.g.
public class SampleActivity extends Activity {
private final Handler mLeakyHandler = new Handler() {
#Override
public void handleMessage(Message msg) {
/* ... */
}
}
}
So I used the following approach
private static class StaticHandler extends Handler {
private final WeakReference<SampleActivity> mSampleAct;
private StaticHandler(SampleActivity act) {
mSampleAct = new WeakReference<SampleActivity>(
act);
}
}
Then I initialize it as following
public class SampleActivity extends Activity {
private Handler myHandler = new StaticHandler(this) {
#Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
processTask(msg.obj);
}
};
}
I wanted to know if my Handler i.e. myHandler will still leak Context, how can I be sure it doesn't?
Using a WeakReference is definitely a smart way to avoid memory leaks. The code looks good to me out of context, but the best way to ensure that it will not leak memory is to use the DDMS memory analyzer. Read more: Memory Analyzer Tool in android?
I'm developing an Android 2.3.3 application with a service. I have this inside that service to communicate with Main activity:
public class UDPListenerService extends Service
{
private static final String TAG = "UDPListenerService";
//private ThreadGroup myThreads = new ThreadGroup("UDPListenerServiceWorker");
private UDPListenerThread myThread;
/**
* Handler to communicate from WorkerThread to service.
*/
private Handler mServiceHandler;
// Used to receive messages from the Activity
final Messenger inMessenger = new Messenger(new IncomingHandler());
// Use to send message to the Activity
private Messenger outMessenger;
class IncomingHandler extends Handler
{
#Override
public void handleMessage(Message msg)
{
}
}
/**
* Target we publish for clients to send messages to Incoming Handler.
*/
final Messenger mMessenger = new Messenger(new IncomingHandler());
[ ... ]
}
And here, final Messenger mMessenger = new Messenger(new IncomingHandler());, I get the following Lint warning:
This Handler class should be static or leaks might occur: IncomingHandler
What does it mean?
If IncomingHandler class is not static, it will have a reference to your Service object.
Handler objects for the same thread all share a common Looper object, which they post messages to and read from.
As messages contain target Handler, as long as there are messages with target handler in the message queue, the handler cannot be garbage collected. If handler is not static, your Service or Activity cannot be garbage collected, even after being destroyed.
This may lead to memory leaks, for some time at least - as long as the messages stay int the queue. This is not much of an issue unless you post long delayed messages.
You can make IncomingHandler static and have a WeakReference to your service:
static class IncomingHandler extends Handler {
private final WeakReference<UDPListenerService> mService;
IncomingHandler(UDPListenerService service) {
mService = new WeakReference<UDPListenerService>(service);
}
#Override
public void handleMessage(Message msg)
{
UDPListenerService service = mService.get();
if (service != null) {
service.handleMessage(msg);
}
}
}
See this post by Romain Guy for further reference
As others have mentioned the Lint warning is because of the potential memory leak. You can avoid the Lint warning by passing a Handler.Callback when constructing Handler (i.e. you don't subclass Handler and there is no Handler non-static inner class):
Handler mIncomingHandler = new Handler(new Handler.Callback() {
#Override
public boolean handleMessage(Message msg) {
// todo
return true;
}
});
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.
Here is a generic example of using a weak reference and static handler class to resolve the problem (as recommended in the Lint documentation):
public class MyClass{
//static inner class doesn't hold an implicit reference to the outer class
private static class MyHandler extends Handler {
//Using a weak reference means you won't prevent garbage collection
private final WeakReference<MyClass> myClassWeakReference;
public MyHandler(MyClass myClassInstance) {
myClassWeakReference = new WeakReference<MyClass>(myClassInstance);
}
#Override
public void handleMessage(Message msg) {
MyClass myClass = myClassWeakReference.get();
if (myClass != null) {
...do work here...
}
}
}
/**
* An example getter to provide it to some external class
* or just use 'new MyHandler(this)' if you are using it internally.
* If you only use it internally you might even want it as final member:
* private final MyHandler mHandler = new MyHandler(this);
*/
public Handler getHandler() {
return new MyHandler(this);
}
}
This way worked well for me, keeps code clean by keeping where you handle the message in its own inner class.
The handler you wish to use
Handler mIncomingHandler = new Handler(new IncomingHandlerCallback());
The inner class
class IncomingHandlerCallback implements Handler.Callback{
#Override
public boolean handleMessage(Message message) {
// Handle message code
return true;
}
}
With the help of #Sogger's answer, I created a generic Handler:
public class MainThreadHandler<T extends MessageHandler> extends Handler {
private final WeakReference<T> mInstance;
public MainThreadHandler(T clazz) {
// Remove the following line to use the current thread.
super(Looper.getMainLooper());
mInstance = new WeakReference<>(clazz);
}
#Override
public void handleMessage(Message msg) {
T clazz = mInstance.get();
if (clazz != null) {
clazz.handleMessage(msg);
}
}
}
The interface:
public interface MessageHandler {
void handleMessage(Message msg);
}
I'm using it as follows. But I'm not 100% sure if this is leak-safe. Maybe someone could comment on this:
public class MyClass implements MessageHandler {
private static final int DO_IT_MSG = 123;
private MainThreadHandler<MyClass> mHandler = new MainThreadHandler<>(this);
private void start() {
// Do it in 5 seconds.
mHandler.sendEmptyMessageDelayed(DO_IT_MSG, 5 * 1000);
}
#Override
public void handleMessage(Message msg) {
switch (msg.what) {
case DO_IT_MSG:
doIt();
break;
}
}
...
}
I am not sure but you can try intialising handler to null in onDestroy()
I'm confused.
The example I found avoids the static property entirely and uses the UI thread:
public class example extends Activity {
final int HANDLE_FIX_SCREEN = 1000;
public Handler DBthreadHandler = new Handler(Looper.getMainLooper()){
#Override
public void handleMessage(Message msg) {
int imsg;
imsg = msg.what;
if (imsg == HANDLE_FIX_SCREEN) {
doSomething();
}
}
};
}
The thing I like about this solution is there is no problem trying to mix class and method variables.
If you're using Kotlin, simply remove the inner keyword when declaring the nested class.
Nested classes in Kotlin are static by default, declaring them with the inner makes them not-static.
Change your nested Handler subclass declaration from
class myService : Service() {
inner class IncomingHandler : Handler(Looper.getMainLooper()) {
/////
}
}
to
class myService : Service() {
class IncomingHandler : Handler(Looper.getMainLooper()) {
/////
}
}
I'm developing an Android 2.3.3 application with a service. I have this inside that service to communicate with Main activity:
public class UDPListenerService extends Service
{
private static final String TAG = "UDPListenerService";
//private ThreadGroup myThreads = new ThreadGroup("UDPListenerServiceWorker");
private UDPListenerThread myThread;
/**
* Handler to communicate from WorkerThread to service.
*/
private Handler mServiceHandler;
// Used to receive messages from the Activity
final Messenger inMessenger = new Messenger(new IncomingHandler());
// Use to send message to the Activity
private Messenger outMessenger;
class IncomingHandler extends Handler
{
#Override
public void handleMessage(Message msg)
{
}
}
/**
* Target we publish for clients to send messages to Incoming Handler.
*/
final Messenger mMessenger = new Messenger(new IncomingHandler());
[ ... ]
}
And here, final Messenger mMessenger = new Messenger(new IncomingHandler());, I get the following Lint warning:
This Handler class should be static or leaks might occur: IncomingHandler
What does it mean?
If IncomingHandler class is not static, it will have a reference to your Service object.
Handler objects for the same thread all share a common Looper object, which they post messages to and read from.
As messages contain target Handler, as long as there are messages with target handler in the message queue, the handler cannot be garbage collected. If handler is not static, your Service or Activity cannot be garbage collected, even after being destroyed.
This may lead to memory leaks, for some time at least - as long as the messages stay int the queue. This is not much of an issue unless you post long delayed messages.
You can make IncomingHandler static and have a WeakReference to your service:
static class IncomingHandler extends Handler {
private final WeakReference<UDPListenerService> mService;
IncomingHandler(UDPListenerService service) {
mService = new WeakReference<UDPListenerService>(service);
}
#Override
public void handleMessage(Message msg)
{
UDPListenerService service = mService.get();
if (service != null) {
service.handleMessage(msg);
}
}
}
See this post by Romain Guy for further reference
As others have mentioned the Lint warning is because of the potential memory leak. You can avoid the Lint warning by passing a Handler.Callback when constructing Handler (i.e. you don't subclass Handler and there is no Handler non-static inner class):
Handler mIncomingHandler = new Handler(new Handler.Callback() {
#Override
public boolean handleMessage(Message msg) {
// todo
return true;
}
});
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.
Here is a generic example of using a weak reference and static handler class to resolve the problem (as recommended in the Lint documentation):
public class MyClass{
//static inner class doesn't hold an implicit reference to the outer class
private static class MyHandler extends Handler {
//Using a weak reference means you won't prevent garbage collection
private final WeakReference<MyClass> myClassWeakReference;
public MyHandler(MyClass myClassInstance) {
myClassWeakReference = new WeakReference<MyClass>(myClassInstance);
}
#Override
public void handleMessage(Message msg) {
MyClass myClass = myClassWeakReference.get();
if (myClass != null) {
...do work here...
}
}
}
/**
* An example getter to provide it to some external class
* or just use 'new MyHandler(this)' if you are using it internally.
* If you only use it internally you might even want it as final member:
* private final MyHandler mHandler = new MyHandler(this);
*/
public Handler getHandler() {
return new MyHandler(this);
}
}
This way worked well for me, keeps code clean by keeping where you handle the message in its own inner class.
The handler you wish to use
Handler mIncomingHandler = new Handler(new IncomingHandlerCallback());
The inner class
class IncomingHandlerCallback implements Handler.Callback{
#Override
public boolean handleMessage(Message message) {
// Handle message code
return true;
}
}
With the help of #Sogger's answer, I created a generic Handler:
public class MainThreadHandler<T extends MessageHandler> extends Handler {
private final WeakReference<T> mInstance;
public MainThreadHandler(T clazz) {
// Remove the following line to use the current thread.
super(Looper.getMainLooper());
mInstance = new WeakReference<>(clazz);
}
#Override
public void handleMessage(Message msg) {
T clazz = mInstance.get();
if (clazz != null) {
clazz.handleMessage(msg);
}
}
}
The interface:
public interface MessageHandler {
void handleMessage(Message msg);
}
I'm using it as follows. But I'm not 100% sure if this is leak-safe. Maybe someone could comment on this:
public class MyClass implements MessageHandler {
private static final int DO_IT_MSG = 123;
private MainThreadHandler<MyClass> mHandler = new MainThreadHandler<>(this);
private void start() {
// Do it in 5 seconds.
mHandler.sendEmptyMessageDelayed(DO_IT_MSG, 5 * 1000);
}
#Override
public void handleMessage(Message msg) {
switch (msg.what) {
case DO_IT_MSG:
doIt();
break;
}
}
...
}
I am not sure but you can try intialising handler to null in onDestroy()
I'm confused.
The example I found avoids the static property entirely and uses the UI thread:
public class example extends Activity {
final int HANDLE_FIX_SCREEN = 1000;
public Handler DBthreadHandler = new Handler(Looper.getMainLooper()){
#Override
public void handleMessage(Message msg) {
int imsg;
imsg = msg.what;
if (imsg == HANDLE_FIX_SCREEN) {
doSomething();
}
}
};
}
The thing I like about this solution is there is no problem trying to mix class and method variables.
If you're using Kotlin, simply remove the inner keyword when declaring the nested class.
Nested classes in Kotlin are static by default, declaring them with the inner makes them not-static.
Change your nested Handler subclass declaration from
class myService : Service() {
inner class IncomingHandler : Handler(Looper.getMainLooper()) {
/////
}
}
to
class myService : Service() {
class IncomingHandler : Handler(Looper.getMainLooper()) {
/////
}
}
I have a thread running in C++, it will call my UI thread's (Java) static method when some condition's satisfied. When the static method was called, I want a Toast to show on my UI. What I have tried are:
1
static void myMethod(){
Toast.makeText(context, "message", Toast.LENGTH_SHORT).show();
(I have a static context reference in global scope)
}
RESULT:
E/AndroidRuntime( 1331): java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()
2
static void myMethod(){
runOnUiThread(new Runnable(){
public void run(){
Toast.makeText(Context, "message", Toast.LENGTH_SHORT).show();
}
});
RESULT:
Can not compile: Cannot make a static reference to the non-static method runOnUiThread(Runnable) from the type Activity
Can anybody throw some light on this? Many thanks to you.
I think you are calling this method from a different thread than the UI thread and this causes an Exception. I have just tried declaring a static method in my Application class that would do the same as your first code. It worked - but of course only when called from main UI thread.
If you would like to be able to call the static method from a different thread, then you will need to create a handler on the UI thread to display the Toast. Something like this:
private static final int MSG_SHOW_TOAST = 1;
private static Handler messageHandler = new Handler() {
public void handleMessage(android.os.Message msg) {
if (msg.what == MSG_SHOW_TOAST) {
String message = (String)msg.obj;
Toast.makeText(App.this, message , Toast.LENGTH_SHORT).show();
}
}
};
private static void displayMessage() {
Message msg = new Message();
msg.what = MSG_SHOW_TOAST;
msg.obj = "Message to show";
messageHandler.sendMessage(msg);
}
The context in my sample is retrieved from App.this, which is the Application class. You can replace this with your Activity, or your static global context.
static Activity thisActivity = null;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
thisActivity = this;
}
public static void showMsg()
{
Toast.makeText(thisActivity, "message" , Toast.LENGTH_SHORT).show();
}
Try this instead, as described in this post:
public class SampleActivity extends Activity {
/**
* Instances of static inner classes do not hold an implicit
* reference to their outer class.
*/
private static class MyHandler extends Handler {
private final WeakReference<SampleActivity> mActivity;
public MyHandler(SampleActivity activity) {
mActivity = new WeakReference<SampleActivity>(activity);
}
#Override
public void handleMessage(Message msg) {
SampleActivity activity = mActivity.get();
if (activity != null) {
// ...
}
}
}
private final MyHandler mHandler = new MyHandler(this);
/**
* Instances of anonymous classes do not hold an implicit
* reference to their outer class when they are "static".
*/
private static final Runnable sRunnable = new Runnable() {
#Override
public void run() { }
};
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Post a message and delay its execution for 10 minutes.
mHandler.postDelayed(sRunnable, 600000);
// Go back to the previous Activity.
finish();
}
}
I had to use a slightly different method to get the context.
Previously created global Application class:
package com.com.YourAppName;
import android.app.Application;
public class YourAppName_app extends Application {
//declarations, getters, setters, etc...
}
A static method inside your Activity/FragmentActivity where you want the Toast:
public class Home extends FragmentActivity {
static YourAppName_app app;
private static void yourStaticMethod() {
app = ((YourAppName_app)getApplicationContext()); //can also call this in onCreate
Toast.makeText(app, "Your Toast message", Toast.LENGTH_LONG)
.show();
}
Hi I want to use only single handler for more than one Activity.Can I do that ?
To add a bit to Octavian answer, you will actually have a single Handler class but one instance per activity.
For example:
public class MyHandler extends Handler {
// Keep a weak reference to the activity owning the handler
private WeakReference<Activity> activityRef;
public MyHandler(Activity a) {
this.activityRef = new WeakReference<Activity>(a);
}
public void handleMessage(Message msg) {
// do your stuff here, for instance, finish the activity
if (activityRef.get()!=null) {
activityRef.get().finish();
}
}
}
Then in your activity:
public class MyActivity extends Activity {
protected MyHandler handler;
public void onCreate() {
// This is where you'll re-use the handler code
handler = new MyHandler(this);
}
}
Of course. Create a new class that implements the desired interface and instantiate it where needed.
Lets take the OnClickListener as an example. Create a class ExternalClickListener.
public class ExternalClickListener implements View.OnClickListener {
#Override
public void onClick(View v) {
// Do whatever you want.
}
}
Now when you want to set it on a Button it'd be
btn.setOnClickListener(new ExternalClickListener());
Declare this Handler as a static and access it : MyClass.myHandler.