Android 6-仅在第一次运行时写入外部存储失败

Android 6-仅在第一次运行时写入外部存储失败,android,permissions,writetofile,Android,Permissions,Writetofile,我知道类似这样的问题被问了好几次,但问题是不同的。 我需要将数据写入外部存储器。我知道我需要在Android 6上的运行时请求权限。到目前为止,所有这些都很有效。只有一件事很奇怪。授予的权限似乎仅在我第二次启动应用程序时有效 我的示例如下所示: @Override public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { super

我知道类似这样的问题被问了好几次,但问题是不同的。 我需要将数据写入外部存储器。我知道我需要在Android 6上的运行时请求权限。到目前为止,所有这些都很有效。只有一件事很奇怪。授予的权限似乎仅在我第二次启动应用程序时有效

我的示例如下所示:

@Override
    public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        switch (requestCode) {
            case REQUEST_WRITE_STORAGE: {
                if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                    Log.d(TAG, "Permission granted!");
                    write();
                } else {
                    Log.d(TAG, "No permission granted!");
                }
            }
        }
    }


    @Override
    public void onClick(View v) {
        boolean hasPermission = (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED);
        if (!hasPermission) {
            Log.d(TAG, "Has no permission! Ask!");
            ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, REQUEST_WRITE_STORAGE);
        } else {
            Log.d(TAG, "Permission already given!");
            write();
        }
    }

    private void write(){
        Log.d(TAG, "Trying to write");
        // Get the directory for the user's public pictures directory.
        File root = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES), "test");
        if (! root.exists() && !root.mkdirs()) {
            Log.e(TAG, "Directory not created");
            return;
        }

        File testFile = new File(root, "TEST.tmp");
        FileWriter writer;
        try {
            StringBuilder sb = new StringBuilder();
            sb.append("TEST TEST TEST");
            writer = new FileWriter(testFile, false);
            writer.append(sb.toString());
            writer.flush();
            writer.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        Log.d(TAG, "Write successful");
    }
private void write(){
        Log.d(TAG, "Trying to write");
        // Get the directory for the user's public pictures directory.
        File root = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES), "test");
        if (! root.exists()){
            if(!root.mkdirs()) {
                Log.e(TAG, "Directory not created");
                return;
            } else {
                Log.e(TAG, "Directory created");
            }
        }
        ...
PackageManager packageManager = getPackageManager();
        Intent intent = packageManager.getLaunchIntentForPackage(getPackageName());
        ComponentName componentName = intent.getComponent();
        Intent mainIntent = IntentCompat.makeRestartActivityTask(componentName);
        startActivity(mainIntent);
        System.exit(0);
按下按钮调用正在检查权限的
onClick()
。如果被授予,它将调用
write()
方法-如果没有,它将请求权限

因此,当第一次按下按钮时,权限弹出窗口打开,我单击“是”,并假设它将写入我的测试文件。尽管如此,我还是收到了以下错误消息,文件甚至目录都没有创建:

D/LOG: Has no permission! Ask! 
D/LOG: Permission granted! 
D/LOG: Trying to write 
E/LOG: Directory not created
如果我再次单击(已授予权限),它只显示

D/LOG: Permission already given!
D/LOG: Trying to write
E/LOG: Directory not created
实际上,目录和文件都不存在。 因此,我关闭应用程序并再次启动它,然后按下按钮。 控制台显示以下内容:

D/LOG: Permission already given!
D/LOG: Trying to write
D/LOG: Write successful
瞧,现在目录和文件已经存在了

那么为什么它只在我第二次启动应用程序时才起作用呢?似乎权限仅在应用程序启动时刷新。有什么想法吗

----编辑---

与注释相关,我将
exists()
mkdirs()
分开

不过,该方法如下所示:

