Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Remote service, leaks activity when rotating

I've got a problem with a callbacks in remote service, after register a callback rotation cause an activity leak. Can You give me some suggestion what I'm doing wrong.

IRemoteApi.aidl

import com.example.remoteservice.IRemoteListener;

    interface IRemoteApi{
        void addListener(IRemoteListener listener);
        void removeListener(IRemoteListener listener);
        void sendRequest(String msg);
    }

IRemoteListener.aidl

 interface IRemoteListener {
        void onMessage(String text);
    }

RemoteService.java

public class RemoteService extends Service {
    private static final String TAG = RemoteService.class.getSimpleName();

    final RemoteCallbackList<IRemoteListener> mCallbacks = new RemoteCallbackList<IRemoteListener>();

    @Override
    public void onCreate() {
        super.onCreate();
        Log.e(TAG, "Create service...");
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        mCallbacks.kill();
    }

    private void dumpMethod(String msg){
        if(msg.equals("OK")){

            final int N = mCallbacks.beginBroadcast();
            for (int i=0; i<N; i++) {
                try {
                    mCallbacks.getBroadcastItem(i).onMessage("Voila!");
                } catch (RemoteException e) {}
            }
            mCallbacks.finishBroadcast();
        }
    }

    @Override
    public IBinder onBind(Intent intent) {
        return mBinder;
    }



    private IRemoteApi.Stub mBinder = new IRemoteApi.Stub() {
        @Override
        public void addListener(IRemoteListener listener) throws RemoteException {
            if (listener != null) mCallbacks.register(listener);
        }

        @Override
        public void removeListener(IRemoteListener listener) throws RemoteException {
            if (listener != null) mCallbacks.unregister(listener);
        }

        @Override
        public void sendRequest(String msg) throws RemoteException {
                dumpMethod(msg);
        }


    };

}

MainActivity.java

public class MainActivity extends ActionBarActivity {
    private static final String TAG = MainActivity.class.getSimpleName();

    IRemoteApi mService;
    boolean isBound = false;


    private ServiceConnection mServiceConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            mService = IRemoteApi.Stub.asInterface(service);
            isBound = true;
            Log.e("merhold", "Bound to service");

            try {
                mService.addListener(serviceListener);

            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        getApplicationContext().startService(new Intent(RemoteService.class.getName()));
        getApplicationContext().bindService(new Intent(RemoteService.class.getName()), mServiceConnection, Context.BIND_AUTO_CREATE);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();

        if(isBound){
            try {
                mService.removeListener(serviceListener);
                getApplicationContext().unbindService(mServiceConnection);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }
    }

    public void sendRequest(View view) {
        try {
            mService.sendRequest("OK");
        } catch (RemoteException e) {
            e.printStackTrace();
        }
    }

    private IRemoteListener serviceListener = new IRemoteListener.Stub(){

        @Override
        public void onMessage(String text) throws RemoteException {
            Log.e(TAG, "Message from listener: "+text);
        }
    };

}
like image 553
Merhold Avatar asked Nov 12 '13 17:11

Merhold


People also ask

What is the main cause of memory leaks in application?

Holding the references of the object and resources that are no longer needed is the main cause of the memory leaks in android applications. As it is known that the memory for the particular object is allocated within the heap and the object point to certain resources using some object reference.

What causes memory leaks in Android?

Memory leaks occur when an application allocates memory for an object, but then fails to release the memory when the object is no longer being used. Over time, leaked memory accumulates and results in poor app performance and even crashes.

Which of the following actions can cause memory leak?

Common causes for these memory leaks are: Excessive session objects. Insertion without deletion into Collection objects. Unbounded caches.


1 Answers

Because there are two processes there are also two garbage collectors involved.

The service and client garbage collector. The service handles small IBinder objects (IRemoteListener) which are not so important to garbage collect fast. From the client side these IBinder objects holds a reference to an activity which is big.

The activity can't be garbage collected until the service have garbage collected the IBinder objects so it will be leaked until that happens. The solution is to change the listener into a static inner class. If you want to access something in the activity you have to use a weak reference.

Here's a related question and an explanation by Dianne Hackborn; https://stackoverflow.com/a/12206516/1035854

Some code:

private static class MyRemoteListener extends IRemoteListener.Stub {
    private final WeakReference<Activity> mWeakActivity;

    public MyRemoteListener(Activity activity) {
        mWeakActivity = new WeakReference<Activity>(activity);
    }

    @Override
    public void onMessage(String text) throws RemoteException {
        Activity activity = mWeakActivity.get();
        if (activity != null) {
            // ((MainActivity)activity).handleOnMessage(text);
        }
    }
}
like image 189
finlir Avatar answered Oct 05 '22 17:10

finlir