【问题标题】:How to request Location Permission at runtime如何在运行时请求位置权限
【发布时间】:2017-03-01 17:44:56
【问题描述】:

在清单文件中,我添加了粗略和精细的权限,当我在装有 Android 6 的设备上运行时,什么也没有发生!我试试 一切,但无法获取位置更新...

我做错了什么?

public class MainActivity extends AppCompatActivity implements LocationListener {

    LocationManager locationManager;
    String provider;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);

        provider = locationManager.getBestProvider(new Criteria(), false);

        if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
            // TODO: Consider calling
            //    ActivityCompat#requestPermissions
            // here to request the missing permissions, and then overriding
            //   public void onRequestPermissionsResult(int requestCode, String[] permissions,
            //                                          int[] grantResults)
            // to handle the case where the user grants the permission. See the documentation
            // for ActivityCompat#requestPermissions for more details.
            return;
        }
        Location location = locationManager.getLastKnownLocation(provider);

        if (location != null) {

            Log.i("Location Info", "Location achieved!");

        } else {

            Log.i("Location Info", "No location :(");

        }

    }


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

        if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
            // TODO: Consider calling
            //    ActivityCompat#requestPermissions
            // here to request the missing permissions, and then overriding
            //   public void onRequestPermissionsResult(int requestCode, String[] permissions,
            //                                          int[] grantResults)
            // to handle the case where the user grants the permission. See the documentation
            // for ActivityCompat#requestPermissions for more details.
            return;
        }
        locationManager.requestLocationUpdates(provider, 400, 1, this);

    }

    @Override
    protected void onPause() {
        super.onPause();

        if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
            // TODO: Consider calling
            //    ActivityCompat#requestPermissions
            // here to request the missing permissions, and then overriding
            //   public void onRequestPermissionsResult(int requestCode, String[] permissions,
            //                                          int[] grantResults)
            // to handle the case where the user grants the permission. See the documentation
            // for ActivityCompat#requestPermissions for more details.
            return;
        }
        locationManager.removeUpdates(this);

    }

    @Override
    public void onLocationChanged(Location location) {

        Double lat = location.getLatitude();
        Double lng = location.getLongitude();

        Log.i("Location info: Lat", lat.toString());
        Log.i("Location info: Lng", lng.toString());

    }

    @Override
    public void onStatusChanged(String provider, int status, Bundle extras) {

    }

    @Override
    public void onProviderEnabled(String provider) {

    }

    @Override
    public void onProviderDisabled(String provider) {

    }

    public void getLocation(View view) {

        if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
            // TODO: Consider calling
            //    ActivityCompat#requestPermissions
            // here to request the missing permissions, and then overriding
            //   public void onRequestPermissionsResult(int requestCode, String[] permissions,
            //                                          int[] grantResults)
            // to handle the case where the user grants the permission. See the documentation
            // for ActivityCompat#requestPermissions for more details.
            return;
        }
        Location location = locationManager.getLastKnownLocation(provider);

        onLocationChanged(location);


    }

}