@Override
    public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        switch (requestCode) {
            case REQUEST_WRITE_STORAGE: {
                if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                    Log.d(TAG, "Permission granted!");
                    write();
                } else {
                    Log.d(TAG, "No permission granted!");
                }
            }
        }
    }


    @Override
    public void onClick(View v) {
        boolean hasPermission = (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED);
        if (!hasPermission) {
            Log.d(TAG, "Has no permission! Ask!");
            ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, REQUEST_WRITE_STORAGE);
        } else {
            Log.d(TAG, "Permission already given!");
            write();
        }
    }

    private void write(){
        Log.d(TAG, "Trying to write");
        // Get the directory for the user's public pictures directory.
        File root = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES), "test");
        if (! root.exists() && !root.mkdirs()) {
            Log.e(TAG, "Directory not created");
            return;
        }

        File testFile = new File(root, "TEST.tmp");
        FileWriter writer;
        try {
            StringBuilder sb = new StringBuilder();
            sb.append("TEST TEST TEST");
            writer = new FileWriter(testFile, false);
            writer.append(sb.toString());
            writer.flush();
            writer.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        Log.d(TAG, "Write successful");
    }
private void write(){
        Log.d(TAG, "Trying to write");
        // Get the directory for the user's public pictures directory.
        File root = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES), "test");
        if (! root.exists()){
            if(!root.mkdirs()) {
                Log.e(TAG, "Directory not created");
                return;
            } else {
                Log.e(TAG, "Directory created");
            }
        }
        ...
PackageManager packageManager = getPackageManager();
        Intent intent = packageManager.getLaunchIntentForPackage(getPackageName());
        ComponentName componentName = intent.getComponent();
        Intent mainIntent = IntentCompat.makeRestartActivityTask(componentName);
        startActivity(mainIntent);
        System.exit(0);
不过,应用程序第一次启动时,我得到的目录没有创建。下次启动应用程序时,目录已创建。所以这似乎不是问题所在

此外,我还添加了一个方法,用于检查
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY\u PICTURES)
是否存在。这是真的,即使是在第一次开始。失败的是我的子文件夹的
mkdirs()

请尝试以下代码:-

@Override
    public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        switch (requestCode) {
            case REQUEST_WRITE_STORAGE: {
                if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                    Log.d(TAG, "Permission granted!");
                    write();
                } else {
                    Log.d(TAG, "No permission granted!");
                }
            }
        }
    }


    @Override
    public void onClick(View v) {
        File root = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES), "test");
        dircheck(root);
        filecheck(new File(root, "TEST.tmp"));
        boolean hasPermission = (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED);
        if (!hasPermission) {
            Log.d(TAG, "Has no permission! Ask!");
            ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, REQUEST_WRITE_STORAGE);
        } else {
            Log.d(TAG, "Permission already given!");
            write();
        }
    }

    private void dircheck(File file) {

            if (!file.exists()) {
                file.mkdirs();
            }

    }

    private void filecheck(File file){
        if(!file.exists()){
            file.createNewFile();
        }
    }
    private void write(){

        File root = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES), "test");

        File testFile = new File(root, "TEST.tmp");
        FileWriter writer;
        try {
            StringBuilder sb = new StringBuilder();
            sb.append("TEST TEST TEST");
            writer = new FileWriter(testFile, false);
            writer.append(sb.toString());
            writer.flush();
            writer.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        Log.d(TAG, "Write successful");
    }
编辑答案:-

@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
    super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    switch (requestCode) {
        case REQUEST_WRITE_STORAGE: {
            if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                Log.d(TAG, "Permission granted!");
                write();
            } else {
                Log.d(TAG, "No permission granted!");
            }
        }
    }
}


@Override
public void onClick(View v) {
    boolean hasPermission = (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED);
    if (!hasPermission) {
        Log.d(TAG, "Has no permission! Ask!");
        ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, REQUEST_WRITE_STORAGE);
    } else {
        Log.d(TAG, "Permission already given!");
        write();
    }
}

private void dircheck(File file) {
    if (!file.exists()) {
        file.mkdirs();
    }

}

