Android 不允许使用 SDK 更新创建目录
Android does not allow directory creation with SDK update
奇怪的问题,但在一台设备上 Android 不再允许应用程序创建目录(或任何目录)。这似乎发生在我移动到 SDK 27 之后(我引入了一个需要升级的第三方库)。更奇怪的是,它适用于其他设备。不工作的设备是 Samsung Galaxy Tab S3,运行 Android 8 (sdk 26)。
这是失败的代码:
public static String mediaStorageDirectory() {
return Environment.getExternalStorageDirectory().toString() + File.separator + "myapp";
}
private DatabaseHelper(Context context) {
super(context, DB_NAME, null, 1);
File dbPath = new File(Utilities.mediaStorageDirectory() + "/databases/");
if (!dbPath.exists()) {
if (dbPath.mkdirs()) {
this.mContext = context;
createDataBase();
}
}
}
代码创建一个目录,然后将应用程序数据库复制到该目录中。此代码从一开始就一直有效...是否 Android 更改了文件创建的一些安全要求?
如果你想知道,我已经设置了如下权限:
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
并在创建目录结构之前提示用户成功获得这些权限。
所以问题似乎与 SDK 27 有关。他们现在似乎要求(强制?)授予 WRITE 的权限。以前只需要 READ。我假设这与安全升级有关,但我找不到有关此更改的任何文档。也许其他人之前只是做了包含。对于那些想查看执行权限处理的代码的人,我不久前从另一个 Stack Overflow 中提取了以下代码。我只是添加了之前缺少的 WRITE_EXTERNAL_STORAGE 部分:
final private int REQUEST_CODE_ASK_MULTIPLE_PERMISSIONS = 124;
public void checkPermissions() {
List<String> permissionsNeeded = new ArrayList<String>();
final List<String> permissionsList = new ArrayList<String>();
if (!addPermission(permissionsList, Manifest.permission.CAMERA))
permissionsNeeded.add("Camera");
if (!addPermission(permissionsList, Manifest.permission.READ_EXTERNAL_STORAGE))
permissionsNeeded.add("Read External Storage");
if (!addPermission(permissionsList, Manifest.permission.WRITE_EXTERNAL_STORAGE))
permissionsNeeded.add("Write External Storage");
if (!addPermission(permissionsList, Manifest.permission.ACCESS_FINE_LOCATION))
permissionsNeeded.add("GPS");
if (!addPermission(permissionsList, Manifest.permission.RECORD_AUDIO))
permissionsNeeded.add("Record Audio");
if (permissionsList.size() > 0) {
if (permissionsNeeded.size() > 0) {
// Need Rationale
String message = "You need to grant access to " + permissionsNeeded.get(0);
for (int i = 1; i < permissionsNeeded.size(); i++) {
message = message + ", " + permissionsNeeded.get(i);
}
message = message + "." + " If you do not the application may not function correctly.";
showMessageOKCancel(message, this,
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
requestPermissions(permissionsList.toArray(new String[permissionsList.size()]),
REQUEST_CODE_ASK_MULTIPLE_PERMISSIONS);
}
});
} else {
requestPermissions(permissionsList.toArray(new String[permissionsList.size()]),
REQUEST_CODE_ASK_MULTIPLE_PERMISSIONS);
}
}
}
private static void showMessageOKCancel(String message, Activity activity, DialogInterface.OnClickListener okListener) {
new AlertDialog.Builder(activity).setMessage(message).setPositiveButton("OK", okListener).create().show();
}
private boolean addPermission(List<String> permissionsList, String permission) {
if (checkSelfPermission(permission) != PackageManager.PERMISSION_GRANTED) {
permissionsList.add(permission);
// Check for Rationale Option
if (!shouldShowRequestPermissionRationale(permission))
return false;
}
return true;
}
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
switch (requestCode) {
case REQUEST_CODE_ASK_MULTIPLE_PERMISSIONS: {
Map<String, Integer> perms = new HashMap<String, Integer>();
// Initial
perms.put(Manifest.permission.ACCESS_FINE_LOCATION, PackageManager.PERMISSION_GRANTED);
perms.put(Manifest.permission.CAMERA, PackageManager.PERMISSION_GRANTED);
perms.put(Manifest.permission.RECORD_AUDIO, PackageManager.PERMISSION_GRANTED);
perms.put(Manifest.permission.READ_EXTERNAL_STORAGE, PackageManager.PERMISSION_GRANTED);
perms.put(Manifest.permission.WRITE_EXTERNAL_STORAGE, PackageManager.PERMISSION_GRANTED);
// Fill with results
for (int i = 0; i < permissions.length; i++)
perms.put(permissions[i], grantResults[i]);
// Check for ACCESS_FINE_LOCATION
if (perms.get(Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED
&& perms.get(Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED
&& perms.get(Manifest.permission.RECORD_AUDIO) == PackageManager.PERMISSION_GRANTED
&& perms.get(Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED
&& perms.get(Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) {
// All Permissions Granted
}
// Warn the user that the application will not run
else if (perms.get(Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
AlertDialog.Builder builder = new AlertDialog.Builder(StartActivity.this);
builder.setTitle("File Read Permissions Not Granted");
builder.setMessage("The Application cannot operate without READ access to files on your device. Do you want to grant this access?");
builder.setPositiveButton("Yes", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
ActivityCompat.requestPermissions(StartActivity.this, new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, 0);
}
});
builder.setNegativeButton("No", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int id) {
}
});
if (!isFinishing())
builder.show();
}
// Warn the user that the application will not run
else if (perms.get(Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
AlertDialog.Builder builder = new AlertDialog.Builder(StartActivity.this);
builder.setTitle("File Write Permissions Not Granted");
builder.setMessage("The Application cannot operate without WRITE access to directories on your device. Do you want to grant this access?");
builder.setPositiveButton("Yes", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
ActivityCompat.requestPermissions(StartActivity.this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, 0);
}
});
builder.setNegativeButton("No", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int id) {
}
});
if (!isFinishing())
builder.show();
}else {
// Permission Denied
Toast.makeText(StartActivity.this, "Some Permission is Denied", Toast.LENGTH_SHORT)
.show();
}
}
break;
default:
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
}
}
该调用是特定于 SDK 的,已添加到主 activity:
的 OnCreate()
// Check permissions for Android 6.0 devices
if (Build.VERSION.SDK_INT >= 23) {
// Marshmallow+
checkPermissions();
}
奇怪的问题,但在一台设备上 Android 不再允许应用程序创建目录(或任何目录)。这似乎发生在我移动到 SDK 27 之后(我引入了一个需要升级的第三方库)。更奇怪的是,它适用于其他设备。不工作的设备是 Samsung Galaxy Tab S3,运行 Android 8 (sdk 26)。
这是失败的代码:
public static String mediaStorageDirectory() {
return Environment.getExternalStorageDirectory().toString() + File.separator + "myapp";
}
private DatabaseHelper(Context context) {
super(context, DB_NAME, null, 1);
File dbPath = new File(Utilities.mediaStorageDirectory() + "/databases/");
if (!dbPath.exists()) {
if (dbPath.mkdirs()) {
this.mContext = context;
createDataBase();
}
}
}
代码创建一个目录,然后将应用程序数据库复制到该目录中。此代码从一开始就一直有效...是否 Android 更改了文件创建的一些安全要求?
如果你想知道,我已经设置了如下权限:
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
并在创建目录结构之前提示用户成功获得这些权限。
所以问题似乎与 SDK 27 有关。他们现在似乎要求(强制?)授予 WRITE 的权限。以前只需要 READ。我假设这与安全升级有关,但我找不到有关此更改的任何文档。也许其他人之前只是做了包含。对于那些想查看执行权限处理的代码的人,我不久前从另一个 Stack Overflow 中提取了以下代码。我只是添加了之前缺少的 WRITE_EXTERNAL_STORAGE 部分:
final private int REQUEST_CODE_ASK_MULTIPLE_PERMISSIONS = 124;
public void checkPermissions() {
List<String> permissionsNeeded = new ArrayList<String>();
final List<String> permissionsList = new ArrayList<String>();
if (!addPermission(permissionsList, Manifest.permission.CAMERA))
permissionsNeeded.add("Camera");
if (!addPermission(permissionsList, Manifest.permission.READ_EXTERNAL_STORAGE))
permissionsNeeded.add("Read External Storage");
if (!addPermission(permissionsList, Manifest.permission.WRITE_EXTERNAL_STORAGE))
permissionsNeeded.add("Write External Storage");
if (!addPermission(permissionsList, Manifest.permission.ACCESS_FINE_LOCATION))
permissionsNeeded.add("GPS");
if (!addPermission(permissionsList, Manifest.permission.RECORD_AUDIO))
permissionsNeeded.add("Record Audio");
if (permissionsList.size() > 0) {
if (permissionsNeeded.size() > 0) {
// Need Rationale
String message = "You need to grant access to " + permissionsNeeded.get(0);
for (int i = 1; i < permissionsNeeded.size(); i++) {
message = message + ", " + permissionsNeeded.get(i);
}
message = message + "." + " If you do not the application may not function correctly.";
showMessageOKCancel(message, this,
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
requestPermissions(permissionsList.toArray(new String[permissionsList.size()]),
REQUEST_CODE_ASK_MULTIPLE_PERMISSIONS);
}
});
} else {
requestPermissions(permissionsList.toArray(new String[permissionsList.size()]),
REQUEST_CODE_ASK_MULTIPLE_PERMISSIONS);
}
}
}
private static void showMessageOKCancel(String message, Activity activity, DialogInterface.OnClickListener okListener) {
new AlertDialog.Builder(activity).setMessage(message).setPositiveButton("OK", okListener).create().show();
}
private boolean addPermission(List<String> permissionsList, String permission) {
if (checkSelfPermission(permission) != PackageManager.PERMISSION_GRANTED) {
permissionsList.add(permission);
// Check for Rationale Option
if (!shouldShowRequestPermissionRationale(permission))
return false;
}
return true;
}
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
switch (requestCode) {
case REQUEST_CODE_ASK_MULTIPLE_PERMISSIONS: {
Map<String, Integer> perms = new HashMap<String, Integer>();
// Initial
perms.put(Manifest.permission.ACCESS_FINE_LOCATION, PackageManager.PERMISSION_GRANTED);
perms.put(Manifest.permission.CAMERA, PackageManager.PERMISSION_GRANTED);
perms.put(Manifest.permission.RECORD_AUDIO, PackageManager.PERMISSION_GRANTED);
perms.put(Manifest.permission.READ_EXTERNAL_STORAGE, PackageManager.PERMISSION_GRANTED);
perms.put(Manifest.permission.WRITE_EXTERNAL_STORAGE, PackageManager.PERMISSION_GRANTED);
// Fill with results
for (int i = 0; i < permissions.length; i++)
perms.put(permissions[i], grantResults[i]);
// Check for ACCESS_FINE_LOCATION
if (perms.get(Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED
&& perms.get(Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED
&& perms.get(Manifest.permission.RECORD_AUDIO) == PackageManager.PERMISSION_GRANTED
&& perms.get(Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED
&& perms.get(Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) {
// All Permissions Granted
}
// Warn the user that the application will not run
else if (perms.get(Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
AlertDialog.Builder builder = new AlertDialog.Builder(StartActivity.this);
builder.setTitle("File Read Permissions Not Granted");
builder.setMessage("The Application cannot operate without READ access to files on your device. Do you want to grant this access?");
builder.setPositiveButton("Yes", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
ActivityCompat.requestPermissions(StartActivity.this, new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, 0);
}
});
builder.setNegativeButton("No", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int id) {
}
});
if (!isFinishing())
builder.show();
}
// Warn the user that the application will not run
else if (perms.get(Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
AlertDialog.Builder builder = new AlertDialog.Builder(StartActivity.this);
builder.setTitle("File Write Permissions Not Granted");
builder.setMessage("The Application cannot operate without WRITE access to directories on your device. Do you want to grant this access?");
builder.setPositiveButton("Yes", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
ActivityCompat.requestPermissions(StartActivity.this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, 0);
}
});
builder.setNegativeButton("No", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int id) {
}
});
if (!isFinishing())
builder.show();
}else {
// Permission Denied
Toast.makeText(StartActivity.this, "Some Permission is Denied", Toast.LENGTH_SHORT)
.show();
}
}
break;
default:
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
}
}
该调用是特定于 SDK 的,已添加到主 activity:
的 OnCreate() // Check permissions for Android 6.0 devices
if (Build.VERSION.SDK_INT >= 23) {
// Marshmallow+
checkPermissions();
}