简体   繁体   English

应用程序因禁用设置的位置信息权限而崩溃

[英]App crashes on disabling location permission from settings

I have a service class that continuously runs in background to fetch location updates. 我有一个在后台连续运行以获取位置更新的服务类。 Everything works as expected but when I disable the permission from the settings, the app crashes. 一切正常,但是当我从设置中禁用权限时,应用程序崩溃。 I couldn't figure out why the app is crashing when I disable the location permission from the settings. 从设置中禁用位置权限后,我无法弄清楚为什么应用程序崩溃。 Below is what I get in logcat. 以下是我在logcat中获得的内容。

FATAL EXCEPTION: GoogleApiHandler
                 Process: com.android.myapp, PID: 7703
                 java.lang.SecurityException: Client must have ACCESS_COARSE_LOCATION or ACCESS_FINE_LOCATION permission to perform any location operations.
                 at android.os.Parcel.readException(Parcel.java:1683)
                 at android.os.Parcel.readException(Parcel.java:1636)
                 at com.google.android.gms.internal.zzeu.zza(Unknown Source)
                 at com.google.android.gms.internal.zzcfa.zzif(Unknown Source)
                 at com.google.android.gms.internal.zzcfd.getLastLocation(Unknown Source)
                 at com.google.android.gms.internal.zzcfk.getLastLocation(Unknown Source)
                 at com.google.android.gms.location.zzg.zza(Unknown Source)
                 at com.google.android.gms.common.api.internal.zze.zza(Unknown Source)
                 at com.google.android.gms.common.api.internal.zzbo.zzb(Unknown Source)
                 at com.google.android.gms.common.api.internal.zzbo.zzaiw(Unknown Source)
                 at com.google.android.gms.common.api.internal.zzbo.onConnected(Unknown Source)
                 at com.google.android.gms.common.internal.zzac.onConnected(Unknown Source)
                 at com.google.android.gms.common.internal.zzn.zzakr(Unknown Source)
                 at com.google.android.gms.common.internal.zze.zzw(Unknown Source)
                 at com.google.android.gms.common.internal.zzi.zzaks(Unknown Source)
                 at com.google.android.gms.common.internal.zzh.handleMessage(Unknown Source)
                 at android.os.Handler.dispatchMessage(Handler.java:102)
                 at android.os.Looper.loop(Looper.java:154)
                 at android.os.HandlerThread.run(HandlerThread.java:61)

Here is my Service class. 这是我的服务班级。 This always runs in the background. 它始终在后台运行。

public class LocationService extends Service {


    ...
    ...
    ...

    @Override
    public void onCreate() {
        try {
            mFusedLocationClient = LocationServices.getFusedLocationProviderClient(this);

            mLocationCallback = new LocationCallback() {
                @Override
                public void onLocationResult(LocationResult locationResult) {
                    super.onLocationResult(locationResult);
                    onNewLocation(locationResult.getLastLocation());
                }
            };

            createLocationRequest();
            getLastLocation();
            requestLocationUpdates();
        }catch(Exception e){
            e.printStackTrace();
        }

    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.i(TAG, "Service started");

        return START_STICKY;
    }

    @Override
    public void onConfigurationChanged(Configuration newConfig) {
        super.onConfigurationChanged(newConfig);
    }


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


    @Override
    public void onDestroy() {
        Log.i(TAG, "Service destroy");
        Toast.makeText(getApplicationContext(), "Service Destroy", Toast.LENGTH_SHORT).show();
    }

    /**
     * Makes a request for location updates. Note that in this sample we merely log the
     * {@link SecurityException}.
     */
    public void requestLocationUpdates() {
        Log.i(TAG, "Requesting location updates");
        Utils.setRequestingLocationUpdates(this, true);
        try {

          mFusedLocationClient.requestLocationUpdates(mLocationRequest,
                mLocationCallback, Looper.myLooper());
        } catch (SecurityException unlikely) {
            Utils.setRequestingLocationUpdates(this, false);
            Log.e(TAG, "Lost location permission. Could not request updates. " + unlikely);
        }
    }

