下载管理器Android权限错误:写外部存储

20

我编写了一个安卓应用程序,使用DownloadManager下载一个示例PDF文件到下载目录。以下是代码:

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_vertretungsplan);
    Button dlbutton = (Button) findViewById(R.id.buttondownload);
    final Context c = this;
    dlbutton.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            DownloadManager.Request request = new DownloadManager.Request(Uri.parse(myurl));
            request.setTitle("Vertretungsplan");
            request.setDescription("wird heruntergeladen");
            request.allowScanningByMediaScanner();
            request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
            String filename = URLUtil.guessFileName(myurl, null, MimeTypeMap.getFileExtensionFromUrl(myurl));
            request.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, filename);
            DownloadManager manager = (DownloadManager) c.getSystemService(Context.DOWNLOAD_SERVICE);
            manager.enqueue(request);
        }
    });
}

现在,我尝试从Android Studio在我的Xperia Z3 Android 6.0上进行调试运行。

当我点击下载按钮时,出现以下错误:

E/AndroidRuntime: FATAL EXCEPTION: main
              Process: com.example.markwitt.schul_app, PID: 29297
              java.lang.SecurityException: No permission to write to /storage/emulated/0/Download/hrpsampletable.pdf: Neither user 10047 nor current process has android.permission.WRITE_EXTERNAL_STORAGE.
                  at android.os.Parcel.readException(Parcel.java:1627)
                  at android.database.DatabaseUtils.readExceptionFromParcel(DatabaseUtils.java:183)
                  at android.database.DatabaseUtils.readExceptionFromParcel(DatabaseUtils.java:135)
                  at android.content.ContentProviderProxy.insert(ContentProviderNative.java:476)
                  at android.content.ContentResolver.insert(ContentResolver.java:1240)
                  at android.app.DownloadManager.enqueue(DownloadManager.java:946)
                  at com.example.markwitt.schul_app.Vertretungsplan$1.onClick(Vertretungsplan.java:59)
                  at android.view.View.performClick(View.java:5280)
                  at android.view.View$PerformClick.run(View.java:21239)
                  at android.os.Handler.handleCallback(Handler.java:739)
                  at android.os.Handler.dispatchMessage(Handler.java:95)
                  at android.os.Looper.loop(Looper.java:224)
                  at android.app.ActivityThread.main(ActivityThread.java:5526)
                  at java.lang.reflect.Method.invoke(Native Method)
                  at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726)
                  at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)

无法连接到目标虚拟机,地址:'localhost:8600',传输方式:'socket'

(在第三行)它显示我在外部存储上没有写入权限。

但我的AndroidManifest已经正确设置:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.markwitt.schul_app">
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.INTERNET" />
<application
    android:launchMode="singleTop"
    android:allowBackup="true"
    android:icon="@mipmap/ic_launcher"
    android:label="@string/app_name"
    android:supportsRtl="true"
    android:theme="@style/AppTheme">

    <activity android:name=".Stundenplan">


        <intent-filter>
            <action android:name="android.intent.action.MAIN" />

            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
    </activity>
    <activity android:name=".Vertretungsplan"></activity>
</application>

我需要做什么?


https://dev59.com/fFwY5IYBdhLWcg3waXS5 - CommonsWare
4个回答

39

你遇到这个错误是因为你的应用正在安卓6.0(API级别23)上运行。从API级别>=23开始,你需要在运行时检查权限。如果API级别在23以下,你的代码是没有问题的。所以请先检查一下用户是否已经授权使用存储空间:

if (checkSelfPermission(android.Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) {
    Log.e("Permission error","You have permission");
    return true;
}

如果不是,则提示请求:

ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, REQUEST_CODE);

总的来说,事情看起来像这样:

public  boolean haveStoragePermission() {
    if (Build.VERSION.SDK_INT >= 23) {
        if (checkSelfPermission(android.Manifest.permission.WRITE_EXTERNAL_STORAGE)
                == PackageManager.PERMISSION_GRANTED) {
            Log.e("Permission error","You have permission");
            return true;
        } else {

            Log.e("Permission error","You have asked for permission");
            ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, 1);
            return false;
        }
    }
    else { //you dont need to worry about these stuff below api level 23
        Log.e("Permission error","You already have the permission");
        return true;
    }
}

最后一件事 :)

通过回调接收结果:

    @Override
    public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        if(grantResults[0]== PackageManager.PERMISSION_GRANTED){
            //you have the permission now.
            DownloadManager.Request request = new DownloadManager.Request(Uri.parse(myurl));
            request.setTitle("Vertretungsplan");
            request.setDescription("wird heruntergeladen");
            request.allowScanningByMediaScanner();
            request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
            String filename = URLUtil.guessFileName(myurl, null, MimeTypeMap.getFileExtensionFromUrl(myurl));
            request.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, filename);
            DownloadManager manager = (DownloadManager) c.getSystemService(Context.DOWNLOAD_SERVICE);
            manager.enqueue(request);
        }
    }



