简体   繁体   中英

Call finish from onPause or onStop when the user destroys the app

Coclusion: onDestroy() is not always being invoked.

Here is the case:

After Launching the app, the MainActivity is created which starts background tracking service. On the other hand, the user can use a checkbox list in the main activity to select his interested routes where the items of the checkbox list represents the routes.

After clicking the submit button, the alarmManager starts firing the data to the IntentService class every 30 seconds. Subsequently, connection with the server is established and retrieving data to the MapActivity is started after the user was redirected automatically to the MapActivity .

The user is able to stop and start the background service by using an icon in the menu of the both activities. I am facing problem to destroy the app in the case describes below.

With the current code:

  • After launching the app I can stop the service in the mainActivity and destory the app.
  • after launching the app when I change from the main activity to the map activity , I can stop and start the service as well as destroy the app with opening Mapactivity.
  • When I go as the following (mainActivity->mapActivity->MainAtivity->MapActivity) and closed the app with opening map view the app is not being destroyed and the service is not being stoped. Also the onDestroy() in MapActivity is not being invoked. Here the app opens gain with the map view.

But when I do the following: adding startService(i)(as the code snippet four lines below) to the onStart() in the MapActivity after bindService I can destroy the app as well stoping and starting the service but the problem here is I dont want to start the service every time I am going from the MainActivity to the MapActivity since if the user stops the service in the MainActivity it must stay off when he goes from the main to the map.

@Override
protected void onStart() {
    super.onStart();
    // Bind to TrackingService.
    Intent intent = new Intent(this, TrackingService.class);
    bindService(intent, mConnection,    Context.BIND_AUTO_CREATE);            
    Intent i = new Intent(this, TrackingService.class);
    startService(i);
}

I know that there's no guarantee of when onDestroy() will be called in an Activity and maybe I should call finish() inside onPause() or onStop() but my question is how to know when to call finish() inside onPause() or onStop() since the tracking service should run in the background until the app is being destroyed by the user or the user decides to stop it with the icon in the menu. I dont want to stop the tracking service when the user goes in the background or everytime onPause() is being called (In my case onPause() in the MapActivity is being called several time in one minute).

How can I call finish() from onPause() or onStop() only when the user destroys the app? How to manage it in this case?

MainActivity:

public class MainActivity extends ActionBarActivity implements
        AsyncTaskCallback {
boolean serviceStatus = true;
TrackingService mService;
boolean mBound = false;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.route_available);
        // Start the TrackingService class.
        Intent i = new Intent(this, TrackingService.class);
        startService(i);        

    }
    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        System.out.println("ABC MainActivity onOptionsItemSelected was invoked.");
        switch (item.getItemId()) {
        case R.id.menu_toggle:

            if (serviceStatus) {
                item.setIcon(R.drawable.off);
                item.setTitle("OFF");
                serviceStatus = false;
                mService.stopTrackingService();
            } else {
                item.setIcon(R.drawable.on);
                item.setTitle("ON");
                serviceStatus = true;
                Intent i = new Intent(this, TrackingService.class);
                startService(i);
                System.out.println("ABC MainActivity onOptionsitemSelected   ON");
        }
        return super.onOptionsItemSelected(item);
    }
}

    @Override
    protected void onStart() {
        super.onStart();        
        // Bind to TrackingService.
        Intent intent = new Intent(this, TrackingService.class);            
        //To start the onPrepareOptionsMenue() after returning from the map activity to change the icon of the toggle button.
        invalidateOptionsMenu();

    }

    @Override
    protected void onStop() {
        super.onStop();
        //13.08.15
        // Unbind from the service
        if (mBound) {
            unbindService(mConnection);
            System.out.println("ABC MainActivity onStop() - unbindService(mConnection) was invoked. " + mBound);
            mBound = false;
        }else{
            System.out.println("ABC MainActivity onStop() - unbindService(mConnection) was invoked. " + mBound);
        }

    }
    @Override
    protected void onDestroy() {

        Intent i = new Intent(this, TrackingService.class); 
        stopService(i);

        mService.stopTrackingService();
    }
    private ServiceConnection mConnection = new ServiceConnection(){

        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            LocalBinder binder = (LocalBinder) service;
            mService = binder.getService();
            mBound = true;          
        }

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

}

MapActivity

   public class MapActivity extends ActionBarActivity implements OnMapReadyCallback,
            ConnectionCallbacks, OnConnectionFailedListener {
         boolean serviceStatus;
        TrackingService mService;
        boolean mBound = false;

        @Override
        protected void onStart() {
            super.onStart();
                  serviceStatus = getIntent().getExtras().getBoolean("ServiceStatusExtras");
    if (serviceStatus) {
        Intent i = new Intent(this, TrackingService.class);
         bindService(i, mConnection, Context.BIND_AUTO_CREATE);
        startService(i);
        System.out.println("ABC MapActivity onStart serviceStatus = " + serviceStatus);
    }
        }

        /** Defines callbacks for service binding, passed to bindService() */
        private ServiceConnection mConnection = new ServiceConnection(){

            @Override
            public void onServiceConnected(ComponentName name, IBinder service) {
                // We've bound to LocalService, cast the IBinder and get LocalService instance

                LocalBinder binder = (LocalBinder) service;
                mService = binder.getService();
                mBound = true;
                //System.out.println("ABC Map onServiceConnected() - " + mBound);

            }

            @Override
            public void onServiceDisconnected(ComponentName name) {

                mBound = false;
                //System.out.println("ABC Map onServiceDisconnected() - mBound");

            }
        };
        @Override
        protected void onStop() {
            super.onStop();         
            if (mBound) {
                unbindService(mConnection);
                mBound = false;
            }       

        }

        @Override
        protected void onDestroy() {
            super.onDestroy();
            markerMap.clear();
            stopAlarm();

                    if(!serviceStatus){
        Intent i = new Intent(this, TrackingService.class); 
        stopService(i);
    }

        }
        @Override
        public boolean onOptionsItemSelected(MenuItem item) {
            switch (item.getItemId()) {
            case R.id.menu_toggle:

                if (serviceStatus) {
                    item.setIcon(R.drawable.off);
                    item.setTitle("OFF");
                    serviceStatus = false;
                    if(mService!=null){
                mService.stopTrackingService(); 
                }
                } else {
                    item.setIcon(R.drawable.on);
                    item.setTitle("ON");
                    serviceStatus = true;
                    Intent i = new Intent(this, TrackingService.class);
                    startService(i);
                }

            }
            return super.onOptionsItemSelected(item);
       }
    }

TrackingService class:

public class TrackingService extends Service implements AsyncTaskCallback,
        LocationListener {
LocationManager lm;
private final IBinder mBinder = new LocalBinder();

    @Override
    public IBinder onBind(Intent intent) {  
        return mBinder;
    }
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        detectLocation();
        return START_NOT_STICKY;
    }

    private void detectLocation() {
        lm = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
        lm.requestLocationUpdates(LocationManager.GPS_PROVIDER, 12 * 1000, 0,
                this);

    }
    public class LocalBinder extends Binder {
        TrackingService getService() {
            return TrackingService.this;
            }
       }

        public void stopTrackingService(){
        if(lm != null){
            lm.removeUpdates(this);
           }
        }

    }

}

a classic solution is to have a static counter that is increased at each activity "onStart" and decreased on "onPause".

when counter is at 0, app is really closed

the only hard part is if you have orientation change on your first activity, as activity is completely destroyed and recreated by system, you may think that app is really closed (as counter is at 0 in this situation)

i have no magic solution for this particular case

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM