FusedLocationApi Background Location service stops

2019-05-07 07:09发布

i'm trying to use FusedLocationApi with pending intent to get period location updates so i can send the data to some server for processing. Everything works. However, there are some instances where the broadcast just stops receiving. I would need to restart the service again in order for it to continue.

I already have the service onStartCommand to return START_STICKY so even if the app is killed it should start the service again. Furthermore, I also added a Boot Completed Intent Receiver so if the phone died and user restarted phone it would restart my service.

So it seems everything is fine and working but just at some point, everything just stops. I did notice a few times that when it does stop working, the last location i received was NULL (i log every location update and error messages throughout my project).

Any ideas why location services just stops working?

P.S. there is no connection failure because i put a message and hook into that function and it's not being called. And it's not internet failure either as i log that as well. Once internet is restored it would continue as normal/expected.

Thanks.

This is the main activity:

public class MainActivity extends AppCompatActivity {

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    ActionBar actionBar = getSupportActionBar();

    if (actionBar != null) {
        actionBar.setDisplayShowHomeEnabled(false);
        actionBar.setDisplayShowTitleEnabled(false);
        if (findViewById(android.R.id.home) != null) {
            findViewById(android.R.id.home).setVisibility(View.GONE);
        }

        LayoutInflater inflator = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        View view = inflator.inflate(R.layout.header_logo, null);

        ActionBar.LayoutParams params = new ActionBar.LayoutParams(ActionBar.LayoutParams.WRAP_CONTENT, ActionBar.LayoutParams.WRAP_CONTENT, Gravity.CENTER);
        actionBar.setDisplayShowCustomEnabled(true);
        actionBar.setCustomView(view, params);
    }

    setContentView(R.layout.activity_main);

    TextView textView = (TextView) findViewById(R.id.status_text);

    // init preferences and status handler
    MyStatusHandler.init(getApplicationContext(), textView);

    // init web service call class
    ...;

    // check for location services
    if (!isLocationEnabled(getApplicationContext())){
        String msg = "Location services not turned on";
        MyStatusHandler.setStatusText(msg);
    }
}

public static boolean isLocationEnabled(Context context) {
    int locationMode = 0;
    String locationProviders;

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT){
        try {
            locationMode = Settings.Secure.getInt(context.getContentResolver(), Settings.Secure.LOCATION_MODE);

        } catch (Settings.SettingNotFoundException e) {
            e.printStackTrace();
        }

        return locationMode != Settings.Secure.LOCATION_MODE_OFF;

    }else{
        locationProviders = Settings.Secure.getString(context.getContentResolver(), Settings.Secure.LOCATION_PROVIDERS_ALLOWED);
        return !TextUtils.isEmpty(locationProviders);
    }
}

@Override
public boolean onCreateOptionsMenu(Menu menu) {
    // Inflate the menu; this adds items to the action bar if it is present.
    getMenuInflater().inflate(R.menu.menu_main, menu);
    return true;
}

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    // Handle action bar item clicks here. The action bar will
    // automatically handle clicks on the Home/Up button, so long
    // as you specify a parent activity in AndroidManifest.xml.
    int id = item.getItemId();

    //noinspection SimplifiableIfStatement
    if (id == R.id.action_settings) {
        // create new intent activity for the settings page
        Intent i = new Intent(this, MySettingsActivity.class);
        startActivity(i);
        return true;
    }
    else if (id == R.id.action_about){
        // 1. Instantiate an AlertDialog.Builder with its constructor
        AlertDialog.Builder builder = new AlertDialog.Builder(this);

        // 2. Chain together various setter methods to set the dialog characteristics
        if (Constants.DEBUG_BUILD == true) {
            builder.setMessage("v." + MyStatusHandler.getReleaseVersionNum() + " dev Build: " + MyStatusHandler.getDevVersionNum())
                    .setTitle("About")
                    .setCancelable(false)
                    .setPositiveButton("OK", null);
        }
        else{
            builder.setMessage("v." + MyStatusHandler.getReleaseVersionNum())
                    .setTitle("About")
                    .setCancelable(false)
                    .setPositiveButton("OK", null);
        }

        // 3. Get the AlertDialog from create()
        AlertDialog dialog = builder.create();

        // show it
        dialog.show();

        return true;
    }

    return super.onOptionsItemSelected(item);
}

// check email entered
public boolean isSettingsEntered(){
    boolean result = true;

    if (MyStatusHandler.getEmailText().equals("") || MyStatusHandler.getPasswordText().equals("")){
        // 1. Instantiate an AlertDialog.Builder with its constructor
        AlertDialog.Builder builder = new AlertDialog.Builder(this);

        // 2. Chain together various setter methods to set the dialog characteristics
        builder.setMessage("Please ensure both email and password are entered in settings")
                .setTitle("Email and/or Password not set")
                .setCancelable(false)
                .setPositiveButton("OK",null);

        // 3. Get the AlertDialog from create()
        AlertDialog dialog = builder.create();

        // show it
        dialog.show();

        result = false;
    }

    return result;
}

/** Called when the user clicks the Opt in button */
public void startService(View view) {
    // Do something in response to button

    if (isSettingsEntered() && isLocationEnabled(getApplicationContext())) {
        // send opt in to web service
        ...;
        // start service
        startService(new Intent(this, BackgroundLocationService.class));

        // update status text
        String msg = "Connecting ...";
        MyStatusHandler.setStatusText(msg);
    }
}