【问题讨论】:

    标签: java android


    【解决方案1】:

    您需要在运行时实际请求位置权限(请注意代码中的 cmets 说明了这一点)。

    更新了 Kotlin 和 API 31 (Android 12) 的后台位置:

    从 API 30 开始,后台位置必须单独请求。 此示例使用targetSdk 31compileSdk 31。 请注意,可以将后台位置请求与 API 29 上的主要位置请求捆绑在一起,但要做到这一点,您需要维护三个单独的代码路径。
    将其分解为对 29 及以上的请求更容易。

    确保在应用级别 gradle(撰写本文时为 18.0.0)中包含最新的定位服务:

    implementation "com.google.android.gms:play-services-location:18.0.0"
    

    在清单中包含位置权限:

    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
    <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
    

    这是一个简化的示例,可以处理大多数情况,但以简化的方式。在用户选择“不再询问”的情况下,在下次启动应用程序时,它将打开用户手动启用权限的设置。

    完整的活动代码:

    import android.Manifest
    import android.app.AlertDialog
    import android.content.Intent
    import android.content.pm.PackageManager
    import android.net.Uri
    import android.os.Build
    import android.os.Bundle
    import android.os.Looper
    import android.provider.Settings
    import android.widget.Toast
    import androidx.appcompat.app.AppCompatActivity
    import androidx.core.app.ActivityCompat
    import androidx.core.content.ContextCompat
    import com.google.android.gms.location.*
    
    
    class MainActivity : AppCompatActivity() {
    
        private var fusedLocationProvider: FusedLocationProviderClient? = null
        private val locationRequest: LocationRequest = LocationRequest.create().apply {
            interval = 30
            fastestInterval = 10
            priority = LocationRequest.PRIORITY_BALANCED_POWER_ACCURACY
            maxWaitTime = 60
        }
    
        private var locationCallback: LocationCallback = object : LocationCallback() {
            override fun onLocationResult(locationResult: LocationResult) {
                val locationList = locationResult.locations
                if (locationList.isNotEmpty()) {
                    //The last location in the list is the newest
                    val location = locationList.last()
                    Toast.makeText(
                        this@MainActivity,
                        "Got Location: " + location.toString(),
                        Toast.LENGTH_LONG
                    )
                        .show()
                }
            }
        }
    
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(R.layout.activity_main)
    
            fusedLocationProvider = LocationServices.getFusedLocationProviderClient(this)
    
            checkLocationPermission()
        }
    
        override fun onResume() {
            super.onResume()
            if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION)
                == PackageManager.PERMISSION_GRANTED
            ) {
    
                fusedLocationProvider?.requestLocationUpdates(
                    locationRequest,
                    locationCallback,
                    Looper.getMainLooper()
                )
            }
        }
    
        override fun onPause() {
            super.onPause()
            if (ContextCompat.checkSelfPermission(
                    this,
                    Manifest.permission.ACCESS_FINE_LOCATION
                )
                == PackageManager.PERMISSION_GRANTED
            ) {
    
                fusedLocationProvider?.removeLocationUpdates(locationCallback)
            }
        }
    
        private fun checkLocationPermission() {
            if (ActivityCompat.checkSelfPermission(
                    this,
                    Manifest.permission.ACCESS_FINE_LOCATION
                ) != PackageManager.PERMISSION_GRANTED
            ) {
                // Should we show an explanation?
                if (ActivityCompat.shouldShowRequestPermissionRationale(
                        this,
                        Manifest.permission.ACCESS_FINE_LOCATION
                    )
                ) {
                    // Show an explanation to the user *asynchronously* -- don't block
                    // this thread waiting for the user's response! After the user
                    // sees the explanation, try again to request the permission.
                    AlertDialog.Builder(this)
                        .setTitle("Location Permission Needed")
                        .setMessage("This app needs the Location permission, please accept to use location functionality")
                        .setPositiveButton(
                            "OK"
                        ) { _, _ ->
                            //Prompt the user once explanation has been shown
                            requestLocationPermission()
                        }
                        .create()
                        .show()
                } else {
                    // No explanation needed, we can request the permission.
                    requestLocationPermission()
                }
            } else {
                checkBackgroundLocation()
            }
        }
    
        private fun checkBackgroundLocation() {
            if (ActivityCompat.checkSelfPermission(
                    this,
                    Manifest.permission.ACCESS_BACKGROUND_LOCATION
                ) != PackageManager.PERMISSION_GRANTED
            ) {
                requestBackgroundLocationPermission()
            }
        }
    
        private fun requestLocationPermission() {
            ActivityCompat.requestPermissions(
                this,
                arrayOf(
                    Manifest.permission.ACCESS_FINE_LOCATION,
                ),
                MY_PERMISSIONS_REQUEST_LOCATION
            )
        }
    
        private fun requestBackgroundLocationPermission() {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
                ActivityCompat.requestPermissions(
                    this,
                    arrayOf(
                        Manifest.permission.ACCESS_BACKGROUND_LOCATION
                    ),
                    MY_PERMISSIONS_REQUEST_BACKGROUND_LOCATION
                )
            } else {
                ActivityCompat.requestPermissions(
                    this,
                    arrayOf(Manifest.permission.ACCESS_FINE_LOCATION),
                    MY_PERMISSIONS_REQUEST_LOCATION
                )
            }
        }
    
        override fun onRequestPermissionsResult(
            requestCode: Int,
            permissions: Array<String>,
            grantResults: IntArray
        ) {
            when (requestCode) {
                MY_PERMISSIONS_REQUEST_LOCATION -> {
                    // If request is cancelled, the result arrays are empty.
                    if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
    
                        // permission was granted, yay! Do the
                        // location-related task you need to do.
                        if (ContextCompat.checkSelfPermission(
                                this,
                                Manifest.permission.ACCESS_FINE_LOCATION
                            ) == PackageManager.PERMISSION_GRANTED
                        ) {
                            fusedLocationProvider?.requestLocationUpdates(
                                locationRequest,
                                locationCallback,
                                Looper.getMainLooper()
                            )
    
                            // Now check background location
                            checkBackgroundLocation()
                        }
    
                    } else {
    
                        // permission denied, boo! Disable the
                        // functionality that depends on this permission.
                        Toast.makeText(this, "permission denied", Toast.LENGTH_LONG).show()
    
                        // Check if we are in a state where the user has denied the permission and
                        // selected Don't ask again
                        if (!ActivityCompat.shouldShowRequestPermissionRationale(
                                this,
                                Manifest.permission.ACCESS_FINE_LOCATION
                            )
                        ) {
                            startActivity(
                                Intent(
                                    Settings.ACTION_APPLICATION_DETAILS_SETTINGS,
                                    Uri.fromParts("package", this.packageName, null),
                                ),
                            )
                        }
                    }
                    return
                }
                MY_PERMISSIONS_REQUEST_BACKGROUND_LOCATION -> {
                    // If request is cancelled, the result arrays are empty.
                    if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
    
                        // permission was granted, yay! Do the
                        // location-related task you need to do.
                        if (ContextCompat.checkSelfPermission(
                                this,
                                Manifest.permission.ACCESS_FINE_LOCATION
                            ) == PackageManager.PERMISSION_GRANTED
                        ) {
                            fusedLocationProvider?.requestLocationUpdates(
                                locationRequest,
                                locationCallback,
                                Looper.getMainLooper()
                            )
    
                            Toast.makeText(
                                this,
                                "Granted Background Location Permission",
                                Toast.LENGTH_LONG
                            ).show()
                        }
                    } else {
    
                        // permission denied, boo! Disable the
                        // functionality that depends on this permission.
                        Toast.makeText(this, "permission denied", Toast.LENGTH_LONG).show()
                    }
                    return
    
                }
            }
        }
    
        companion object {
            private const val MY_PERMISSIONS_REQUEST_LOCATION = 99
            private const val MY_PERMISSIONS_REQUEST_BACKGROUND_LOCATION = 66
        }
    }
    

    在 Android 10 (API 29) 上,用户可以选择在初始位置请求后授予后台位置:

    在 Android 12 (API 31) 上它会执行相同的操作,但界面不同:

    Java 中的原始答案:

    这里是请求位置权限的测试和工作代码。

    把这段代码放到Activity中:

    public static final int MY_PERMISSIONS_REQUEST_LOCATION = 99;
    
    public boolean checkLocationPermission() {
        if (ContextCompat.checkSelfPermission(this,
                Manifest.permission.ACCESS_FINE_LOCATION)
                != PackageManager.PERMISSION_GRANTED) {
    
            // Should we show an explanation?
            if (ActivityCompat.shouldShowRequestPermissionRationale(this,
                    Manifest.permission.ACCESS_FINE_LOCATION)) {
    
                // Show an explanation to the user *asynchronously* -- don't block
                // this thread waiting for the user's response! After the user
                // sees the explanation, try again to request the permission.
                new AlertDialog.Builder(this)
                        .setTitle(R.string.title_location_permission)
                        .setMessage(R.string.text_location_permission)
                        .setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
                            @Override
                            public void onClick(DialogInterface dialogInterface, int i) {
                                //Prompt the user once explanation has been shown
                                ActivityCompat.requestPermissions(MainActivity.this,
                                        new String[]{Manifest.permission.ACCESS_FINE_LOCATION},
                                        MY_PERMISSIONS_REQUEST_LOCATION);
                            }
                        })
                        .create()
                        .show();
    
    
            } else {
                // No explanation needed, we can request the permission.
                ActivityCompat.requestPermissions(this,
                        new String[]{Manifest.permission.ACCESS_FINE_LOCATION},
                        MY_PERMISSIONS_REQUEST_LOCATION);
            }
            return false;
        } else {
            return true;
        }
    }
    
    @Override
    public void onRequestPermissionsResult(int requestCode,
                                           String permissions[], int[] grantResults) {
        switch (requestCode) {
            case MY_PERMISSIONS_REQUEST_LOCATION: {
                // If request is cancelled, the result arrays are empty.
                if (grantResults.length > 0
                        && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
    
                    // permission was granted, yay! Do the
                    // location-related task you need to do.
                    if (ContextCompat.checkSelfPermission(this,
                            Manifest.permission.ACCESS_FINE_LOCATION)
                            == PackageManager.PERMISSION_GRANTED) {
    
                        //Request location updates:
                        locationManager.requestLocationUpdates(provider, 400, 1, this);
                    }
    
                } else {
    
                    // permission denied, boo! Disable the
                    // functionality that depends on this permission.
    
                }
                return;
            }
    
        }
    }
    

    然后调用onCreate()中的checkLocationPermission()方法:

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    
        //.........
    
        checkLocationPermission();
    }
    

    然后您可以完全按照问题中的方式使用onResume()onPause()

    这是一个更简洁的精简版:

    @Override
    protected void onResume() {
        super.onResume();
        if (ContextCompat.checkSelfPermission(this,
                Manifest.permission.ACCESS_FINE_LOCATION)
                == PackageManager.PERMISSION_GRANTED) {
    
            locationManager.requestLocationUpdates(provider, 400, 1, this);
        }
    }
    
    @Override
    protected void onPause() {
        super.onPause();
        if (ContextCompat.checkSelfPermission(this,
                Manifest.permission.ACCESS_FINE_LOCATION)
                == PackageManager.PERMISSION_GRANTED) {
    
            locationManager.removeUpdates(this);
        }
    }
    

    【讨论】:

    • 什么是“提供者”? 'MainActivity' 是请求权限的活动吗?
    • @AbdullahUmer 你可以看到provider在问题中是如何使用的:String provider = locationManager.getBestProvider(new Criteria(), false);
    • 是的,MainActivity 是此示例中此代码所在的 Activity。查看问题中的 MainActivity 了解更多上下文。
    • 很好的例子。谢谢!。注意问题.. 确保您使用的是android.Manifest。 Android Studio 似乎默认为 my.app.package.Manifest
    • @hexicle 你是对的。我只是按原样运行这段代码,它肯定不能正常工作。最简单的解决方法是在onCreate() 中调用checkLocationPermission(),所以我修改了答案来做到这一点。使用此解决方案,您可以完全按照问题中的方式使用onResume()onPause()。感谢您提醒我注意这一点,干杯!
    【解决方案2】:

    Google 创建了一个用于轻松管理权限的库。它叫EasyPermissions

    这是一个使用这个库请求位置权限的简单示例。

    public class MainActivity extends AppCompatActivity {
    
        private final int REQUEST_LOCATION_PERMISSION = 1;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            requestLocationPermission();
        }
    
        @Override
        public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
            super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    
            // Forward results to EasyPermissions
            EasyPermissions.onRequestPermissionsResult(requestCode, permissions, grantResults, this);
        }
    
        @AfterPermissionGranted(REQUEST_LOCATION_PERMISSION)
        public void requestLocationPermission() {
            String[] perms = {Manifest.permission.ACCESS_FINE_LOCATION};
            if(EasyPermissions.hasPermissions(this, perms)) {
                Toast.makeText(this, "Permission already granted", Toast.LENGTH_SHORT).show();
            }
            else {
                EasyPermissions.requestPermissions(this, "Please grant the location permission", REQUEST_LOCATION_PERMISSION, perms);
            }
        }
    }
    

    @AfterPermissionsGranted(REQUEST_CODE)用于表示请求码为REQUEST_CODE的权限请求被授予后需要执行的方法。

    上述情况,如果用户授予访问位置服务的权限,则调用方法requestLocationPermission()方法。因此,该方法既可用作回调,又可用作请求权限的方法。

    您也可以针对授予的权限和拒绝的权限实现单独的回调。 github页面中有说明。

    【讨论】:

    • 提这个库很有帮助,非常感谢
    【解决方案3】:

    Android 10Android Q 中的位置权限隐私更改。

    如果用户想在后台访问他们当前的位置,我们必须定义额外的 ACCESS_BACKGROUND_LOCATION 权限,因此用户需要在requestPermission() 中授予权限runtime p>

    如果我们使用低于 Android 10 的设备,则 ACCESS_BACKGROUND_LOCATION 权限 自动允许 使用 ACCESS_FINE_LOCATIONACCESS_COARSE_LOCATION 权限

    如果我们不在清单文件中指定ACCESS_BACKGROUND_LOCATION,这种表格格式可能很容易理解。

    AndroidManifest.xml

    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
    <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" /> // here we defined ACCESS_BACKGROUND_LOCATION for Android 10 device
    

    MainActivity.java
    onCreate()onResume() 中致电checkRunTimePermission()

    public void checkRunTimePermission() {
       if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            if (ActivityCompat.checkSelfPermission(context, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED || 
                 ActivityCompat.checkSelfPermission(context, Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED||
                 ActivityCompat.checkSelfPermission(context, Manifest.permission.ACCESS_BACKGROUND_LOCATION) == PackageManager.PERMISSION_GRANTED) {
                    gpsTracker = new GPSTracker(context);
    
            } else {
                    requestPermissions(new String[]{Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION},
                            10);
            }
       } else {
                gpsTracker = new GPSTracker(context); //GPSTracker is class that is used for retrieve user current location
       }
    }
    
     @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        if (requestCode == 10) {
            if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                gpsTracker = new GPSTracker(context);
            } else {
                if (!ActivityCompat.shouldShowRequestPermissionRationale((Activity) context, Manifest.permission.ACCESS_FINE_LOCATION)) {
                    // If User Checked 'Don't Show Again' checkbox for runtime permission, then navigate user to Settings
                    AlertDialog.Builder dialog = new AlertDialog.Builder(context);
                    dialog.setTitle("Permission Required");
                    dialog.setCancelable(false);
                    dialog.setMessage("You have to Allow permission to access user location");
                    dialog.setPositiveButton("Settings", new DialogInterface.OnClickListener() {
                        @Override
                        public void onClick(DialogInterface dialog, int which) {
                            Intent i = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS, Uri.fromParts("package",
                                    context.getPackageName(), null));
                            //i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                            startActivityForResult(i, 1001);
                        }
                    });
                    AlertDialog alertDialog = dialog.create();
                    alertDialog.show();
                }
                //code for deny
            }
        }
    }
    
    @Override
    public void startActivityForResult(Intent intent, int requestCode) {
        super.startActivityForResult(intent, requestCode);
        switch (requestCode) {
            case 1001:
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                    if (ActivityCompat.checkSelfPermission(context, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED ||
                            ActivityCompat.checkSelfPermission(context, Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED 
                            || ActivityCompat.checkSelfPermission(context, Manifest.permission.ACCESS_BACKGROUND_LOCATION) == PackageManager.PERMISSION_GRANTED) {
                        gpsTracker = new GPSTracker(context);
                        if (gpsTracker.canGetLocation()) {
                            latitude = gpsTracker.getLatitude();
                            longitude = gpsTracker.getLongitude();
                        }
                    } else {
                        requestPermissions(new String[]{Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION,
                            Manifest.permission.ACCESS_BACKGROUND_LOCATION},10);
                    }
                }
                break;
            default:
                break;
        }
    }
    

    build.gradle(应用级别)

    android {
        compileSdkVersion 29 //should be >= 29
        buildToolsVersion "29.0.2"
        useLibrary 'org.apache.http.legacy'
        defaultConfig {
            applicationId "com.example.runtimepermission"
            minSdkVersion 21
            targetSdkVersion 29 //should be >= 29
            versionCode 1
            versionName "1.0"
            multiDexEnabled true
            testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
            vectorDrawables.useSupportLibrary = true
        }
    }
    

    Here你可以找到GPSTracker.java文件代码

    【讨论】:

    • 你好,谢谢你的回答。您知道如何确定用户选择了“始终允许”选项还是“一次性使用”选项??
    • @MohammadZarei 很高兴为您提供帮助,如果答案有帮助,请支持我的答案,是的,您可以跟踪记录,有onRequestPermissionsResult() 方法,其中有一个参数int[] grantResults你必须写 for loop for (int grantResult : grantResults) { Log.d("TAG", "onRequestPermissionsResult: " + grantResult); } 如果 grantResult 0 然后权限被授予 else -1 然后权限被拒绝。
    • 在“always allow”和“one-time-use”都授予了权限,但是如果用户选择了“one-time-use”,下次他回来我们的应用程序时,必须给我们相同的权限。我想知道我们是否可以检查他是否永久允许我们?
    • @MohammadZarei 所以你必须在sharedPreference 中存储标志onRequestPermissionsResult()。当用户选择“一次性使用”权限时,您必须将标志设置为 true(默认设置为 false),在 MainActivity 中,您可以从偏好中获得价值并检查它是真还是假跨度>
    • 是的,但我找不到一种方法来说明用户选择“一次性”还是总是。这就是我要找的。​​span>
    【解决方案4】:

    从 MainActivity 检查此代码

     // Check location permission is granted - if it is, start
    // the service, otherwise request the permission
    fun checkOrAskLocationPermission(callback: () -> Unit) {
        // Check GPS is enabled
        val lm = getSystemService(Context.LOCATION_SERVICE) as LocationManager
        if (!lm.isProviderEnabled(LocationManager.GPS_PROVIDER)) {
            Toast.makeText(this, "Please enable location services", Toast.LENGTH_SHORT).show()
            buildAlertMessageNoGps(this)
            return
        }
    
        // Check location permission is granted - if it is, start
        // the service, otherwise request the permission
        val permission = ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION)
        if (permission == PackageManager.PERMISSION_GRANTED) {
            callback.invoke()
        } else {
            // callback will be inside the activity's onRequestPermissionsResult(
            ActivityCompat.requestPermissions(
                this,
                arrayOf(Manifest.permission.ACCESS_FINE_LOCATION),
                PERMISSIONS_REQUEST
            )
        }
    }
    

       override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults)
        if (requestCode == PERMISSIONS_REQUEST) {
            if (grantResults[0] == PackageManager.PERMISSION_GRANTED){
                  // Permission ok. Do work.
             }
        }
    }
    

     fun buildAlertMessageNoGps(context: Context) {
        val builder = AlertDialog.Builder(context);
        builder.setMessage("Your GPS is disabled. Do you want to enable it?")
            .setCancelable(false)
            .setPositiveButton("Yes") { _, _ -> context.startActivity(Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS)) }
            .setNegativeButton("No") { dialog, _ -> dialog.cancel(); }
        val alert = builder.create();
        alert.show();
    }
    

    用法

    checkOrAskLocationPermission() {
                // Permission ok. Do work.
            }
    

    【讨论】:

      【解决方案5】:

      以下是我在 28 及以下、29 和 30 上请求前台和后台位置权限的解决方案。API 之间的差异很细微但很重要。

      API 28 及以下,系统将前台和后台位置权限视为相同。如果您授予位置权限,则应用程序被隐式授予。

      API 29,您可以同时请求前台和后台权限。

      API 30,您必须先申请前台位置权限,然后才能在授予前台位置权限后才申请后台位置权限。如果您同时请求前台和后台权限,那么系统将忽略该请求。另一个区别是用户必须在应用程序位置权限设置中允许后台位置权限,而不是通过系统对话框。

      下面的解决方案只有在用户接受了前台和后台位置跟踪后才开始指定的操作(例如后台位置跟踪):

      LocationPermissionUtil.kt

      private const val REQUEST_CODE_FOREGROUND = 1
      private const val REQUEST_CODE_FOREGROUND_AND_BACKGROUND = 2
      
      object LocationPermissionUtil {
      
          private fun Context.isPermissionGranted(permission: String): Boolean = ActivityCompat
              .checkSelfPermission(this, permission) == PackageManager.PERMISSION_GRANTED
      
          private val Context.isFineLocationPermissionGranted
              get() = isPermissionGranted(
                  Manifest.permission.ACCESS_FINE_LOCATION
              )
      
          private val Context.isBackgroundPermissionGranted
              get() = when {
                  Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q -> ActivityCompat.checkSelfPermission(
                      this,
                      Manifest.permission.ACCESS_BACKGROUND_LOCATION
                  ) == PackageManager.PERMISSION_GRANTED
                  else -> isFineLocationPermissionGranted
              }
      
          private val Context.isFineAndBackgroundLocationPermissionsGranted
              get() = isFineLocationPermissionGranted && isBackgroundPermissionGranted
      
          private fun Activity.checkFineLocationPermission() {
              if (isFineLocationPermissionGranted) return
      
              val shouldShowFineLocationPermissionRationale = ActivityCompat
                  .shouldShowRequestPermissionRationale(
                      this,
                      Manifest.permission.ACCESS_FINE_LOCATION
                  )
      
              if (shouldShowFineLocationPermissionRationale) {
                  presentAlertDialog(
                      R.string.dialog_fine_location_rationale_title,
                      R.string.dialog_fine_location_rationale_description,
                      R.string.yes,
                  ) {
                      requestLocationPermissions()
                  }
              } else {
                  requestLocationPermissions()
              }
          }
      
          private fun Activity.requestLocationPermissions() =
              if (Build.VERSION.SDK_INT == Build.VERSION_CODES.Q) {
                  requestFineLocationAndBackground()
              } else {
                  ActivityCompat.requestPermissions(
                      this,
                      arrayOf(Manifest.permission.ACCESS_FINE_LOCATION),
                      REQUEST_CODE_FOREGROUND
                  )
              }
      
          @TargetApi(29)
          private fun Activity.requestFineLocationAndBackground() {
              ActivityCompat.requestPermissions(
                  this,
                  arrayOf(
                      Manifest.permission.ACCESS_FINE_LOCATION,
                      Manifest.permission.ACCESS_BACKGROUND_LOCATION
                  ),
                  REQUEST_CODE_FOREGROUND_AND_BACKGROUND
              )
          }
      
          @TargetApi(29)
          private fun Activity.checkBackgroundLocationPermission() {
              if (isFineAndBackgroundLocationPermissionsGranted) return
      
              val shouldShowBackgroundPermissionRationale = ActivityCompat
                  .shouldShowRequestPermissionRationale(
                      this,
                      Manifest.permission.ACCESS_BACKGROUND_LOCATION
                  )
      
              if (shouldShowBackgroundPermissionRationale) {
                  presentAlertDialog(
                      R.string.dialog_background_location_rationale_title,
                      R.string.dialog_background_location_rationale_description,
                      R.string.yes,
                  ) {
                      requestFineLocationAndBackground()
                  }
              } else {
                  requestFineLocationAndBackground()
              }
          }
      
          fun checkLocationPermissions(activity: Activity, action: () -> Unit) = with(activity) {
              if (isFineAndBackgroundLocationPermissionsGranted) {
                  action()
                  return
              }
      
              checkFineLocationPermission()
          }
      
          fun onRequestPermissionsResult(
              activity: Activity,
              requestCode: Int,
              action: () -> Unit
          ) = with(activity) {
              when (requestCode) {
                  REQUEST_CODE_FOREGROUND -> {
                      if (!isFineLocationPermissionGranted) {
                          checkFineLocationPermission()
                          return
                      }
      
                      if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
                          checkBackgroundLocationPermission()
                      } else {
                          action()
                      }
                  }
                  REQUEST_CODE_FOREGROUND_AND_BACKGROUND -> {
                      if (!isFineLocationPermissionGranted) {
                          checkFineLocationPermission()
                          return
                      }
      
                      if (isBackgroundPermissionGranted) {
                          action()
                      } else {
                          checkBackgroundLocationPermission()
                      }
                  }
              }
          }
      }
      

      活动:

      class MainActivity : AppCompatActivity() {
      
          override fun onCreate(savedInstanceState: Bundle?) {
              super.onCreate(savedInstanceState)
      
              setContentView(R.layout.activity_main)
      
              LocationPermissionUtil.checkLocationPermissions(this, this::onLocationPermissionsGranted)
          }
      
          override fun onRequestPermissionsResult(
              requestCode: Int,
              permissions: Array<out String>,
              grantResults: IntArray
          ) {
              super.onRequestPermissionsResult(requestCode, permissions, grantResults)
      
              LocationPermissionUtil.onRequestPermissionsResult(
                  this,
                  requestCode,
                  this::onLocationPermissionsGranted
              )
          }
      
          private fun onLocationPermissionsGranted() {
              Toast.makeText(
                  this,
                  "Background location permitted, starting location tracking...",
                  Toast.LENGTH_LONG
              ).show()
          }
      
      }
      

      【讨论】:

      • 你好@masterwok 什么是presentAlertDialog? Android Studio 无法解决此问题。
      【解决方案6】:

      此代码对我有用。我也处理过“Never Ask Me”案

      在 AndroidManifest.xml 中

      <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
      <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
      

      在 build.gradle(模块:app)中

      dependencies {
          ....
          implementation "com.google.android.gms:play-services-location:16.0.0"
      }
      

      这是 CurrentLocationManager.kt

      import android.Manifest
      import android.app.Activity
      import android.content.Context
      import android.content.IntentSender
      import android.content.pm.PackageManager
      import android.location.Location
      import android.location.LocationListener
      import android.location.LocationManager
      import android.os.Bundle
      import android.os.CountDownTimer
      import android.support.v4.app.ActivityCompat
      import android.support.v4.content.ContextCompat
      import android.util.Log
      import com.google.android.gms.common.api.ApiException
      import com.google.android.gms.common.api.CommonStatusCodes
      import com.google.android.gms.common.api.ResolvableApiException
      import com.google.android.gms.location.LocationRequest
      import com.google.android.gms.location.LocationServices
      import com.google.android.gms.location.LocationSettingsRequest
      import com.google.android.gms.location.LocationSettingsStatusCodes
      import java.lang.ref.WeakReference
      
      
      object CurrentLocationManager : LocationListener {
      
          const val REQUEST_CODE_ACCESS_LOCATION = 123
      
          fun checkLocationPermission(activity: Activity) {
              if (ContextCompat.checkSelfPermission(
                      activity,
                      Manifest.permission.ACCESS_FINE_LOCATION
                  ) != PackageManager.PERMISSION_GRANTED
              ) {
                  ActivityCompat.requestPermissions(
                      activity,
                      arrayOf(Manifest.permission.ACCESS_FINE_LOCATION),
                      REQUEST_CODE_ACCESS_LOCATION
                  )
              } else {
                  Thread(Runnable {
                      // Moves the current Thread into the background
                      android.os.Process.setThreadPriority(android.os.Process.THREAD_PRIORITY_BACKGROUND)
                      //
                      requestLocationUpdates(activity)
                  }).start()
              }
          }
      
          /**
           * be used in HomeActivity.
           */
          const val REQUEST_CHECK_SETTINGS = 55
          /**
           * The number of millis in the future from the call to start().
           * until the countdown is done and onFinish() is called.
           *
           *
           * It is also the interval along the way to receive onTick(long) callbacks.
           */
          private const val TWENTY_SECS: Long = 20000
          /**
           * Timer to get location from history when requestLocationUpdates don't return result.
           */
          private var mCountDownTimer: CountDownTimer? = null
          /**
           * WeakReference of current activity.
           */
          private var mWeakReferenceActivity: WeakReference<Activity>? = null
          /**
           * user's location.
           */
          var currentLocation: Location? = null
      
          @Synchronized
          fun requestLocationUpdates(activity: Activity) {
              if (mWeakReferenceActivity == null) {
                  mWeakReferenceActivity = WeakReference(activity)
              } else {
                  mWeakReferenceActivity?.clear()
                  mWeakReferenceActivity = WeakReference(activity)
              }
              //create location request: https://developer.android.com/training/location/change-location-settings.html#prompt
              val mLocationRequest = LocationRequest()
              // Which your app prefers to receive location updates. Note that the location updates may be
              // faster than this rate, or slower than this rate, or there may be no updates at all
              // (if the device has no connectivity)
              mLocationRequest.interval = 20000
              //This method sets the fastest rate in milliseconds at which your app can handle location updates.
              // You need to set this rate because other apps also affect the rate at which updates are sent
              mLocationRequest.fastestInterval = 10000
              mLocationRequest.priority = LocationRequest.PRIORITY_HIGH_ACCURACY
      
              //Get Current Location Settings
              val builder = LocationSettingsRequest.Builder().addLocationRequest(mLocationRequest)
              //Next check whether the current location settings are satisfied
              val client = LocationServices.getSettingsClient(activity)
              val task = client.checkLocationSettings(builder.build())
              //Prompt the User to Change Location Settings
              task.addOnSuccessListener(activity) {
                  Log.d("CurrentLocationManager", "OnSuccessListener")
                  // All location settings are satisfied. The client can initialize location requests here.
                  // If it's failed, the result after user updated setting is sent to onActivityResult of HomeActivity.
                  val activity1 = mWeakReferenceActivity?.get()
                  if (activity1 != null) {
                      startRequestLocationUpdate(activity1.applicationContext)
                  }
              }
      
              task.addOnFailureListener(activity) { e ->
                  Log.d("CurrentLocationManager", "addOnFailureListener")
                  val statusCode = (e as ApiException).statusCode
                  when (statusCode) {
                      CommonStatusCodes.RESOLUTION_REQUIRED ->
                          // Location settings are not satisfied, but this can be fixed
                          // by showing the user a dialog.
                          try {
                              val activity1 = mWeakReferenceActivity?.get()
                              if (activity1 != null) {
                                  // Show the dialog by calling startResolutionForResult(),
                                  // and check the result in onActivityResult().
                                  val resolvable = e as ResolvableApiException
                                  resolvable.startResolutionForResult(
                                      activity1, REQUEST_CHECK_SETTINGS
                                  )
                              }
                          } catch (sendEx: IntentSender.SendIntentException) {
                              // Ignore the error.
                              sendEx.printStackTrace()
                          }
      
                      LocationSettingsStatusCodes.SETTINGS_CHANGE_UNAVAILABLE -> {
                          // Location settings are not satisfied. However, we have no way
                          // to fix the settings so we won't show the dialog.
                      }
                  }
              }
          }
      
          fun startRequestLocationUpdate(appContext: Context) {
              val mLocationManager = appContext.getSystemService(Context.LOCATION_SERVICE) as LocationManager
              if (ActivityCompat.checkSelfPermission(
                      appContext.applicationContext,
                      Manifest.permission.ACCESS_FINE_LOCATION
                  ) == PackageManager.PERMISSION_GRANTED
              ) {
                  //Utilities.showProgressDialog(mWeakReferenceActivity.get());
                  if (mLocationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER)) {
                      mLocationManager.requestLocationUpdates(
                          LocationManager.NETWORK_PROVIDER, 10000, 0f, this
                      )
                  } else {
                      mLocationManager.requestLocationUpdates(
                          LocationManager.GPS_PROVIDER, 10000, 0f, this
                      )
                  }
              }
      
              /*Timer to call getLastKnownLocation() when requestLocationUpdates don 't return result*/
              countDownUpdateLocation()
          }
      
          override fun onLocationChanged(location: Location?) {
              if (location != null) {
                  stopRequestLocationUpdates()
                  currentLocation = location
              }
          }
      
          override fun onStatusChanged(provider: String, status: Int, extras: Bundle) {
      
          }
      
          override fun onProviderEnabled(provider: String) {
      
          }
      
          override fun onProviderDisabled(provider: String) {
      
          }
      
          /**
           * Init CountDownTimer to to get location from history when requestLocationUpdates don't return result.
           */
          @Synchronized
          private fun countDownUpdateLocation() {
              mCountDownTimer?.cancel()
              mCountDownTimer = object : CountDownTimer(TWENTY_SECS, TWENTY_SECS) {
                  override fun onTick(millisUntilFinished: Long) {}
      
                  override fun onFinish() {
                      if (mWeakReferenceActivity != null) {
                          val activity = mWeakReferenceActivity?.get()
                          if (activity != null && ActivityCompat.checkSelfPermission(
                                  activity,
                                  Manifest.permission.ACCESS_FINE_LOCATION
                              ) == PackageManager.PERMISSION_GRANTED
                          ) {
                              val location = (activity.applicationContext
                                  .getSystemService(Context.LOCATION_SERVICE) as LocationManager)
                                  .getLastKnownLocation(LocationManager.PASSIVE_PROVIDER)
                              stopRequestLocationUpdates()
                              onLocationChanged(location)
                          } else {
                              stopRequestLocationUpdates()
                          }
                      } else {
                          mCountDownTimer?.cancel()
                          mCountDownTimer = null
                      }
                  }
              }.start()
          }
      
          /**
           * The method must be called in onDestroy() of activity to
           * removeUpdateLocation and cancel CountDownTimer.
           */
          fun stopRequestLocationUpdates() {
              val activity = mWeakReferenceActivity?.get()
              if (activity != null) {
                  /*if (ActivityCompat.checkSelfPermission(activity,
                          Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) {*/
                  (activity.applicationContext
                      .getSystemService(Context.LOCATION_SERVICE) as LocationManager).removeUpdates(this)
                  /*}*/
              }
              mCountDownTimer?.cancel()
              mCountDownTimer = null
          }
      }
      

      在 MainActivity.kt 中

      override fun onCreate(savedInstanceState: Bundle?) {
              super.onCreate(savedInstanceState)
      ...
      CurrentLocationManager.checkLocationPermission(this@LoginActivity)
      }
      
      override fun onDestroy() {
              CurrentLocationManager.stopRequestLocationUpdates()
              super.onDestroy()
          }
      
      
          override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
              super.onRequestPermissionsResult(requestCode, permissions, grantResults)
              if (requestCode == CurrentLocationManager.REQUEST_CODE_ACCESS_LOCATION) {
                  if (grantResults[0] == PackageManager.PERMISSION_DENIED) {
                      //denied
                      val builder = AlertDialog.Builder(this)
                      builder.setMessage("We need permission to use your location for the purpose of finding friends near you.")
                          .setTitle("Device Location Required")
                          .setIcon(com.eswapp.R.drawable.ic_info)
                          .setPositiveButton("OK") { _, _ ->
                              if (ActivityCompat.shouldShowRequestPermissionRationale(
                                      this,
                                      Manifest.permission.ACCESS_FINE_LOCATION
                                  )
                              ) {
                                  //only deny
                                  CurrentLocationManager.checkLocationPermission(this@LoginActivity)
                              } else {
                                  //never ask again
                                  val intent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS)
                                  val uri = Uri.fromParts("package", packageName, null)
                                  intent.data = uri
                                  startActivityForResult(intent, CurrentLocationManager.REQUEST_CHECK_SETTINGS)
                              }
                          }
                          .setNegativeButton("Ask Me Later") { _, _ ->
      
                          }
                      // Create the AlertDialog object and return it
                      val dialog = builder.create()
                      dialog.show()
                  } else if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                      CurrentLocationManager.requestLocationUpdates(this)
                  }
              }
          }
      
          //Forward Login result to the CallBackManager in OnActivityResult()
          override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
              when (requestCode) {
                  //case 1. After you allow the app access device location, Another dialog will be displayed to request you to turn on device location
                  //case 2. Or You chosen Never Ask Again, you open device Setting and enable location permission
                  CurrentLocationManager.REQUEST_CHECK_SETTINGS -> when (resultCode) {
                      RESULT_OK -> {
                          Log.d("REQUEST_CHECK_SETTINGS", "RESULT_OK")
                          //case 1. You choose OK
                          CurrentLocationManager.startRequestLocationUpdate(applicationContext)
                      }
                      RESULT_CANCELED -> {
                          Log.d("REQUEST_CHECK_SETTINGS", "RESULT_CANCELED")
                          //case 1. You choose NO THANKS
                          //CurrentLocationManager.requestLocationUpdates(this)
      
                          //case 2. In device Setting screen: user can enable or not enable location permission,
                          // so when user back to this activity, we should re-call checkLocationPermission()
                          CurrentLocationManager.checkLocationPermission(this@LoginActivity)
                      }
                      else -> {
                          //do nothing
                      }
                  }
                  else -> {
                      super.onActivityResult(requestCode, resultCode, data)
                  }
              }
          }
      

      【讨论】:

      • 老兄这个问题有 JAVA 和 android 标签...但是 Kotlin 人总是要让你知道
      【解决方案7】:

      在清单文件中定义它之后,比原生解决方案更友好的替代方案是使用 Aaper:https://github.com/LikeTheSalad/aaper,如下所示:

      @EnsurePermissions(permissions = [Manifest.permission.ACCESS_FINE_LOCATION])
      private fun scanForLocation() {
          // Your code that needs the location permission granted.
      }
      

      免责声明,我是 Aaper 的创造者。

      【讨论】:

        【解决方案8】:

        寻找更简单的代码?试试这个!

        if (ContextCompat.checkSelfPermission(LoginActivity.this,
                    Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
                ActivityCompat.requestPermissions(LoginActivity.this,
                        new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, REQUEST_CALL);
            }
        

        另外,不要忘记请求权限

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2018-01-31
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多