    /**
     * Removes location updates. Note that in this sample we merely log the
     * {@link SecurityException}.
     */
    public void removeLocationUpdates() {
        Log.i(TAG, "Removing location updates");
        try {

   mFusedLocationClient.removeLocationUpdates(mLocationCallback);
            Utils.setRequestingLocationUpdates(this, false);
            stopSelf();
        } catch (SecurityException unlikely) {
            Utils.setRequestingLocationUpdates(this, true);
            Log.e(TAG, "Lost location permission. Could not remove updates. " + unlikely);
        }
    }

    private void getLastLocation() {
        try {
            mFusedLocationClient.getLastLocation()
                .addOnCompleteListener(task -> {
                        if (task.isSuccessful() && task.getResult() != null) {
                            mLocation = task.getResult();
                        } else {
                            Log.w(TAG, "Failed to get location.");
                        }
                    });
        } catch (SecurityException unlikely) {
            Log.e(TAG, "Lost location permission." + unlikely);
        }
    }

    private void onNewLocation(Location location) {
        Log.i(TAG, "New location: " + location);

        mLocation = location;
        // Doing some operations using location data.
        mPreviousLocation = mLocation;
        System.gc();

    }

    /**
     * Sets the location request parameters.
     */
    private void createLocationRequest() {
        mLocationRequest = new LocationRequest();
        mLocationRequest.setInterval(UPDATE_INTERVAL_IN_MILLISECONDS);
        mLocationRequest
         .setFastestInterval(FASTEST_UPDATE_INTERVAL_IN_MILLISECONDS);
        mLocationRequest
        .setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
    }

    @Override
    public void onTaskRemoved(Intent rootIntent) {
        Log.i(TAG, "Service Task Removed");
        Toast.makeText(getApplicationContext(), "Service Task Removed", Toast.LENGTH_SHORT).show();
        startService(new Intent(getApplicationContext(), LocationService.class));
        super.onTaskRemoved(rootIntent);
    }
}

I am checking location permission in my activity class and starting the service once the user accepts the permission. 我正在活动类中检查位置权限,并在用户接受权限后启动服务。 There is no issue with that and I am even getting location updates from background service. 这样做没有问题,我什至可以从后台服务获取位置更新。 Meanwhile when I try to disable the permission from settings the app crashes. 同时,当我尝试从设置中禁用权限时,应用崩溃。 Here is my MainActivity class where I am checking the location permission. 这是我的MainActivity类,用于检查位置权限。

public class MainActivity extends AppCompatActivity{


@Override
protected void onResume() {
    super.onResume();
    checkAndEnableGps();
}

private void checkAndEnableGps() {
    try {
        new RxPermissions(this)
                .request(Manifest.permission.ACCESS_FINE_LOCATION)
                .subscribe(granted -> {
                    if (granted) {
                        if (!Utils.isServiceRunning(this, LocationService.class)) {
                            startService(new Intent(this, LocationService.class));
                        }
                        showSettingDialog();
                    } 
                });

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

}

You are getting an error regarding location access permission denied by the user and you need to check the permission granted by using checkSelfPermission(context, permission) method, this will return the permission grant state and then you can request for permission access from user. 您收到有关用户拒绝的位置访问权限的错误,并且需要检查使用checkSelfPermission(context,Permission)方法授予的权限,这将返回权限授予状态,然后您可以请求用户进行权限访问。

if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION)
                == PackageManager.PERMISSION_GRANTED) {
    mMap.setMyLocationEnabled(true);
} else {
    // Show rationale and request permission.
}

Then use 'onRequestPermissionsResult' callback from the context you requested the permission and start your location service if the permission is granted. 然后,从您请求权限的上下文中使用“ onRequestPermissionsResult”回调,如果授予了权限,则启动位置服务。

@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
    if (requestCode == MY_LOCATION_REQUEST_CODE) {
      if (permissions.length == 1 &&
          permissions[0] == Manifest.permission.ACCESS_FINE_LOCATION &&
          grantResults[0] == PackageManager.PERMISSION_GRANTED) {
        mMap.setMyLocationEnabled(true);
    } else {
      // Permission was denied. Display an error message.
    }
}

