Android 6 - 仅在第一个 运行 时写入外部存储失败
Android 6 - Writing to external storage fails ON FIRST RUN ONLY
我知道类似这样的问题被问过好几次,但那个问题是不同的。
我需要将数据写入外部存储。我知道我需要在运行时请求 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");
}
按下按钮会调用正在检查权限的 onClick()
。如果获得许可,它会调用 write()
方法 - 如果没有,它会请求许可。
所以当第一次按下按钮时,权限弹出窗口打开,我单击 "yes" 并假设它会写入我的测试文件。尽管如此,我还是收到以下错误消息,并且文件甚至目录都没有创建:
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()
分开了。
方法仍然是这样的:
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");
}
}
...
仍然,第一次启动应用程序时,我得到未创建目录。下次我启动该应用程序时,该目录已创建。所以这似乎不是问题。
此外,我添加了一个检查 Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_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,必须重新启动该过程。下次应用程序打开时,将设置新的组 ID 并授予权限。这得出的结论是,这不是一个错误,但实际上 Linux 和 Android 的一个更大的问题。
我 "solved" 通过在第一次执行应用程序时请求权限并在获得权限后重新启动它,如下所示:
PackageManager packageManager = getPackageManager();
Intent intent = packageManager.getLaunchIntentForPackage(getPackageName());
ComponentName componentName = intent.getComponent();
Intent mainIntent = IntentCompat.makeRestartActivityTask(componentName);
startActivity(mainIntent);
System.exit(0);
我没有尝试过的一个解决方案是在后台创建一个服务 运行(因此有另一个进程 ID)并授予它权限。这样只需要重新启动服务而不需要重新启动整个应用程序。不利的一面是,这可能会为进程之间的通信做更多的工作。如果我理解正确,内容提供者也可能有用,但也意味着更多的工作。
--- 编辑 ---
用户M66B () found a list of the related gids. Further information can be found here: https://android.googlesource.com/platform/frameworks/base/+/master/data/etc/platform.xml
我知道类似这样的问题被问过好几次,但那个问题是不同的。 我需要将数据写入外部存储。我知道我需要在运行时请求 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");
}
按下按钮会调用正在检查权限的 onClick()
。如果获得许可,它会调用 write()
方法 - 如果没有,它会请求许可。
所以当第一次按下按钮时,权限弹出窗口打开,我单击 "yes" 并假设它会写入我的测试文件。尽管如此,我还是收到以下错误消息,并且文件甚至目录都没有创建:
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()
分开了。
方法仍然是这样的:
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");
}
}
...
仍然,第一次启动应用程序时,我得到未创建目录。下次我启动该应用程序时,该目录已创建。所以这似乎不是问题。
此外,我添加了一个检查 Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_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,必须重新启动该过程。下次应用程序打开时,将设置新的组 ID 并授予权限。这得出的结论是,这不是一个错误,但实际上 Linux 和 Android 的一个更大的问题。
我 "solved" 通过在第一次执行应用程序时请求权限并在获得权限后重新启动它,如下所示:
PackageManager packageManager = getPackageManager();
Intent intent = packageManager.getLaunchIntentForPackage(getPackageName());
ComponentName componentName = intent.getComponent();
Intent mainIntent = IntentCompat.makeRestartActivityTask(componentName);
startActivity(mainIntent);
System.exit(0);
我没有尝试过的一个解决方案是在后台创建一个服务 运行(因此有另一个进程 ID)并授予它权限。这样只需要重新启动服务而不需要重新启动整个应用程序。不利的一面是,这可能会为进程之间的通信做更多的工作。如果我理解正确,内容提供者也可能有用,但也意味着更多的工作。
--- 编辑 ---
用户M66B () found a list of the related gids. Further information can be found here: https://android.googlesource.com/platform/frameworks/base/+/master/data/etc/platform.xml