通过 UI 为复制数据选择的文件夹路径仍然是 EACCES(权限被拒绝)

Folder Path Selected for Copy Data via UI still EACCES (Permission denied)

我提到了 https://github.com/afollestad/material-dialogs,它有指向 select 文件夹的代码,并通过应用程序获取其路径。按照它,我实现了以下代码:

重要我在 KitKat API 19.

上的 Asus Zenphone 5 运行 上收到此错误
package jss.folderselector;

import android.Manifest;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.v4.app.ActivityCompat;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;

import com.afollestad.materialdialogs.folderselector.FolderChooserDialog;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

public class MainActivity extends AppCompatActivity implements FolderChooserDialog.FolderCallback {
    TextView path;

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


        foldersel = (Button) findViewById(R.id.folder);
        copy = (Button) findViewById(R.id.copy);
        path = (TextView) findViewById(R.id.path);

        foldersel.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                showFolderChooser();
            }
        });

        copy.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                moveFile("/sdcard/Download/a.apk",path.getText().toString()+"/a.apk");
            }
        });

    }

    public void showFolderChooser() {

        if (ActivityCompat.checkSelfPermission(MainActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE) !=
                PackageManager.PERMISSION_GRANTED) {
            ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, 1);
            return;
        }
        new FolderChooserDialog.Builder(MainActivity.this)
                .chooseButton(R.string.md_choose_label)
                .allowNewFolder(true, 0)
                // changes label of the choose button
                .initialPath("/sdcard/Download")  // changes initial path, defaults to external storage directory
                .tag("optional-identifier")
                .goUpLabel("Up") // custom go up label, default label is "..."
                .show();
    }

    @Override
    public void onFolderSelection(@NonNull com.afollestad.materialdialogs.folderselector.FolderChooserDialog dialog, @NonNull File folder) {
        Toast.makeText(this, folder.getAbsolutePath(), Toast.LENGTH_SHORT).show();
        path.setText(folder.getAbsolutePath());
        final String tag = dialog.getTag(); // gets tag set from Builder, if you use multiple dialogs
    }

    @Override
    public void onFolderChooserDismissed(@NonNull FolderChooserDialog dialog) {

    }

    public void moveFile(String s, String d) {
        InputStream inStream = null;
        OutputStream outStream = null;

        try {

            File afile = new File(s);
            File bfile = new File(d);

            inStream = new FileInputStream(afile);
            outStream = new FileOutputStream(bfile);

            byte[] buffer = new byte[1024];

            int length;
            //copy the file content in bytes
            while ((length = inStream.read(buffer)) > 0) {

                outStream.write(buffer, 0, length);

            }

            inStream.close();
            outStream.close();

            //delete the original file
            afile.delete();

            System.out.println("File is copied successful!");
            //run();

        } catch (IOException e) {
            e.printStackTrace();

        }


    }
}

我可以选择 MicroSD 卡的路径并将其设置为复制文件的目的地。但是失败了

我在清单中拥有写入外部存储的权限。还有什么需要修的。因为我读到如果应用程序通过 UI 选择任何文件夹,应用程序获得了写入权限,但对我不起作用。

清单文件:

 <?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="jss.folderselector">
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

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

</manifest>

错误日志:

