[英]Android 11, Java | External Storage permission @SDK >29
As you may already know, Google has changed the guidelines for access rights to external storage, among other things.您可能已经知道,Google 已经更改了外部存储访问权限的准则等。 Tried all tips and tricks from google docu, but it didn't work for me.
尝试了谷歌文档中的所有提示和技巧,但它对我不起作用。 The app crashes, once I clicked to "Backup" button and to "OK".
一旦我单击“备份”按钮和“确定”,应用程序就会崩溃。 I have no more ideas to fix the problem.
我没有更多的想法来解决这个问题。 Now I turn to you experts.
现在我请教各位专家。 I am happy about every tip.
我对每一个小费都很满意。
I'll be sharing the relevant codes to make them as clear as possible.我将分享相关代码以使它们尽可能清晰。
Manifest:显现:
<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实用程序.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备份.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片段备份.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)
Try to add in manifest尝试添加清单
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"/>
and also ask user to give file manage permission at run time in-app并要求用户在应用程序内运行时授予文件管理权限
String path = Environment.getExternalStorageDirectory().getAbsolutePath() + "/myApp";
字符串路径 = Environment.getExternalStorageDirectory().getAbsolutePath() + "/myApp";
On an Android 11+ device apps cannot create their own folders in root of external storage.在 Android 11+ 设备上,应用程序无法在外部存储的根目录中创建自己的文件夹。
Instead try to create your own folder in public Documents dorectory for instance.例如,尝试在公共文档目录中创建自己的文件夹。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.