Cilenco
Cilenco

Reputation: 7117

Communication between Activity and Service causes memory leak

In my application I have a (background) Service which runs in its own process. To communicate with other components I defined some AIDL interfaces:

interface MyService {
    void addOnChangeListener(ServiceChangedListener listener);
    void removeOnChangeListener(ServiceChangedListener listener);
}

interface ServiceChangedListener {
    void onUserChanged(in User newUser);
}

Here is my service with the MyService implementation.

public class UserService extends Service {
    private RemoteCallbackList<ServiceChangedListener> listeners;

    public class ServiceBinder extends MyService.Stub {
        public void addOnChangeListener(ServiceChangedListener listener) {
            listeners.register(listener);
        }

        public void removeOnChangeListener(ServiceChangedListener listener) {
            listeners.unregister(listener);
        }
    }

    // all the other implementation...
}

My Activity connects to this Service in onStart and disconnects from it in onStop where it also release the listener which is registered in onServiceConnected.

public class MainAcitivity extends AppCompatActivity {
    private ServiceListener listener;
    private MyService service;

    protected void onStart() {
        super.onStart();

        Intent intent = new Intent(this, UserService.class);
        bindService(intent, this, Context.BIND_AUTO_CREATE);
    }

    protected void onStop() {
        super.onStop();

        try { service.removeOnChangeListener(listener); }
        catch (RemoteException e) { e.printStackTrace(); }

        unbindService(this);
    }

    public void onServiceConnected(ComponentName className, IBinder service) {
        service = MyService.Stub.asInterface(service);

        try {
            listener = new ServiceListener();
            service.addOnChangeListener(listener);
        } catch (RemoteException e) {
            e.printStackTrace();
        }
    }

    private class ServiceListener extends ServiceChangedListener.Stub {
        public void onUserChanged(User u) throws RemoteException {
            runOnUiThread(() -> { ... });
        }
    }
}

The add and remove methods for the listener simply operate on an ArrayList as you would expect so there is no long ongoing work.

LeakCanary as well as the Android studio memory analysis tool tell me that this causes a memory leak. If I comment out the lines affecting the listener no memory leak is detected. Do you have any ideas why?

Upvotes: 2

Views: 862

Answers (1)

Cilenco
Cilenco

Reputation: 7117

I finally found the answer! You have to make the inner class static and use a WeakReference within it. Here is an official answer of a Google engineer and here a simplified one. Good luck anyone with the same problem!

Upvotes: 3

Related Questions