Refer https://developers.google.com/maps/documentation/android-api/location for documentation. 请参阅https://developers.google.com/maps/documentation/android-api/location以获取文档。

You have to add checkpermission for 6+ ie from Marshmallow like below, 您必须添加6 checkpermission以上的Checkpermission,例如棉花糖,如下所示,

public void checkPermission()
{
    if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED ||
                   ContextCompat.checkSelfPermission(this,Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED)
    {

            ActivityCompat.requestPermissions(this,
                    new String[]{Manifest.permission.ACCESS_FINE_LOCATION,Manifest.permission.ACCESS_COARSE_LOCATION},
                    123);
    }
}

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M ) {
    checkPermission();
}

Hope it helps! 希望能帮助到你!

It needs those permissions, you remove them, so when it tries to get the location it crashes. 它需要这些权限,将其删除,因此当它尝试获取位置时会崩溃。

Either use a catch statement to handle the exception or check if you have permissions and ask for them an runtime before actually trying to get the location 使用catch语句来处理异常,或者检查您是否具有权限,并在实际尝试获取位置之前先请求运行时

Just check if you have permission before running the Service. 在运行服务之前,只需检查您是否有权限。

mContext.checkSelfPermission(Manifest.permission.ACCESS_COARSE_LOCATION ) == PackageManager.PERMISSION_GRANTED;

If you don't have the permission you can request for it from either a Fragment or an Activity . 如果没有权限,则可以从FragmentActivity进行请求。

requestPermissions(new String[]{Manifest.permission.ACCESS_COARSE_LOCATION }, 200 /*request code*/);

You can get the user response ie PERMISSION_GRANTED in the Fragment or Activity class. 您可以在FragmentActivity类中获得用户响应,即PERMISSION_GRANTED

@Override
public void onRequestPermissionsResult(int permsRequestCode, @NonNull String[] permissions, @NonNull int[] grantResults){

    switch(permsRequestCode){

        case 200:    // notice the request code is the same as requested.

            if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {

                startService();
            }
            else {
                // don't start service.
            }

            break;
    }
}

This is happening because since API version 6.0 you need to check permission at run time. 发生这种情况是因为自API版本6.0起,您需要在运行时检查权限。 And if permission is not granted, your app should ask to grant permission before doing permission related task. 并且,如果未授予权限,则您的应用应在执行与权限相关的任务之前要求授予权限。

Here, for access user location required permission is ACCESS_COARSE_LOCATION or ACCESS_FINE_LOCATION . 在这里,访问用户位置所需的权限是ACCESS_COARSE_LOCATION or ACCESS_FINE_LOCATION So, you need to check user granted Location permission or not. 因此,您需要检查是否授予用户授予的位置权限。

One more thing, you are taking location in a service class. 还有一件事,您正在位置服务类中。 Here is a problem - 这是一个问题-

You cannot ask the user for permission from a service 您不能要求用户获得服务许可

but you can check it by calling checkSelfPermission() 但您可以通过调用checkSelfPermission()检查

To make it more nice you should check and request permission in Activity/Fragment class then start Service manually. 为了使其更加美观,您应该在Activity / Fragment类中检查并请求权限,然后手动启动Service。

On the other hand, you can check for permissions in service, if not accepted by user then show notification, grant permissions and then start Service. 另一方面,您可以检查服务中的权限,如果未被用户接受,则显示通知,授予权限,然后启动服务。

YOU HAVE CHECKED PERMISSION IN AN ACTIVITY: 您已在活动中检查权限:

So, now use checkSelfPermission() to check whether user granted permission or not. 因此,现在使用checkSelfPermission()来检查用户是否授予了权限。 If not then show Notification with PendingIntent add some flag with it. 如果没有,则显示带有PendingIntent的Notification并添加一些标志。 Opening of Activity request for permission and then start service by, 打开活动请求许可,然后通过以下方式启动服务:

startService(new Intent(YourActivity.this, YourService.class));

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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