02-11 13:54:31.191 28199-28199/net.rdrei.android.dirchooser.sample D/DirectoryChooserFragment: Changed directory to /storage/emulated
02-11 13:54:31.481 28199-28199/net.rdrei.android.dirchooser.sample D/DirectoryChooserFragment: Changed directory to /storage
02-11 13:54:32.101 28199-28199/net.rdrei.android.dirchooser.sample D/DirectoryChooserFragment: Selected index: 0
02-11 13:54:32.111 28199-28199/net.rdrei.android.dirchooser.sample D/DirectoryChooserFragment: Changed directory to /storage/MicroSD
02-11 13:54:33.091 28199-28199/net.rdrei.android.dirchooser.sample D/DirectoryChooserFragment: Selected index: 6
02-11 13:54:33.101 28199-28199/net.rdrei.android.dirchooser.sample D/DirectoryChooserFragment: Changed directory to /storage/MicroSD/WhatsAppold
02-11 13:54:33.581 28199-28199/net.rdrei.android.dirchooser.sample D/DirectoryChooserFragment: Returning /storage/MicroSD/WhatsAppold as result
02-11 13:54:33.581 28199-28255/net.rdrei.android.dirchooser.sample D/DirectoryChooserFragment: FileObserver received event 32768
02-11 13:54:33.601 28199-28199/net.rdrei.android.dirchooser.sample D/DirectoryChooserFragment: Changed directory to /storage/MicroSD/WhatsAppold
02-11 13:54:33.601 28199-28199/net.rdrei.android.dirchooser.sample I/DirChooserSample: Return from DirChooser with result 1
02-11 13:54:35.601 28199-28199/net.rdrei.android.dirchooser.sample W/System.err: java.io.FileNotFoundException: /storage/MicroSD/WhatsAppold/a.apk: open failed: EACCES (Permission denied)
02-11 13:54:35.601 28199-28199/net.rdrei.android.dirchooser.sample W/System.err:     at libcore.io.IoBridge.open(IoBridge.java:409)
02-11 13:54:35.601 28199-28199/net.rdrei.android.dirchooser.sample W/System.err:     at java.io.FileOutputStream.<init>(FileOutputStream.java:88)
02-11 13:54:35.601 28199-28199/net.rdrei.android.dirchooser.sample W/System.err:     at java.io.FileOutputStream.<init>(FileOutputStream.java:73)
02-11 13:54:35.601 28199-28199/net.rdrei.android.dirchooser.sample W/System.err:     at net.rdrei.android.dirchooser.sample.DirChooserSample.moveFile(DirChooserSample.java:109)
02-11 13:54:35.601 28199-28199/net.rdrei.android.dirchooser.sample W/System.err:     at net.rdrei.android.dirchooser.sample.DirChooserSample.onClick(DirChooserSample.java:76)
02-11 13:54:35.601 28199-28199/net.rdrei.android.dirchooser.sample W/System.err:     at android.view.View.performClick(View.java:4478)
02-11 13:54:35.601 28199-28199/net.rdrei.android.dirchooser.sample W/System.err:     at android.view.View$PerformClick.run(View.java:18698)
02-11 13:54:35.601 28199-28199/net.rdrei.android.dirchooser.sample W/System.err:     at android.os.Handler.handleCallback(Handler.java:733)
02-11 13:54:35.601 28199-28199/net.rdrei.android.dirchooser.sample W/System.err:     at android.os.Handler.dispatchMessage(Handler.java:95)
02-11 13:54:35.601 28199-28199/net.rdrei.android.dirchooser.sample W/System.err:     at android.os.Looper.loop(Looper.java:149)
02-11 13:54:35.601 28199-28199/net.rdrei.android.dirchooser.sample W/System.err:     at android.app.ActivityThread.main(ActivityThread.java:5257)
02-11 13:54:35.601 28199-28199/net.rdrei.android.dirchooser.sample W/System.err:     at java.lang.reflect.Method.invokeNative(Native Method)
02-11 13:54:35.601 28199-28199/net.rdrei.android.dirchooser.sample W/System.err:     at java.lang.reflect.Method.invoke(Method.java:515)
02-11 13:54:35.601 28199-28199/net.rdrei.android.dirchooser.sample W/System.err:     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:793)
02-11 13:54:35.601 28199-28199/net.rdrei.android.dirchooser.sample W/System.err:     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:609)
02-11 13:54:35.601 28199-28199/net.rdrei.android.dirchooser.sample W/System.err:     at dalvik.system.NativeStart.main(Native Method)
02-11 13:54:35.601 28199-28199/net.rdrei.android.dirchooser.sample W/System.err: Caused by: libcore.io.ErrnoException: open failed: EACCES (Permission denied)
02-11 13:54:35.601 28199-28199/net.rdrei.android.dirchooser.sample W/System.err:     at libcore.io.Posix.open(Native Method)
02-11 13:54:35.601 28199-28199/net.rdrei.android.dirchooser.sample W/System.err:     at libcore.io.BlockGuardOs.open(BlockGuardOs.java:110)
02-11 13:54:35.601 28199-28199/net.rdrei.android.dirchooser.sample W/System.err:     at libcore.io.IoBridge.open(IoBridge.java:393)
02-11 13:54:35.601 28199-28199/net.rdrei.android.dirchooser.sample W/System.err:    ... 15 more

移动设备路径图片

由于您使用的是 API 19 的 KitKat,因此您需要在清单中隐式声明 READ_EXTERNAL_STORAGE 权限。 如文档所示:

This permission is enforced starting in API level 19. Before API level 19, this permission is not enforced and all apps still have access to read from external storage. You can test your app with the permission enforced by enabling Protect USB storage under Developer options in the Settings app on a device running Android 4.1 or higher.

但是请记住,如果您的目标是 API 高于 19,并且如果您只在清单中添加 WRITE_EXTERNAL_STORAGE,它会在构建时自动添加 READ_EXTERNAL_STORAGE,因此您不需要额外的读取权限。但在你的情况下,你需要拥有它,因为你的目标是 19 岁及以下。

I could able to pick path for MicroSD card. 

/Removable/MicroSD/.......

堆栈跟踪中的路径不存在。不在单个 Android 设备上。 (或以其他方式告诉哪个设备)。 (你让我无法在发布图片时复制该路径!)

在现代 Android 系统上,您将无法写入 SD 卡。除了您的应用的私有目录。