2

I am trying to figure out why my service is leaking from my application.

The official error I am getting is that the Service is not registered any longer.

Here's what works: I create a service which creates a listener, when the listener is triggered the service sets of an intent to start another activity. The new activity begins and does its thing.

The problem: When I get back to a main screen which gives me the option to turn off the service, I get the error which I stated previously which causes an IllegalArgumentException (when I try to unbind the service which is not registered).

Any help would be greatly appreciated. Here is the code for my service. It is all I've included because this seems to be where the problem is, but if you need any more let me know.

Thanks in advance, here is the code.

import java.lang.ref.WeakReference;
import java.util.List;

import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.os.Binder;
import android.os.IBinder;
import android.util.Log;
import android.widget.Toast;


public class AccelService extends Service
{
public static boolean listening = false;
public boolean callMade = false;
private static Sensor sensor;
private static SensorManager ASensorManager;


private SensorEventListener EventListener = 
    new SensorEventListener() {

    private float x = 0;
    private float y = 0;
    private float z = 0;
    private double max = 0;
    private double force = 0;

    public void onAccuracyChanged(Sensor sensor, int accuracy) {}

    public void onSensorChanged(SensorEvent event) 
    {

        x = event.values[0];
        y = event.values[1];
        z = event.values[2];
        force = Math.sqrt(x*x+y*y+z*z);
        Log.i("LocalService", "Event happened: " + force);


        if (force > Main.dropValue)
        {
            onDrop(force);
        }
    } 
};

public void startListener()
{
    ASensorManager = (SensorManager) this.getSystemService(Context.SENSOR_SERVICE);
    List<Sensor> sensors = ASensorManager.getSensorList(Sensor.TYPE_ACCELEROMETER);
    if (sensors.size() > 0) 
    {
        sensor = sensors.get(0);
        listening = ASensorManager.registerListener(accelEventListener, sensor, SensorManager.SENSOR_DELAY_GAME);

    }
}   


public class AccelBinder<S> extends Binder 
{
    private WeakReference<S> mService;

    public AccelBinder (S service)
    {
        mService = new WeakReference<S>(service);
    }

    public S getService()
    {
        return mService.get();
    }
}

public IBinder mBinder;

@Override
public void onCreate()
{
    startListener();

    mBinder = new AccelBinder<AccelService>(this);
}

public boolean isListening()
{
    return listening;
}

/*@Override
public void onStart(Intent intent, int startId)
{
    Log.i("LocalService", "Received start id " + startId + ": " + intent);
}*/

@Override
public int onStartCommand(Intent intent, int flags, int startId) 
{
    Log.i("LocalService", "Received start id " + startId + ": " + intent);
    return AccelService.START_STICKY;
}

@Override
public void onDestroy() 
{
    if (listening)
        stopListening();
    mBinder = null;

    super.onDestroy();
}

public void onDrop(double force)
{
    if (!callMade)
    {
        Toast.makeText(this, "Phone dropped: " + force, 5000).show();
        Intent i = new Intent(this,Next.class);
        i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        callMade = true;
        //stopListening();
        //onDestroy();
        //SafetyNet.ctxt.unbindService(SafetyNet.AccelWatch);
        this.startActivity(i);
    }
}

public void stopListening()
{       

    listening = false;
    try {
        if (ASensorManager != null && accelEventListener != null) 
        {
            ASensorManager.unregisterListener(accelEventListener);
        }
    } catch (Exception e) {}        
}

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

}
Kevin
  • 127
  • 1
  • 7

1 Answers1

8

I don't know much about Sensors, but your Service is looking pretty good.

IF AccelBinder is an inner class of your service make it a static inner class or, as I generally do, a separate class altogether. Static inner classes don't have a reference to the outer class. Remember you WILL leak your Binder. If your Binder is a non-static inner class, it has a reference to your Service so that will leak as well.

I am guessing - like really wildly guessing - in the absence of any code that there is something wrong with the management of your Activity life-cycle and how you handle your Binder objects.

Things to keep in mind....

Don't keep a static reference to your Binder object - they are disposable - get a new one each time you bind.

Keep your Binding symmetrical with regards to your Activity life-cycle. If you bind in onCreate(), unbind in onDestroy() ( or onPause if isFinishing() ) etc. This is particularly important if you don't understand that a physical rotation of a phone with Sensor destroys your Activity the recreates it from scratch.

You seem altogether too fond of "static" class variables. In Android static things tend to lead to memory leaks - and if what you leak has a Context - things get nasty. If you want to keep state between instances of the same class, consider using Preferences instead.

For example,

private static Sensor sensor;
private static SensorManager ASensorManager;

Throw these away between uses.

Make sure you especially are not keeping a static reference to your Service in your Activity. Services are singletons by the very nature of the Android, if it is still running you will get the same Service each time you bind. Count on your Service being killed by the OS at some point in time. Write your Service so that it does it's job if it is restarted afresh.

That's enough guessing for one day.

brucko
  • 361
  • 2
  • 7