private void filecheck(File file){
    if(!file.exists()){
        try {
            file.createNewFile();    
        }catch (Exception e){e.printStackTrace();}

    }
}
private void write(){
    File root = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES), "test");
    dircheck(root);
    filecheck(new File(root, "TEST.tmp"));

    File testFile = new File(root, "TEST.tmp");
    FileWriter writer;
    try {
        StringBuilder sb = new StringBuilder();
        sb.append("TEST TEST TEST");
        writer = new FileWriter(testFile, false);
        writer.append(sb.toString());
        writer.flush();
        writer.close();
    } catch (IOException e) {
        e.printStackTrace();
    }
    Log.d(TAG, "Write successful");
}
如下所示: 我相信我找到了解决办法——或者至少是一个解释

事实证明,这似乎是一个更大的问题。更改写入外部存储的权限会更改此进程的Linux GID。 为了更改ID,必须重新启动进程。下次打开应用程序时,将设置新的groupID并授予权限。这就得出结论,这不是一个bug,事实上是Linux和Android的一个更大的问题。

我“解决”了这个问题,在第一次执行应用程序时请求许可,并在获得许可时重新启动它,如下所示:

@Override
    public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        switch (requestCode) {
            case REQUEST_WRITE_STORAGE: {
                if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                    Log.d(TAG, "Permission granted!");
                    write();
                } else {
                    Log.d(TAG, "No permission granted!");
                }
            }
        }
    }


    @Override
    public void onClick(View v) {
        boolean hasPermission = (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED);
        if (!hasPermission) {
            Log.d(TAG, "Has no permission! Ask!");
            ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, REQUEST_WRITE_STORAGE);
        } else {
            Log.d(TAG, "Permission already given!");
            write();
        }
    }

    private void write(){
        Log.d(TAG, "Trying to write");
        // Get the directory for the user's public pictures directory.
        File root = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES), "test");
        if (! root.exists() && !root.mkdirs()) {
            Log.e(TAG, "Directory not created");
            return;
        }

        File testFile = new File(root, "TEST.tmp");
        FileWriter writer;
        try {
            StringBuilder sb = new StringBuilder();
            sb.append("TEST TEST TEST");
            writer = new FileWriter(testFile, false);
            writer.append(sb.toString());
            writer.flush();
            writer.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        Log.d(TAG, "Write successful");
    }
private void write(){
        Log.d(TAG, "Trying to write");
        // Get the directory for the user's public pictures directory.
        File root = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES), "test");
        if (! root.exists()){
            if(!root.mkdirs()) {
                Log.e(TAG, "Directory not created");
                return;
            } else {
                Log.e(TAG, "Directory created");
            }
        }
        ...
PackageManager packageManager = getPackageManager();
        Intent intent = packageManager.getLaunchIntentForPackage(getPackageName());
        ComponentName componentName = intent.getComponent();
        Intent mainIntent = IntentCompat.makeRestartActivityTask(componentName);
        startActivity(mainIntent);
        System.exit(0);
我没有尝试的一个解决方案是创建一个在后台运行的服务(因此有另一个进程id)并授予它权限。这样,只需要重新启动服务,而不需要重新启动整个应用程序。另一方面,这可能会为进程之间的通信做更多的工作。如果我理解正确的话,一个内容提供者可能也可以工作,但也意味着更多的工作

---编辑---


用户M66B()找到了相关GID的列表。可以在此处找到更多信息:

尝试使用
exists()
mkdirs()
调用来分隔if子句,以缩小问题范围。请提供某种解释,说明为什么这样可以解决问题。之前@Tobias Reich在if语句中同时使用exist()和mkdir()。所以现在我已经创建了一个单独的方法来检查dir是否存在,如果不存在,那么就创建它。好吧,至少这看起来是可行的。很明显,我必须分别创建每个目录?我假设mkdirs()将完成创建所有子目录的工作。@TobiasReich如果它对您有效,请不要忘记将答案标记为已接受。谢谢……很高兴认识你,对不起,我原以为这样行得通,但显然不行。我得到的唯一更改是“权限授予”后的异常,即:“FileNotFoundException:/storage/emulated/0/Pictures/test/test.tmp:open failed:enoint(没有这样的文件或目录)”此问题可能是旧问题,但我只是在一台使用天堂OS 7.1.2的设备上有相同的行为。所有其他模拟器和设备都不显示它。LinOS严格使用SELinux运行,我不是Linux的家伙,但这可能是一个原因?!