"Allow all the time" 位置提示未进入 Android SDK 29
"Allow all the time" location prompt not coming in Android SDK 29
我无法在 SDK 29 中得到“始终允许”位置提示。我已经在清单中设置了这些权限:
<uses-permission android:name="com.google.android.providers.gsf.permission.READ_GSERVICES" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
并请求用户在 运行 时允许这些权限。但它只有 returns“当应用程序打开时”和“拒绝”选项。
关于如何在 SDK 29 中显示它的任何想法。
在清单中添加权限 ACCESS_BACKGROUND_LOCATION
。需要在 android 10 及更高版本上始终显示允许选项。
参见https://developer.android.com/training/location/background#evaluate中的第二点
为了在 运行 Android 10(API 级别 29)或更高级别的设备上访问后台位置,您还需要在清单文件中使用以下权限
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
更多信息请参考以下link
https://developer.android.com/training/location/permissions?hl=fr
我发现仅在清单中添加权限是不够的。我必须为用户提供一个在启动时授予它的选项,就像我需要为 ACCESS_FINE_LOCATION 做的一样。有趣的是,当我检查是否已在应用程序中授予后台权限时,它永远不会给出 false。但如果我不检查,我就不会得到“始终允许”选项。这是 Android Pixel 2 版本 10
它只在 Android 的 10+ 版本中是必需的。请求这两种权限都会让用户在对话框中进行选择……当然用户可能不会 select 'All the time'。对此我们无能为力!
我有点不好意思添加我的代码,因为我知道它很糟糕。其他人肯定可以改进它。我找不到避免两次循环访问权限的方法。
/**
* This method lists all the permissions needed and gives reasons for each of them in a single
* dialog. When the user presses ok, Android will popup separate dialogs for each permission
* that is needed. In the case of location, the "Allow all the time" gives background permission
* "Allow only while the app is running gives just the location permission while in foreground.
* @return false if permissions are to be requested, true if they have already been granted.
*
* In Android 11 Build 30 things are a lot different. One has to ask for location first, approve
* the 'while the app is running' option and then upon return in the Activity result, ask for
* the background. Thus the new variable 'secondPassR' to handle this case. After the background
* one comes back to the Activity result and then one can go into the main activity.
*/
static boolean secondPassR = false;
private static final int REQUEST_CODE_MULTIPLE_PERMISSIONS = 57;
private boolean requestPermissions(Activity activity)
{
Log.v(TAG, "requestPermissions() called");
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
{
final List<String> permissionsList = new ArrayList<>();
final List<String> reasonList = new ArrayList<>();
if(!addPermission(permissionsList, Manifest.permission.ACCESS_FINE_LOCATION, activity))
{
reasonList.add("LOCATION PERMISSION: Needs needs to be granted for the Bluetooth " +
" LE scanner to discover devices!\n\n");
}
if ((Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) && (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) || secondPassR)
{
if (!addPermission(permissionsList, Manifest.permission.ACCESS_BACKGROUND_LOCATION, activity))
{
reasonList.add("BACKGROUND PERMISSION: Needs to be granted for the Bluetooth " +
"LE Scanner to run in the background.\n\n");
}
}
if (permissionsList.size() > 0)
{
if (reasonList.size() > 0)
{
// Need Rationale
StringBuilder message = new StringBuilder(reasonList.get(0));
for (int i = 1; i < reasonList.size(); i++)
{
message.append(" ").append(reasonList.get(i));
}
final androidx.appcompat.app.AlertDialog.Builder builder =
new androidx.appcompat.app.AlertDialog.Builder(new ContextThemeWrapper(activity, R.style.Theme_AppCompat_Light));
builder.setTitle("Demo needs the following permissions:");
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
{
builder.setMessage(Html.fromHtml(message.toString(), Html.FROM_HTML_MODE_LEGACY));
}
else
{
builder.setMessage(Html.fromHtml(message.toString()));
}
builder.setPositiveButton(android.R.string.ok, null);
builder.setOnDismissListener(dialog -> {
Log.v(TAG, "Requesting permissions");
activity.requestPermissions(permissionsList.toArray(new String[0]), // newer Java recommended
REQUEST_CODE_MULTIPLE_PERMISSIONS);
});
builder.show();
return false;
}
activity.requestPermissions(permissionsList.toArray(new String[0]), // newer Java recommended
REQUEST_CODE_MULTIPLE_PERMISSIONS);
}
else
{
return true;
}
}
else
{
return true;
}
return false;
}
@TargetApi(23)
private boolean addPermission(List<String> permissionsList, String permission, Activity activity)
{
Log.v(TAG,
"addPermission() called with: " + "permissionsList = " +
"[" + permissionsList + "], permission = [" + permission + "]");
if (ActivityCompat.checkSelfPermission(activity, permission) != PackageManager.PERMISSION_GRANTED)
{
permissionsList.add(permission);
// Check for Rationale Option
return activity.shouldShowRequestPermissionRationale(permission);
}
return true;
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults)
{
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
String permission = "";
Log.v(TAG,
"onRequestPermissionsResult() called with: " + "requestCode = [" + requestCode +
"], permissions = [" + Arrays.toString(permissions) + "]," +
" grantResults = [" + Arrays.toString(grantResults) + "]");
if (requestCode == REQUEST_CODE_MULTIPLE_PERMISSIONS)
{
for (int i = 0; i < permissions.length; i++)
{
switch (permissions[i])
{
case Manifest.permission.ACCESS_FINE_LOCATION:
if (grantResults[i] == PackageManager.PERMISSION_GRANTED)
{
Log.d(TAG, "H@H: onRequestPermissionsResult: FINE LOCATION PERMISSION");
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R)
{
Log.d(TAG, "H@H: Now requesting BACKGROUND PERMISSION for version 11+");
secondPassR = true;
requestPermissions(thisActivity);
return;
}
}
break;
case Manifest.permission.ACCESS_BACKGROUND_LOCATION:
if (grantResults[i] == PackageManager.PERMISSION_GRANTED)
{
Log.d(TAG, "H@H: onRequestPermissionsResult: BACKGROUND PERMISSION");
}
break;
}
}
}
Log.d(TAG, "Starting primary activity");
secondPassR = false;
startActivityForResult(new Intent(context, PchaDemoPhg_Activity.class), EXIT_QUIT);
}
private void showMessageOKCancel(String message, DialogInterface.OnClickListener okListener)
{
new AlertDialog.Builder(this)
.setMessage(message)
.setCancelable(false)
.setPositiveButton("OK", okListener)
.create()
.show();
}
============== 更新 ANDROID 12 和 ActivityResultLauncher =======
随着 Android12 的出现,出现了一组新的权限,我们不再需要请求位置权限才能使用 BTLE 扫描仪。他们还有一种处理 activity 结果的新方法,那就是 ActivityResultLauncher,其中一个内置功能是运行时权限。下面是我现在用来向 ANdroid 6+ 请求蓝牙扫描仪和后台权限的内容。我使用单权限启动器在每个请求之前放置一个解释对话框。用户体验比我以前拥有的要好得多。 (我包括我的字符串资源。)
import android.Manifest;
import android.annotation.SuppressLint;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Bundle;
import android.text.Html;
import android.util.Log;
import androidx.activity.result.ActivityResultCallback;
import androidx.activity.result.ActivityResultLauncher;
import androidx.activity.result.contract.ActivityResultContracts;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.view.ContextThemeWrapper;
import androidx.core.app.ActivityCompat;
import java.util.ArrayList;
import java.util.List;
/*
resource strings used:
<string name="permissions_connect_12"><p>This app needs permission to connect to Bluetooth devices</p> </string>
<string name="permissions_scan_12"><p>To search for Bluetooth devices this app needs permission to use the BLE scanner.</p> </string>
<string name="permissions_overlay_11">READ CAREFULLY! Setting permissions in Android 11+ is much more complicated!\n\n
This app wants to popup a dialog when it discovers a PHD that it can work with.\n\n
It needs Window-Overlay permissions to do that. Android will start a Settings system activity where it will list all the installed applications.\n\n
You will need to scroll down to Health@Home PHG and tap it. That will bring you to the original pre-11 Settings system overlay permission activity.
Give the permission and use the back arrow to exit. You will need use the back arrow once more to return to Health@Home.</string>
<string name="permissions_location"><p><font color=\"#007700\"><b>LOCATION PERMISSION:</b></font> Needs to be granted in order for this app to use the Bluetooth LE scanner.
The scanner is needed to discover BLE health devices and know what they are.</p>
<p><font color=\"red\"><b>This app does NOT use location information or expose location information!</b></font>
Without this permission you will only be able to work with SPP and HDP devices.</p></string>
<string name="permissions_background"><p>BACKGROUND PERMISSION: Needs to be granted for the Bluetooth LE Scanner to run in the background
to support re-connection without user intervention.</p>
<p>Please select \'Allow all the time\'.</p></string>
*/
public class PermissionsActivity extends AppCompatActivity
{
private final static String TAG = PermissionsActivity.class.getName();
private final static boolean isVersionS_Plus = (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S); // 31
private final static boolean isVersionM_Plus = (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M); // 23 (Marshmallow)
private final static boolean isVersionN_Plus = (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N);
private static final boolean isVersionO_Plus = (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O);
private static final boolean isVersionR = (Build.VERSION.SDK_INT == Build.VERSION_CODES.R); // 30
private static final boolean isVersionR_Plus = (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R); // 30
private static final boolean isVersionOtoR = ((Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) // 26
&& (Build.VERSION.SDK_INT <= Build.VERSION_CODES.R)); // 30
private final List<String> permissionsList = new ArrayList<>();
private final List<String> reasonList = new ArrayList<>();
private int index = 0; // Keeps track of what permission is being requested
private int indexMax; // The maximum number of permissions being asked for (set below)
ActivityResultLauncher<String> activityResultLauncher =
registerForActivityResult(new ActivityResultContracts.RequestPermission(), new ActivityResultCallback<Boolean>()
{
@Override
public void onActivityResult(Boolean result)
{
Log.d(TAG, "HH2: Permission " + permissionsList.get(index) + (result ? " granted" : "rejected"));
index++;
if (index >= indexMax)
{
handlePermissionSummary();
}
else
{
requestPermissions(index);
}
}
});
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
// too lazy to make a layout; your activity very likely has one!
if (handlePermissions())
{
//setLoginScreen(); // Do what needs to be done after permissions are granted or if they already are granted
}
}
// Method checks to see if the needed permissions are granted and if they are it returns true.
// If they are not granted the method returns false but starts the process of asking the user to grant the permissions
@SuppressLint("NewApi")
private boolean handlePermissions()
{
if (isVersionS_Plus) // Android 12 + (31+) Completely new runtime permissions for Bluetooth in Android 12
{ // At least one no longer has to ask for location permissions for Bluetooth completely
// confusing the user.
// Requesting BLUETOOTH_CONNECT, BLUETOOTH_SCAN, and ACCESS_BACKGROUND_LOCATION. The latter is still needed
// to use the BTLE scanner in the background.
// There is a weird bug in Android 12 with respect to the BLUETOOTH_CONNECT and BLUETOOTH_SCAN
// permissions. If you need both, regardless of what order you ask for them in, you get only one
// dialog from Android where the user grants the permission. But you have to ask for both permissions
// (if you need both). See this bug: https://issuetracker.google.com/issues/214434187
// Thus I skip the application dialog by making it empty for the second permission and just request
// the permission. That way both permissions get granted.
if (checkSelfPermission(Manifest.permission.BLUETOOTH_CONNECT) != PackageManager.PERMISSION_GRANTED)
{
permissionsList.add(Manifest.permission.BLUETOOTH_CONNECT);
reasonList.add(getString(R.string.permissions_connect_12));
}
if (checkSelfPermission(Manifest.permission.BLUETOOTH_SCAN) != PackageManager.PERMISSION_GRANTED)
{
permissionsList.add(Manifest.permission.BLUETOOTH_SCAN);
reasonList.add(""); // Work-a-round. If empty, present no dialog explaining the request to the user
//reasonList.add(getString(R.string.permissions_scan_12));
}
if (checkSelfPermission(Manifest.permission.ACCESS_BACKGROUND_LOCATION) != PackageManager.PERMISSION_GRANTED)
{
permissionsList.add(Manifest.permission.ACCESS_BACKGROUND_LOCATION);
reasonList.add(getString(R.string.permissions_background));
}
indexMax = 3; // Need three permissions
}
else if (isVersionM_Plus)
{
// Need location permissions to use the BTLE Scanner. Some versions of Android after 6 require FINE location and
// some require only coarse and some both. TO minimize headache, I always ask for FINE and place COARSE location
// in the Manifest file. That gives you use of the BTLE scanner for all pre-12 versions of Android
if (checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) // Android 6 +
{
permissionsList.add(Manifest.permission.ACCESS_FINE_LOCATION); // Require ACCESS_COARSE_LOCATION in Manifest file as well
reasonList.add(getString(R.string.permissions_location));
indexMax = 1; // Need only one here
}
if (isVersionOtoR) // Android 8 - 11. For these versions need BACKGROUND permission to use the scanner in the background.
{
if (checkSelfPermission(Manifest.permission.ACCESS_BACKGROUND_LOCATION) != PackageManager.PERMISSION_GRANTED)
{
permissionsList.add(Manifest.permission.ACCESS_BACKGROUND_LOCATION);
reasonList.add(getString(R.string.permissions_background));
indexMax = 2; // Need two permissions here
}
}
}
// If no permissions are needed, return true.
if (permissionsList.size() == 0)
{
return true;
}
// Otherwise, begin the permission request sequence which involves launching permission requests.
// The process is asynchronous, so the launch returns immediately.
requestPermissions(index);
return false; // returning false indicates that permissions have not been granted and the request process is ongoing
}
// THis method pops up an application dialog explaining to the user why the application needs the requested permission.
// When the user clicks OK, the permission request is launched. Android pops up whatever system action it dreams up to
// handle the request. Sometimes it is a dialog, and sometimes it is an activity.
// After the user responds, the result is returned in ActivityResultCallback above. The result is a boolean - true if
// granted, false if not. Within the callback, the 'index' is checked. If there are more permissions to request,
// this method is called again. If not, the summary method below is called.
// It's ugly, but it is the only way I have been able to figure out how to place an explanation dialog before each
// Android System action for the permission. Using the multiple permission approach I could not get my dialogs to
// appear before each of the Android actions.
@SuppressLint("NewApi")
private void requestPermissions(int index)
{
if (reasonList.get(index).isEmpty()) // Work-a-round for Android 12. If explanation is empty then
{
// Skip explanation dialog but still request permission. Android pops up no dialog but auto-grants permission.
activityResultLauncher.launch(permissionsList.get(index));
return;
}
// Popup a dialog explaining why the app needs this permission and perhaps what Android is going to put you
// through to grant the permission. For example, for BLUETOOTH_CONNECT/SCAN permissions Android pops up a
// dialog. But for BACKGROUND permissions, Android presents an Activity. Exiting the dialog requires different
// behavior from the user than exiting an activity.
final androidx.appcompat.app.AlertDialog.Builder builder =
new androidx.appcompat.app.AlertDialog.Builder(new ContextThemeWrapper(this, R.style.Theme_AppCompat_Light));
builder.setTitle("Health@Home needs the following permission:");
if (isVersionN_Plus)
{
builder.setMessage(Html.fromHtml(reasonList.get(index), Html.FROM_HTML_MODE_LEGACY));
}
else
{
builder.setMessage(Html.fromHtml(reasonList.get(index)));
}
builder.setPositiveButton(android.R.string.ok, null);
builder.setOnDismissListener(dialog ->
{
Log.v(TAG, "HH2: Requesting permissions");
activityResultLauncher.launch(permissionsList.get(index));
});
builder.show();
}
// THis method just summarizes the results of the permissions.
@SuppressLint("NewApi")
private void handlePermissionSummary()
{
boolean connectOk = (ActivityCompat.checkSelfPermission(this, Manifest.permission.BLUETOOTH_CONNECT) == PackageManager.PERMISSION_GRANTED);
boolean scanOk = (ActivityCompat.checkSelfPermission(this, Manifest.permission.BLUETOOTH_SCAN) == PackageManager.PERMISSION_GRANTED);
boolean locationOk = (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED);
boolean backgroundOk = (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_BACKGROUND_LOCATION) == PackageManager.PERMISSION_GRANTED);
Log.d(TAG, "HH2: BLUETOOTH_CONNECT Permissions are " + connectOk);
Log.d(TAG, "HH2: BLUETOOTH_SCAN Permissions are " + scanOk);
Log.d(TAG, "HH2: ACCESS_FINE_LOCATION Permissions are " + locationOk);
Log.d(TAG, "HH2: ACCESS_BACKGROUND_LOCATION Permissions are " + backgroundOk);
String message = "";
if (!connectOk && isVersionS_Plus)
{
message = "<p><b>Bluetooth Connect permissions not given.</b> You will be unable to find and connect to Bluetooth Low Energy devices</p>";
}
if (!scanOk && isVersionS_Plus)
{
message = "<p><b>Bluetooth Scan permissions not given.</b> You will be unable to find and connect to Bluetooth Low Energy devices</p>";
}
if (!locationOk && !isVersionS_Plus)
{
message = "<p><b>Location permissions not given.</b> You will be unable to find and connect to Bluetooth Low Energy devices</p>";
}
if (!backgroundOk && isVersionO_Plus)
{
message = message + "<p><b>Background permissions not given.</b> Operations with Bluetooth Low Energy devices will only work " +
"while Health@Home PHG is visible on the screen.</p>";
}
if (!message.isEmpty())
{
message = message + "<p>Remedies:<br>1. Restart Health@Home PHG.<br>2. Set permissions directly in the Android Settings menu<br>" +
"3. Uninstall and re-install Health@Home PHG</p>";
final AlertDialog.Builder builder =
new AlertDialog.Builder(new ContextThemeWrapper(this, R.style.Theme_AppCompat_Light));
builder.setTitle("Health@Home not given certain permissions!");
if (isVersionN_Plus)
{
builder.setMessage(Html.fromHtml(message, Html.FROM_HTML_MODE_LEGACY));
}
else
{
builder.setMessage(Html.fromHtml(message));
}
builder.setPositiveButton(android.R.string.ok, null);
builder.setOnDismissListener(dialog ->
{
Log.d(TAG, "Starting Login");
// setLoginScreen(); // Do whatever you would do after permissions are handled
});
builder.show();
return;
}
Log.d(TAG, "Starting Login");
// setLoginScreen(); // Do whatever you would do after permissions are handled
}
}
正如@Brian Reinhold 在其回答中所说,将 ACCESS_BACKGROUND_LOCATION
放入清单中不足以实现“ 始终允许 ”。权限弹出窗口中显示的选项。
您需要做的是在您的代码中明确要求 ACCESS_BACKGROUND_LOCATION
许可,例如:
使用 EasyPermissions 库:
if (!EasyPermissions.hasPermissions(requireContext(), *perms)) {
val builder = PermissionRequest
.Builder(this, REQUEST_LOCATION_PERMISSION, *perms)
.setRationale(getString(R.string.location_disclaimer))
EasyPermissions.requestPermissions(builder.build())
}
companion object {
var perms = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
arrayOf(
Manifest.permission.ACCESS_FINE_LOCATION,
Manifest.permission.ACCESS_BACKGROUND_LOCATION)
} else {
arrayOf(Manifest.permission.ACCESS_FINE_LOCATION)
}
}
要在 android API 29 级或更高级别的设备上访问后台位置,您还需要在清单文件中添加以下权限。
<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"/>
您可以保留全部三个,也可以删除 ACCESS_FINE_LOCATION 或 ACCESS_COARSE_LOCATION,但其中一个必须存在于清单文件中。
添加这些后,如果我们可以通过以下代码块访问所有这些权限,则需要在某个地方签入代码。如果没有,您需要通过构建所有权限的数组并使用基于整数的请求代码来请求权限:
if (requireContext().checkSelfPermission(PERMISSION_NAME_1) != PackageManager.PERMISSION_DENIED && requireContext().checkSelfPermission(PERMISSION_NAME_2) != PackageManager.PERMISSION_DENIED ...) {
requestPermissions(@NonNull String[] permissions, int requestCode)
在清单和 permissions array 中添加“ACCESS_BACKGROUND_LOCATION”。如果您仅在清单中添加权限,则不会显示“始终允许”选项。您需要添加数组以要求用户在运行时授予。
清单中:
<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" />
在你的activity中:
if (ContextCompat.checkSelfPermission( this.applicationContext, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this@MainActivity, arrayOf(Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_BACKGROUND_LOCATION), MY_PERMISSIONS_REQUEST_LOCATION)
} else {
// permission granted
}
正如其他答案所说,你必须使用
<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"/>
但您也必须手动请求用户允许此权限。在 ionic cordova 中你可以这样做:
const accessFineLocationPermission = await this.permissions.checkPermission(this.permissions.PERMISSION.ACCESS_FINE_LOCATION)
if (accessFineLocationPermission && !accessFineLocationPermission.hasPermission) {
await this.permissions.requestPermission(this.permissions.PERMISSION.ACCESS_FINE_LOCATION)
}
使用 android 权限 cordova 插件:
https://ionicframework.com/docs/native/android-permissions
您必须请求 ACCESS_FINE_LOCATION、ACCESS_COARSE_LOCATION 和 ACCESS_BACKGROUND_LOCATION。
位置访问需要两种类型的 Android 权限:
- 前台位置的前台权限(仅在使用应用程序时允许)
- 后台位置的后台权限(始终允许)
如果您的应用请求后台权限,请求应该如下:
- 首先,请求获取前台位置。
- 然后你就可以请求后台位置访问了。
Note: It's recommended that, disable user access for background
location until your app has foreground location access.
那么如何获取后台权限:
第 1 步:在清单中声明这些权限。
<manifest>
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
</manifest>
第二步:检查运行时权限
if (ActivityCompat.checkSelfPermission(
this,
Manifest.permission.ACCESS_FINE_LOCATION
) != PackageManager.PERMISSION_GRANTED
) {
// Request for Foreground permission
ActivityCompat.requestPermissions(
this@YourActivity,
arrayOf(Manifest.permission.ACCESS_FINE_LOCATION),
FINE_LOCATION_REQUEST_CODE
)
} else {
// Request for Background permission
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
ActivityCompat.requestPermissions(
this@MainActivity,
arrayOf(Manifest.permission.ACCESS_BACKGROUND_LOCATION),
BACKGROUND_REQUEST_CODE
)
}
}
我无法在 SDK 29 中得到“始终允许”位置提示。我已经在清单中设置了这些权限:
<uses-permission android:name="com.google.android.providers.gsf.permission.READ_GSERVICES" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
并请求用户在 运行 时允许这些权限。但它只有 returns“当应用程序打开时”和“拒绝”选项。
关于如何在 SDK 29 中显示它的任何想法。
在清单中添加权限 ACCESS_BACKGROUND_LOCATION
。需要在 android 10 及更高版本上始终显示允许选项。
参见https://developer.android.com/training/location/background#evaluate中的第二点
为了在 运行 Android 10(API 级别 29)或更高级别的设备上访问后台位置,您还需要在清单文件中使用以下权限
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
更多信息请参考以下link
https://developer.android.com/training/location/permissions?hl=fr
我发现仅在清单中添加权限是不够的。我必须为用户提供一个在启动时授予它的选项,就像我需要为 ACCESS_FINE_LOCATION 做的一样。有趣的是,当我检查是否已在应用程序中授予后台权限时,它永远不会给出 false。但如果我不检查,我就不会得到“始终允许”选项。这是 Android Pixel 2 版本 10
它只在 Android 的 10+ 版本中是必需的。请求这两种权限都会让用户在对话框中进行选择……当然用户可能不会 select 'All the time'。对此我们无能为力!
我有点不好意思添加我的代码,因为我知道它很糟糕。其他人肯定可以改进它。我找不到避免两次循环访问权限的方法。
/**
* This method lists all the permissions needed and gives reasons for each of them in a single
* dialog. When the user presses ok, Android will popup separate dialogs for each permission
* that is needed. In the case of location, the "Allow all the time" gives background permission
* "Allow only while the app is running gives just the location permission while in foreground.
* @return false if permissions are to be requested, true if they have already been granted.
*
* In Android 11 Build 30 things are a lot different. One has to ask for location first, approve
* the 'while the app is running' option and then upon return in the Activity result, ask for
* the background. Thus the new variable 'secondPassR' to handle this case. After the background
* one comes back to the Activity result and then one can go into the main activity.
*/
static boolean secondPassR = false;
private static final int REQUEST_CODE_MULTIPLE_PERMISSIONS = 57;
private boolean requestPermissions(Activity activity)
{
Log.v(TAG, "requestPermissions() called");
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
{
final List<String> permissionsList = new ArrayList<>();
final List<String> reasonList = new ArrayList<>();
if(!addPermission(permissionsList, Manifest.permission.ACCESS_FINE_LOCATION, activity))
{
reasonList.add("LOCATION PERMISSION: Needs needs to be granted for the Bluetooth " +
" LE scanner to discover devices!\n\n");
}
if ((Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) && (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) || secondPassR)
{
if (!addPermission(permissionsList, Manifest.permission.ACCESS_BACKGROUND_LOCATION, activity))
{
reasonList.add("BACKGROUND PERMISSION: Needs to be granted for the Bluetooth " +
"LE Scanner to run in the background.\n\n");
}
}
if (permissionsList.size() > 0)
{
if (reasonList.size() > 0)
{
// Need Rationale
StringBuilder message = new StringBuilder(reasonList.get(0));
for (int i = 1; i < reasonList.size(); i++)
{
message.append(" ").append(reasonList.get(i));
}
final androidx.appcompat.app.AlertDialog.Builder builder =
new androidx.appcompat.app.AlertDialog.Builder(new ContextThemeWrapper(activity, R.style.Theme_AppCompat_Light));
builder.setTitle("Demo needs the following permissions:");
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
{
builder.setMessage(Html.fromHtml(message.toString(), Html.FROM_HTML_MODE_LEGACY));
}
else
{
builder.setMessage(Html.fromHtml(message.toString()));
}
builder.setPositiveButton(android.R.string.ok, null);
builder.setOnDismissListener(dialog -> {
Log.v(TAG, "Requesting permissions");
activity.requestPermissions(permissionsList.toArray(new String[0]), // newer Java recommended
REQUEST_CODE_MULTIPLE_PERMISSIONS);
});
builder.show();
return false;
}
activity.requestPermissions(permissionsList.toArray(new String[0]), // newer Java recommended
REQUEST_CODE_MULTIPLE_PERMISSIONS);
}
else
{
return true;
}
}
else
{
return true;
}
return false;
}
@TargetApi(23)
private boolean addPermission(List<String> permissionsList, String permission, Activity activity)
{
Log.v(TAG,
"addPermission() called with: " + "permissionsList = " +
"[" + permissionsList + "], permission = [" + permission + "]");
if (ActivityCompat.checkSelfPermission(activity, permission) != PackageManager.PERMISSION_GRANTED)
{
permissionsList.add(permission);
// Check for Rationale Option
return activity.shouldShowRequestPermissionRationale(permission);
}
return true;
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults)
{
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
String permission = "";
Log.v(TAG,
"onRequestPermissionsResult() called with: " + "requestCode = [" + requestCode +
"], permissions = [" + Arrays.toString(permissions) + "]," +
" grantResults = [" + Arrays.toString(grantResults) + "]");
if (requestCode == REQUEST_CODE_MULTIPLE_PERMISSIONS)
{
for (int i = 0; i < permissions.length; i++)
{
switch (permissions[i])
{
case Manifest.permission.ACCESS_FINE_LOCATION:
if (grantResults[i] == PackageManager.PERMISSION_GRANTED)
{
Log.d(TAG, "H@H: onRequestPermissionsResult: FINE LOCATION PERMISSION");
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R)
{
Log.d(TAG, "H@H: Now requesting BACKGROUND PERMISSION for version 11+");
secondPassR = true;
requestPermissions(thisActivity);
return;
}
}
break;
case Manifest.permission.ACCESS_BACKGROUND_LOCATION:
if (grantResults[i] == PackageManager.PERMISSION_GRANTED)
{
Log.d(TAG, "H@H: onRequestPermissionsResult: BACKGROUND PERMISSION");
}
break;
}
}
}
Log.d(TAG, "Starting primary activity");
secondPassR = false;
startActivityForResult(new Intent(context, PchaDemoPhg_Activity.class), EXIT_QUIT);
}
private void showMessageOKCancel(String message, DialogInterface.OnClickListener okListener)
{
new AlertDialog.Builder(this)
.setMessage(message)
.setCancelable(false)
.setPositiveButton("OK", okListener)
.create()
.show();
}
============== 更新 ANDROID 12 和 ActivityResultLauncher =======
随着 Android12 的出现,出现了一组新的权限,我们不再需要请求位置权限才能使用 BTLE 扫描仪。他们还有一种处理 activity 结果的新方法,那就是 ActivityResultLauncher,其中一个内置功能是运行时权限。下面是我现在用来向 ANdroid 6+ 请求蓝牙扫描仪和后台权限的内容。我使用单权限启动器在每个请求之前放置一个解释对话框。用户体验比我以前拥有的要好得多。 (我包括我的字符串资源。)
import android.Manifest;
import android.annotation.SuppressLint;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Bundle;
import android.text.Html;
import android.util.Log;
import androidx.activity.result.ActivityResultCallback;
import androidx.activity.result.ActivityResultLauncher;
import androidx.activity.result.contract.ActivityResultContracts;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.view.ContextThemeWrapper;
import androidx.core.app.ActivityCompat;
import java.util.ArrayList;
import java.util.List;
/*
resource strings used:
<string name="permissions_connect_12"><p>This app needs permission to connect to Bluetooth devices</p> </string>
<string name="permissions_scan_12"><p>To search for Bluetooth devices this app needs permission to use the BLE scanner.</p> </string>
<string name="permissions_overlay_11">READ CAREFULLY! Setting permissions in Android 11+ is much more complicated!\n\n
This app wants to popup a dialog when it discovers a PHD that it can work with.\n\n
It needs Window-Overlay permissions to do that. Android will start a Settings system activity where it will list all the installed applications.\n\n
You will need to scroll down to Health@Home PHG and tap it. That will bring you to the original pre-11 Settings system overlay permission activity.
Give the permission and use the back arrow to exit. You will need use the back arrow once more to return to Health@Home.</string>
<string name="permissions_location"><p><font color=\"#007700\"><b>LOCATION PERMISSION:</b></font> Needs to be granted in order for this app to use the Bluetooth LE scanner.
The scanner is needed to discover BLE health devices and know what they are.</p>
<p><font color=\"red\"><b>This app does NOT use location information or expose location information!</b></font>
Without this permission you will only be able to work with SPP and HDP devices.</p></string>
<string name="permissions_background"><p>BACKGROUND PERMISSION: Needs to be granted for the Bluetooth LE Scanner to run in the background
to support re-connection without user intervention.</p>
<p>Please select \'Allow all the time\'.</p></string>
*/
public class PermissionsActivity extends AppCompatActivity
{
private final static String TAG = PermissionsActivity.class.getName();
private final static boolean isVersionS_Plus = (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S); // 31
private final static boolean isVersionM_Plus = (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M); // 23 (Marshmallow)
private final static boolean isVersionN_Plus = (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N);
private static final boolean isVersionO_Plus = (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O);
private static final boolean isVersionR = (Build.VERSION.SDK_INT == Build.VERSION_CODES.R); // 30
private static final boolean isVersionR_Plus = (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R); // 30
private static final boolean isVersionOtoR = ((Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) // 26
&& (Build.VERSION.SDK_INT <= Build.VERSION_CODES.R)); // 30
private final List<String> permissionsList = new ArrayList<>();
private final List<String> reasonList = new ArrayList<>();
private int index = 0; // Keeps track of what permission is being requested
private int indexMax; // The maximum number of permissions being asked for (set below)
ActivityResultLauncher<String> activityResultLauncher =
registerForActivityResult(new ActivityResultContracts.RequestPermission(), new ActivityResultCallback<Boolean>()
{
@Override
public void onActivityResult(Boolean result)
{
Log.d(TAG, "HH2: Permission " + permissionsList.get(index) + (result ? " granted" : "rejected"));
index++;
if (index >= indexMax)
{
handlePermissionSummary();
}
else
{
requestPermissions(index);
}
}
});
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
// too lazy to make a layout; your activity very likely has one!
if (handlePermissions())
{
//setLoginScreen(); // Do what needs to be done after permissions are granted or if they already are granted
}
}
// Method checks to see if the needed permissions are granted and if they are it returns true.
// If they are not granted the method returns false but starts the process of asking the user to grant the permissions
@SuppressLint("NewApi")
private boolean handlePermissions()
{
if (isVersionS_Plus) // Android 12 + (31+) Completely new runtime permissions for Bluetooth in Android 12
{ // At least one no longer has to ask for location permissions for Bluetooth completely
// confusing the user.
// Requesting BLUETOOTH_CONNECT, BLUETOOTH_SCAN, and ACCESS_BACKGROUND_LOCATION. The latter is still needed
// to use the BTLE scanner in the background.
// There is a weird bug in Android 12 with respect to the BLUETOOTH_CONNECT and BLUETOOTH_SCAN
// permissions. If you need both, regardless of what order you ask for them in, you get only one
// dialog from Android where the user grants the permission. But you have to ask for both permissions
// (if you need both). See this bug: https://issuetracker.google.com/issues/214434187
// Thus I skip the application dialog by making it empty for the second permission and just request
// the permission. That way both permissions get granted.
if (checkSelfPermission(Manifest.permission.BLUETOOTH_CONNECT) != PackageManager.PERMISSION_GRANTED)
{
permissionsList.add(Manifest.permission.BLUETOOTH_CONNECT);
reasonList.add(getString(R.string.permissions_connect_12));
}
if (checkSelfPermission(Manifest.permission.BLUETOOTH_SCAN) != PackageManager.PERMISSION_GRANTED)
{
permissionsList.add(Manifest.permission.BLUETOOTH_SCAN);
reasonList.add(""); // Work-a-round. If empty, present no dialog explaining the request to the user
//reasonList.add(getString(R.string.permissions_scan_12));
}
if (checkSelfPermission(Manifest.permission.ACCESS_BACKGROUND_LOCATION) != PackageManager.PERMISSION_GRANTED)
{
permissionsList.add(Manifest.permission.ACCESS_BACKGROUND_LOCATION);
reasonList.add(getString(R.string.permissions_background));
}
indexMax = 3; // Need three permissions
}
else if (isVersionM_Plus)
{
// Need location permissions to use the BTLE Scanner. Some versions of Android after 6 require FINE location and
// some require only coarse and some both. TO minimize headache, I always ask for FINE and place COARSE location
// in the Manifest file. That gives you use of the BTLE scanner for all pre-12 versions of Android
if (checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) // Android 6 +
{
permissionsList.add(Manifest.permission.ACCESS_FINE_LOCATION); // Require ACCESS_COARSE_LOCATION in Manifest file as well
reasonList.add(getString(R.string.permissions_location));
indexMax = 1; // Need only one here
}
if (isVersionOtoR) // Android 8 - 11. For these versions need BACKGROUND permission to use the scanner in the background.
{
if (checkSelfPermission(Manifest.permission.ACCESS_BACKGROUND_LOCATION) != PackageManager.PERMISSION_GRANTED)
{
permissionsList.add(Manifest.permission.ACCESS_BACKGROUND_LOCATION);
reasonList.add(getString(R.string.permissions_background));
indexMax = 2; // Need two permissions here
}
}
}
// If no permissions are needed, return true.
if (permissionsList.size() == 0)
{
return true;
}
// Otherwise, begin the permission request sequence which involves launching permission requests.
// The process is asynchronous, so the launch returns immediately.
requestPermissions(index);
return false; // returning false indicates that permissions have not been granted and the request process is ongoing
}
// THis method pops up an application dialog explaining to the user why the application needs the requested permission.
// When the user clicks OK, the permission request is launched. Android pops up whatever system action it dreams up to
// handle the request. Sometimes it is a dialog, and sometimes it is an activity.
// After the user responds, the result is returned in ActivityResultCallback above. The result is a boolean - true if
// granted, false if not. Within the callback, the 'index' is checked. If there are more permissions to request,
// this method is called again. If not, the summary method below is called.
// It's ugly, but it is the only way I have been able to figure out how to place an explanation dialog before each
// Android System action for the permission. Using the multiple permission approach I could not get my dialogs to
// appear before each of the Android actions.
@SuppressLint("NewApi")
private void requestPermissions(int index)
{
if (reasonList.get(index).isEmpty()) // Work-a-round for Android 12. If explanation is empty then
{
// Skip explanation dialog but still request permission. Android pops up no dialog but auto-grants permission.
activityResultLauncher.launch(permissionsList.get(index));
return;
}
// Popup a dialog explaining why the app needs this permission and perhaps what Android is going to put you
// through to grant the permission. For example, for BLUETOOTH_CONNECT/SCAN permissions Android pops up a
// dialog. But for BACKGROUND permissions, Android presents an Activity. Exiting the dialog requires different
// behavior from the user than exiting an activity.
final androidx.appcompat.app.AlertDialog.Builder builder =
new androidx.appcompat.app.AlertDialog.Builder(new ContextThemeWrapper(this, R.style.Theme_AppCompat_Light));
builder.setTitle("Health@Home needs the following permission:");
if (isVersionN_Plus)
{
builder.setMessage(Html.fromHtml(reasonList.get(index), Html.FROM_HTML_MODE_LEGACY));
}
else
{
builder.setMessage(Html.fromHtml(reasonList.get(index)));
}
builder.setPositiveButton(android.R.string.ok, null);
builder.setOnDismissListener(dialog ->
{
Log.v(TAG, "HH2: Requesting permissions");
activityResultLauncher.launch(permissionsList.get(index));
});
builder.show();
}
// THis method just summarizes the results of the permissions.
@SuppressLint("NewApi")
private void handlePermissionSummary()
{
boolean connectOk = (ActivityCompat.checkSelfPermission(this, Manifest.permission.BLUETOOTH_CONNECT) == PackageManager.PERMISSION_GRANTED);
boolean scanOk = (ActivityCompat.checkSelfPermission(this, Manifest.permission.BLUETOOTH_SCAN) == PackageManager.PERMISSION_GRANTED);
boolean locationOk = (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED);
boolean backgroundOk = (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_BACKGROUND_LOCATION) == PackageManager.PERMISSION_GRANTED);
Log.d(TAG, "HH2: BLUETOOTH_CONNECT Permissions are " + connectOk);
Log.d(TAG, "HH2: BLUETOOTH_SCAN Permissions are " + scanOk);
Log.d(TAG, "HH2: ACCESS_FINE_LOCATION Permissions are " + locationOk);
Log.d(TAG, "HH2: ACCESS_BACKGROUND_LOCATION Permissions are " + backgroundOk);
String message = "";
if (!connectOk && isVersionS_Plus)
{
message = "<p><b>Bluetooth Connect permissions not given.</b> You will be unable to find and connect to Bluetooth Low Energy devices</p>";
}
if (!scanOk && isVersionS_Plus)
{
message = "<p><b>Bluetooth Scan permissions not given.</b> You will be unable to find and connect to Bluetooth Low Energy devices</p>";
}
if (!locationOk && !isVersionS_Plus)
{
message = "<p><b>Location permissions not given.</b> You will be unable to find and connect to Bluetooth Low Energy devices</p>";
}
if (!backgroundOk && isVersionO_Plus)
{
message = message + "<p><b>Background permissions not given.</b> Operations with Bluetooth Low Energy devices will only work " +
"while Health@Home PHG is visible on the screen.</p>";
}
if (!message.isEmpty())
{
message = message + "<p>Remedies:<br>1. Restart Health@Home PHG.<br>2. Set permissions directly in the Android Settings menu<br>" +
"3. Uninstall and re-install Health@Home PHG</p>";
final AlertDialog.Builder builder =
new AlertDialog.Builder(new ContextThemeWrapper(this, R.style.Theme_AppCompat_Light));
builder.setTitle("Health@Home not given certain permissions!");
if (isVersionN_Plus)
{
builder.setMessage(Html.fromHtml(message, Html.FROM_HTML_MODE_LEGACY));
}
else
{
builder.setMessage(Html.fromHtml(message));
}
builder.setPositiveButton(android.R.string.ok, null);
builder.setOnDismissListener(dialog ->
{
Log.d(TAG, "Starting Login");
// setLoginScreen(); // Do whatever you would do after permissions are handled
});
builder.show();
return;
}
Log.d(TAG, "Starting Login");
// setLoginScreen(); // Do whatever you would do after permissions are handled
}
}
正如@Brian Reinhold 在其回答中所说,将 ACCESS_BACKGROUND_LOCATION
放入清单中不足以实现“ 始终允许 ”。权限弹出窗口中显示的选项。
您需要做的是在您的代码中明确要求 ACCESS_BACKGROUND_LOCATION
许可,例如:
使用 EasyPermissions 库:
if (!EasyPermissions.hasPermissions(requireContext(), *perms)) {
val builder = PermissionRequest
.Builder(this, REQUEST_LOCATION_PERMISSION, *perms)
.setRationale(getString(R.string.location_disclaimer))
EasyPermissions.requestPermissions(builder.build())
}
companion object {
var perms = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
arrayOf(
Manifest.permission.ACCESS_FINE_LOCATION,
Manifest.permission.ACCESS_BACKGROUND_LOCATION)
} else {
arrayOf(Manifest.permission.ACCESS_FINE_LOCATION)
}
}
要在 android API 29 级或更高级别的设备上访问后台位置,您还需要在清单文件中添加以下权限。
<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"/>
您可以保留全部三个,也可以删除 ACCESS_FINE_LOCATION 或 ACCESS_COARSE_LOCATION,但其中一个必须存在于清单文件中。
添加这些后,如果我们可以通过以下代码块访问所有这些权限,则需要在某个地方签入代码。如果没有,您需要通过构建所有权限的数组并使用基于整数的请求代码来请求权限:
if (requireContext().checkSelfPermission(PERMISSION_NAME_1) != PackageManager.PERMISSION_DENIED && requireContext().checkSelfPermission(PERMISSION_NAME_2) != PackageManager.PERMISSION_DENIED ...) {
requestPermissions(@NonNull String[] permissions, int requestCode)
在清单和 permissions array 中添加“ACCESS_BACKGROUND_LOCATION”。如果您仅在清单中添加权限,则不会显示“始终允许”选项。您需要添加数组以要求用户在运行时授予。
清单中:
<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" />
在你的activity中:
if (ContextCompat.checkSelfPermission( this.applicationContext, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this@MainActivity, arrayOf(Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_BACKGROUND_LOCATION), MY_PERMISSIONS_REQUEST_LOCATION)
} else {
// permission granted
}
正如其他答案所说,你必须使用
<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"/>
但您也必须手动请求用户允许此权限。在 ionic cordova 中你可以这样做:
const accessFineLocationPermission = await this.permissions.checkPermission(this.permissions.PERMISSION.ACCESS_FINE_LOCATION)
if (accessFineLocationPermission && !accessFineLocationPermission.hasPermission) {
await this.permissions.requestPermission(this.permissions.PERMISSION.ACCESS_FINE_LOCATION)
}
使用 android 权限 cordova 插件: https://ionicframework.com/docs/native/android-permissions
您必须请求 ACCESS_FINE_LOCATION、ACCESS_COARSE_LOCATION 和 ACCESS_BACKGROUND_LOCATION。
位置访问需要两种类型的 Android 权限:
- 前台位置的前台权限(仅在使用应用程序时允许)
- 后台位置的后台权限(始终允许)
如果您的应用请求后台权限,请求应该如下:
- 首先,请求获取前台位置。
- 然后你就可以请求后台位置访问了。
Note: It's recommended that, disable user access for background location until your app has foreground location access.
那么如何获取后台权限:
第 1 步:在清单中声明这些权限。
<manifest>
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
</manifest>
第二步:检查运行时权限
if (ActivityCompat.checkSelfPermission(
this,
Manifest.permission.ACCESS_FINE_LOCATION
) != PackageManager.PERMISSION_GRANTED
) {
// Request for Foreground permission
ActivityCompat.requestPermissions(
this@YourActivity,
arrayOf(Manifest.permission.ACCESS_FINE_LOCATION),
FINE_LOCATION_REQUEST_CODE
)
} else {
// Request for Background permission
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
ActivityCompat.requestPermissions(
this@MainActivity,
arrayOf(Manifest.permission.ACCESS_BACKGROUND_LOCATION),
BACKGROUND_REQUEST_CODE
)
}
}