安卓 11, 爪哇 |外部存储权限 @SDK >29



正如您可能已经知道的,谷歌已经改变了对外部存储访问权限的指导原则。尝试了谷歌文档中的所有提示和技巧,但对我来说都不起作用;备份";按钮和";OK";。我没有更多的想法来解决这个问题。现在我向各位专家请教。我对每一个小费都很满意。

  • 安卓11
  • 目标SDK 31

我将分享相关代码,使其尽可能清晰。

清单:

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" tools:ignore="ScopedStorage"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

Utils.java

public static Boolean askForReadPermission(final Activity activity, final PermissionCallback permissionCallback, View view){
if(android.os.Build.VERSION.SDK_INT < Build.VERSION_CODES.M || Nammu.checkPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE)){
return true;
}
else{
final String[] permissions = {Manifest.permission.WRITE_EXTERNAL_STORAGE};
if (Nammu.shouldShowRequestPermissionRationale(activity, Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
Snackbar.make(view, activity.getString(R.string.error_permission_backup),
Snackbar.LENGTH_INDEFINITE)
.setAction("OK", new View.OnClickListener() {
@Override
public void onClick(View view) {
Nammu.askForPermission(activity, permissions, permissionCallback);
}
}).show();
} else {
Nammu.askForPermission(activity, permissions, permissionCallback);
}
return false;
}
}
public static String getStorageDir(){
String path = Environment.getExternalStorageDirectory().getAbsolutePath() + "/myApp";
File f = new File(path);
if(!f.exists()) if(!f.mkdirs()) return null;
return f.getAbsolutePath();
}
public static String getStorageDirForBackup(){
String path = Environment.getExternalStorageDirectory().getAbsolutePath() + "/My_Backup";
File f = new File(path);
if(!f.exists()) if(!f.mkdirs()) return null;
return f.getAbsolutePath();
}

Backup.java

public class RealmBackup {
private static final String TAG = "Backuplog";
private final String EXPORT_REALM_FILE_NAME = "backup.realm";

private final Activity activity;
private final Realm realm;
public RealmBackupRestore(Activity activity, Realm realm) {
this.activity = activity;
this.realm = realm;
}
@SuppressLint("StaticFieldLeak")
public void backup() {
new AsyncTask<Void, Void, Boolean>() {  

private SweetAlertDialog dialog;
@Override
protected void onPreExecute() {
dialog = new SweetAlertDialog(activity, SweetAlertDialog.PROGRESS_TYPE);
dialog.setContentText(activity.getString(R.string.settings_do_backup));
dialog.setTitleText("");
dialog.show();
}
@Override
protected Boolean doInBackground(Void... params) {
Realm realm = Realm.getDefaultInstance();
File exportRealmFile;
exportRealmFile = new File(Utils.getStorageDir(), EXPORT_REALM_FILE_NAME);
realm.writeCopyTo(exportRealmFile);
File zipFile = new File(Utils.getStorageDirForBackup(), "backup_" + System.currentTimeMillis() + ".zip");
zipFile.delete();

File wholeDirectory = new File(Utils.getStorageDir() + "/");
ZipUtil.pack(wholeDirectory, zipFile);
exportRealmFile.delete();
realm.beginTransaction();
Objects.requireNonNull(Settings.getSettings(realm)).setLastBackup(new Date());
realm.commitTransaction();    
realm.close();
return true;
}
@Override
protected void onPostExecute(Boolean result) {
dialog.cancel();

AlertDialog.Builder alertBuilder = new AlertDialog.Builder(activity);
alertBuilder
.setMessage(activity.getString(R.string.settings_backup_stored))
.setNeutralButton(activity.getString(R.string.generell_ok), new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
}
})
.show();
}
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}

private String dbPath(){
return realm.getPath();
}
}

FragmentBackup.java

backup.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
AlertDialog.Builder alertBuilder = new AlertDialog.Builder(getContext());
alertBuilder
.setMessage(R.string.settings_backup_store_dialog)
.setPositiveButton(R.string.generell_ok, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
if(!Utils.askForReadPermission(getActivity(), permissionCallbackBackup ,backup))return;
realmBackupRestore.backup();
}
})
.setNegativeButton(R.string.generell_cancel, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
}
})
.show();
}
});

Logcat:

2021-12-28 16:38:54.375 28406-28575/devs.myApp E/AndroidRuntime: FATAL EXCEPTION: AsyncTask #1
Process: devs.myApp, PID: 28406
java.lang.RuntimeException: An error occurred while executing doInBackground()
at android.os.AsyncTask$4.done(AsyncTask.java:415)
at java.util.concurrent.FutureTask.finishCompletion(FutureTask.java:383)
at java.util.concurrent.FutureTask.setException(FutureTask.java:252)
at java.util.concurrent.FutureTask.run(FutureTask.java:271)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
at java.lang.Thread.run(Thread.java:923)
Caused by: io.realm.exceptions.RealmFileException: Unable to open a realm at path '/backup.realm'. Please use a path where your app has read-write permissions. (open("/backup.realm") failed: Read-only file system) (/backup.realm) in /home/cc/repo/realm/release/realm/realm-library/src/main/cpp/io_realm_internal_OsSharedRealm.cpp line 407 Kind: PERMISSION_DENIED.
at io.realm.internal.OsSharedRealm.nativeWriteCopy(Native Method)
at io.realm.internal.OsSharedRealm.writeCopy(OsSharedRealm.java:343)
at io.realm.BaseRealm.writeCopyTo(BaseRealm.java:274)
at io.realm.Realm.writeCopyTo(Realm.java:135)
at devs.myApp.utils.RealmBackupRestore$1.doInBackground(RealmBackupRestore.java:65)
at devs.myApp.utils.RealmBackupRestore$1.doInBackground(RealmBackupRestore.java:45)
at android.os.AsyncTask$3.call(AsyncTask.java:394)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167) 
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641) 
at java.lang.Thread.run(Thread.java:923) 

尝试在清单中添加

<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"/>

并要求用户在运行时在应用中授予文件管理权限

字符串路径=Environment.getExternalStorageDirectory((.getAbsolutePath((+"myApp";;

在Android 11+设备上,应用程序无法在外部存储的根目录中创建自己的文件夹。

相反,尝试在公共文档或目录中创建自己的文件夹。

您应该更好地检查mkdir((是否确实创建了所需的文件夹。

最新更新