我正在尝试使用Android虚拟设备(AVD)Nexus 7(2012)API 29在Android Studio 3.4.1上使用非常基本的相机应用程序的源代码。我在模拟器中使用相机拍摄了图片并尝试保存它作为/storage/emulated/0/Pictures/myPic.png,但/storage/emulated/0/Pictures/myPic.png打开例外:EACCES(权限被拒绝)。我尝试了所有在线可用的建议,例如:
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
public class MainActivity extends Activity {
private static final int REQUEST_EXTERNAL_STORAGE = 1;
private static String[] PERMISSIONS_STORAGE = {
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.WRITE_EXTERNAL_STORAGE
};
public static void verifyStoragePermissions(Activity activity) {
int permission = ActivityCompat.checkSelfPermission(activity, Manifest.permission.WRITE_EXTERNAL_STORAGE);
boolean checkPermission = (permission == PackageManager.PERMISSION_GRANTED);
Toast toastPermission = Toast.makeText(activity,
"Is permission granted? " + checkPermission,
Toast.LENGTH_SHORT);
LinearLayout toastLayoutPermission = (LinearLayout) toastPermission.getView();
TextView toastTVPermission = (TextView) toastLayoutPermission.getChildAt(0);
toastTVPermission.setTextSize(30);
toastPermission.show();
if (permission != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(
activity,
PERMISSIONS_STORAGE,
REQUEST_EXTERNAL_STORAGE
);
}
}
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
verifyStoragePermissions(this);
......
private void saveImageToFile(File file)
{
if (mCameraBitmap != null)
{
FileOutputStream outStream = null;
verifyStoragePermissions(this);
try
{
outStream = new FileOutputStream(file);
...
}
catch (Exception e)
{
Toast toastException = Toast.makeText(MainActivity.this,
"Exception: " + e.getMessage(),
Toast.LENGTH_SHORT);
LinearLayout toastLayoutException = (LinearLayout) toastException.getView();
TextView toastTVException = (TextView) toastLayoutException.getChildAt(0);
toastTVException.setTextSize(30);
toastException.show();
}
...
但是,所有建议都无效!
代码抛出了一个异常
/storage/emulated/0/Pictures/myPic.png打开失败:EACCES(权限被拒绝)
到达
行时outStream = new FileOutputStream(file);
有进一步解决这个问题的想法吗?非常感谢。
答案 0 :(得分:5)
您的问题是由于在API 29中引入了scoped storage。您有3种解决方案(已经提到了一些解决方案,但是我认为这将有助于澄清和巩固):
仅在运行API 28及以下版本的设备上运行您的应用。
向AndroidManifest.xml添加android:requestLegacyExternalStorage="true"
,以便您的应用可以在API 29及更高版本上运行:
<应用程序 android:requestLegacyExternalStorage =“ true” ...
针对API 29+现代化您的应用。首先摆脱WRITE_EXTERNAL_STORAGE权限。并将对不推荐使用的Environment.getExternalStoragePublicDirectory()
的调用替换为getExternalFilesDir()
,这会将您的图片保存在外部存储设备中,但仅在您的应用可以访问的目录中。
以下是使用MediaStore将位图另存为JPG的方法,该方法允许其他应用访问图像:
private void saveImage(Bitmap bitmap) throws IOException {
ContentValues contentValues = new ContentValues();
contentValues.put(MediaStore.MediaColumns.DISPLAY_NAME, "myImage.jpg");
contentValues.put(MediaStore.MediaColumns.MIME_TYPE, "image/jpeg");
contentValues.put(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_PICTURES);
ContentResolver resolver = getContentResolver();
Uri uri = resolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues);
OutputStream imageOutStream = null;
try {
if (uri == null) {
throw new IOException("Failed to insert MediaStore row");
}
imageOutStream = resolver.openOutputStream(uri);
if (!bitmap.compress(Bitmap.CompressFormat.JPEG, 100, imageOutStream)) {
throw new IOException("Failed to compress bitmap");
}
}
finally {
if (imageOutStream != null) {
imageOutStream.close();
}
}
}
答案 1 :(得分:4)
在Android Manifest中添加 android:requestLegacyExternalStorage =“ true” 对我来说适用于Android 29
<application
android:name=".MyApplication"
android:allowBackup="true"
android:requestLegacyExternalStorage="true" ...
答案 2 :(得分:4)
我还尝试使用相机并将照片保存到ImageView中,但权限出现问题。我将API从29更改为28-没有帮助,添加
android:requestLegacyExternalStorage="true"
没有帮助。 我更改了用于创建用于存储图像的文件的方法:
File storageDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES);
收件人:
File storageDir = getExternalFilesDir(Environment.DIRECTORY_PICTURES);
问题消失了。顺便说一句,我不知道为什么。也许是因为第一个不推荐使用。
答案 3 :(得分:0)
我最终发现问题出在Android API 29,一旦我降级到API 28,就不再有权限问题。
答案 4 :(得分:0)
答案 5 :(得分:0)
将android:requestLegacyExternalStorage="true"
添加到您的Android清单
<manifest ... >
<application android:requestLegacyExternalStorage="true" ... />
</manifest>
原因,您可以阅读https://medium.com/@sriramaripirala/android-10-open-failed-eacces-permission-denied-da8b630a89df