/** Called when the user clicks the Opt out button */
public void stopService(View view) {
    // Do something in response to button

    if (isSettingsEntered() && isLocationEnabled(getApplicationContext())) {
        // send OptOut to web service
        ...;

        // update status text
        String msg = "Connecting ...";
        MyStatusHandler.setStatusText(msg);
    }
}

public static void ...(boolean isOptIn, Location location, boolean sendOptIn){
    if (sendOptIn)
    {
        // send opt in via async task
    }
    else{
       // send location via async task
    }
}

// Handle results returned to the FragmentActivity by Google Play services
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    // Decide what to do based on the original request code
    switch (requestCode) {
        case Constants.CONNECTION_FAILURE_RESOLUTION_REQUEST :
            // log the error
            MyStatusHandler.logDataToFile("Connection Failure Resolution Request - Result Code: "+String.valueOf(resultCode));
            // If the result code is Activity.RESULT_OK, try to connect again
            switch (resultCode) {
                case Activity.RESULT_OK :
                    // Try the request again
                    MyStatusHandler.logDataToFile("Attempting to re-start service");
                    // start service
                    startService(new Intent(this, BackgroundLocationService.class));
                    break;
            }
    }
}
}

Here is the background service:

public class BackgroundLocationService extends Service implements
    GoogleApiClient.ConnectionCallbacks,
    GoogleApiClient.OnConnectionFailedListener,
    LocationListener {

public static final String TAG = BackgroundLocationService.class.getSimpleName();

private GoogleApiClient mGoogleApiClient;
private boolean mInProgress;

private LocationRequest mLocationRequest;

public void onCreate(){
    super.onCreate();

    mGoogleApiClient = new GoogleApiClient.Builder(this)
         .addConnectionCallbacks(this)
            .addOnConnectionFailedListener(this)
            .addApi(LocationServices.API)
            .build();
}

@Override
public int onStartCommand (Intent intent, int flags, int startId) {
    super.onStartCommand(intent, flags, startId);

    if(mGoogleApiClient.isConnected() || mInProgress)
        return START_STICKY;


    if(!mGoogleApiClient.isConnected() || !mGoogleApiClient.isConnecting() && !mInProgress) {
        mInProgress = true;
        mGoogleApiClient.connect();
    }

    return START_STICKY;
}

@Override
public IBinder onBind(Intent intent) {
    // TODO: Return the communication channel to the service.
    throw new UnsupportedOperationException("Not yet implemented");
}

@Override
public void onConnected(Bundle bundle) {
    mLocationRequest = LocationRequest.create()
            .setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY)
            .setFastestInterval(Constants.FASTEST_INTERVAL)
            .setInterval(Constants.UPDATE_INTERVAL);

    PendingIntent pendingIntent = PendingIntent.getBroadcast(this, 0,
            new Intent(this, MyLocationHandler.class),
            PendingIntent.FLAG_CANCEL_CURRENT);

    if (mGoogleApiClient.isConnected()) {
        LocationServices.FusedLocationApi.requestLocationUpdates(mGoogleApiClient, mLocationRequest, pendingIntent);
    }
    else{
        MyStatusHandler.setStatusText("Google Client Failed");
    }
}

@Override
public void onConnectionSuspended(int i) {

}

@Override
public void onLocationChanged(Location location) {

}

@Override
public void onConnectionFailed(ConnectionResult connectionResult) {
    mInProgress = false;

    if (connectionResult.hasResolution()) {
        try {
            // Start an Activity that tries to resolve the error
            connectionResult.startResolutionForResult(null, Constants.CONNECTION_FAILURE_RESOLUTION_REQUEST);

            // * Thrown if Google Play services canceled the original
            // * PendingIntent

        } catch (IntentSender.SendIntentException e) {
            // Log the error
            e.printStackTrace();
        }
    } else {

        //* If no resolution is available, display a dialog to the
        // * user with the error.

        Log.i(TAG, "Location services connection failed with code " + connectionResult.getErrorCode());
        MyStatusHandler.setStatusText("Location services connection failed with code " + connectionResult.getErrorCode());
    }
}

@Override
public void onDestroy(){
    mInProgress = false;
    if (mGoogleApiClient.isConnected()) {
        LocationServices.FusedLocationApi.removeLocationUpdates(mGoogleApiClient, this);
        mGoogleApiClient.disconnect();
    }
    super.onDestroy();
}
}

Here is the broadcast receiver:

public class MyLocationHandler extends BroadcastReceiver {

@Override
public void onReceive(Context context, Intent intent) {
    Location location = intent.getParcelableExtra(FusedLocationProviderApi.KEY_LOCATION_CHANGED);

    if (location != null && MyStatusHandler.getOptInStatus()) {
        // debug messages
        String msg = Double.toString(location.getLatitude()) + "," +
                Double.toString(location.getLongitude());
        Log.d("debug", msg);

        // log location to file
        MyStatusHandler.logDataToFile("Location: "+msg);

        // send Location to web service
        MainActivity....(MyStatusHandler.getOptInStatus(), location, false);
    }

    if (location == null){
        MyStatusHandler.logDataToFile("Location == NULL!");
    }
}
}

I have set the interval to 5minutes and Fast interval to 2minutes. NOTE: i removed some function calls and code for the web service operations

Everything works and i get updates as expected but at some point in time, the broadcast receiver doesn't get anything. When i check the logs, OptInStatus doesn't change and the last location broadcast i received was NULL.

The other parts of my code to call the web service and handle the status messages doesn't touch the service or location requests.

0条回答
登录 后发表回答