Android service not restarting in lollipop

前端 未结 7 1302
借酒劲吻你
借酒劲吻你 2020-12-11 01:13

In my application, I use location based service in background. So I need to restart my service when it gets destroyed.

But I got this message in logcat

相关标签:
7条回答
  • 2020-12-11 02:16

    The idea of having a service ALWAYS running in background in Android is just wrong 99% of the times.

    The system need to "shut down" CPU, and switch to a low battery usage profile.

    You are saying you have a location based service. I assume you are using Google Play Services FusedLocationProvider, if not you should.

    The FusedLocationProvider allow you to register for location changes using a PendingIntent. Meaning your services doesn't need to run all the time, it just need to register for location changes and then react when a new location come and do its stuff.

    See the FusedLocationProviderApi official documentation.

    To start listening for location updates

    1. connect to the GoogleClient using the LocationServices.API API
    2. Build your LocationRequest according to your needs (see the doc)
    3. Call requestLocationUpdates() using the PendingIntent version

    To stop listening

    1. connect to the GoogleClient using the LocationServices.API API
    2. Call removeLocationUpdates() using the same PendingIntent

    Your PendingIntent can launch another service to handle the new location.

    For example doing this from a service:

    public void startMonitoringLocation(Context context) {
        GoogleApiClient client = new GoogleApiClient.Builder(context)
                     .addApi(LocationServices.API)
                     .build()
        ConnectionResult connectionResult = mApiClient.blockingConnect();
        if (connectionResult.isSuccess()) {
            LocationServices.FusedLocationApi
                    .requestLocationUpdates(client, buildLocationRequest(), buildPendingIntent(context));
        } else {
            handleConnectionFailed(context);
        }
    }
    

    Then the service can immediately stop.

    The first time this code run it WILL fail. The connection to the google client usually require the user to take some actions. The ConnectionResult.hasResolution() method will return true if this is the case. Otherwise the reason is something else and you can't recover from it. Meaning the only thing you can do is inform the user the feature will not work or have a nice fallback.

    The ConnectionResult.getResolution() give you a PendingIntent you need to use an Activity and startIntentSenderForResult() method on the Activity to resolve this intent. So you would create a Notification starting your Activity to resolve that, and in the end call your Service again.

    I usually just start an Activity dedicated to do all the work. It's lot easier but you don't want to call connectBlocking() in it. Check out this on how to do it.

    You may ask why not requesting location updates directly in the Activity. That's actually perfectly fine, unless you need the location monitor to automatically start with the device, even if the user didn't explicitly opened the App.

    <receiver android:name=".BootCompletedBroadcastReceiver">
        <intent-filter>
            <action android:name="android.intent.action.BOOT_COMPLETED" />
        </intent-filter>
    </receiver>
    

    This way you can just run your service to connect and request location updates when the device is rebooted.

    Example on how you can build your location request:

        public LocationRequest buildLocationRequest() {
            LocationRequest locRequest = LocationRequest.create();
            // Use high accuracy
            locRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
            // how often do you need to check for the location
            // (this is an indication, it's not exact)
            locRequest.setInterval(REQUIRED_INTERVAL_SEC * 1000);
            // if others services requires the location more often
            // you can still receive those updates, if you do not want
            // too many consider setting this lower limit
            locRequest.setFastestInterval(FASTEST_INTERVAL_SEC * 1000);
            // do you care if the user moved 1 meter? or if he move 50? 1000?
            // this is, again, an indication
            locRequest.setSmallestDisplacement(SMALLEST_DISPLACEMENT_METERS);
            return locRequest;
        }
    

    And your pending intent:

    public PendingIntent buildPendingIntent(Context context) {
        Intent intent = new Intent(context, LocationUpdateHandlerService.class);
        intent.setAction(ACTION_LOCATION_UPDATE);
        intent.setPackage(context.getPackageName());
        return PendingIntent.getService(context, REQUEST_CODE, intent, PendingIntent.FLAG_CANCEL_CURRENT);
    }
    

    Your LocationUpdateHandlerService can be an IntentService if you need to do work in background:

    @Override
    protected void onHandleIntent(Intent intent) {
        if (intent != null) {
            Bundle extras = intent.getExtras();
            if (extras != null && extras.containsKey(FusedLocationProviderApi.KEY_LOCATION_CHANGED)) {
                Location location = extras.getParcelable(FusedLocationProviderApi.KEY_LOCATION_CHANGED);
                handleLocationChanged(location);
            } else {
                Log.w(TAG, "Didn't receive any location update in the receiver");
            }
    
        }
    }
    

    But can also be a Broadcast or anything that suits you.

    0 讨论(0)
提交回复
热议问题