或者,您可以使用我的自定义类PermissionCheck来轻松处理所有这些实现。以下是该类:

import android.Manifest;
import android.app.Activity;
import android.content.Context;
import android.content.pm.PackageManager;
import android.support.v4.app.ActivityCompat;

/**
 * Created by Tushar on 6/30/2017.
 */

public class PermissionCheck{
    public static boolean readAndWriteExternalStorage(Context context){
        if(ActivityCompat.checkSelfPermission(context, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED || ActivityCompat.checkSelfPermission(context, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED){
            ActivityCompat.requestPermissions((Activity) context, new String[]{Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.READ_EXTERNAL_STORAGE}, 1);
            return false;
        }else{
            return true;
        }

    }

    public static boolean audioRecord(Context context){
        if(ActivityCompat.checkSelfPermission(context, Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED ){
            ActivityCompat.requestPermissions((Activity) context, new String[]{Manifest.permission.RECORD_AUDIO}, 2);
            return false;
        }else{
            return true;
        }

    }

    public static boolean readAndWriteContacts(Context context){
        if(ActivityCompat.checkSelfPermission(context, Manifest.permission.READ_CONTACTS) != PackageManager.PERMISSION_GRANTED || ActivityCompat.checkSelfPermission(context, Manifest.permission.WRITE_CONTACTS) != PackageManager.PERMISSION_GRANTED){
            ActivityCompat.requestPermissions((Activity) context, new String[]{Manifest.permission.READ_CONTACTS, Manifest.permission.WRITE_CONTACTS}, 3);
            return false;
        }else{
            return true;
        }

    }

    public static boolean vibrate(Context context){
        if(ActivityCompat.checkSelfPermission(context, Manifest.permission.VIBRATE) != PackageManager.PERMISSION_GRANTED){
            ActivityCompat.requestPermissions((Activity) context, new String[]{Manifest.permission.VIBRATE}, 4);
            return false;
        }else{
            return true;
        }

    }

    public static boolean sendSms(Context context){
        if(ActivityCompat.checkSelfPermission(context, Manifest.permission.SEND_SMS) != PackageManager.PERMISSION_GRANTED){
            ActivityCompat.requestPermissions((Activity) context, new String[]{Manifest.permission.SEND_SMS}, 5);
            return false;
        }else{
            return true;
        }

    }

    //Just like this you can implement rest of the permissions. 
}

使用:
if(PermissionCheck.readAndWriteExternalStorage(context)){
    //Your read write code.
}

if(PermissionCheck.sendSms(context)){
    //Your sms sending code.
}

请注意,这些方法的调用将自动请求权限,并且您的onRequestPermissionsResult方法也将被触发。 :)


你能告诉我字符串数组中的1是什么意思吗? - Mark Witt
1
以防万一,如果有人遇到Manifest.permission.WRITE_EXTERNAL_STORAGE的问题,请改用android.Manifest.permission.WRITE_EXTERNAL_STORAGE - Srujan Barai
在您提供的这行代码中,c是什么? DownloadManager manager = (DownloadManager) c.getSystemService(Context.DOWNLOAD_SERVICE); 答案:c是上下文变量。 - Ankesh Roy
@AnkeshRoy 这是 context - Tushar Monirul

10

不需要做这一切。只需将以下代码粘贴到您的onCreate()函数中:

private static int REQUEST_CODE=1;  

ActivityCompat.requestPermissions(this, new String[]{
    Manifest.permission.WRITE_EXTERNAL_STORAGE
}, REQUEST_CODE);

1
我在我的API 23模拟器上遇到了同样的问题(24和25没问题),运行一个自定义标签应用程序。我尝试了这个解决方案,以及其他一些变化,我可以看到我被授予权限,但任何下载尝试都无法解决问题。 当我在真实设备上运行应用程序(不带权限请求代码)时,我会自动提示允许Google访问存储 - 而不是我的应用程序。 我删除了运行时权限的代码,并设置了浏览器(模拟器上的默认值)的权限以访问存储,我的应用程序正常工作。 以下文章提供了一些关于模拟器可能发生的情况的见解:请求并允许WRITE_EXTERNAL_STORAGE权限在当前会话中没有效果

0

请确保在您的Manifest.xml文件中包含READ_EXTERNAL_STORAGE和WRITE_EXTERNAL_STORAGE权限:

然后在您的下载函数中包含以下代码

if(ActivityCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED || ActivityCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED){
            ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE}, 1);
            // this will request for permission when permission is not true
        }else{
            // Download code here
        }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接