【问题标题】:Android 11, Java | External Storage permission @SDK >29Android 11,Java |外部存储权限@SDK >29
【发布时间】:2021-12-28 15:50:15
【问题描述】:

您可能已经知道,Google 已经更改了外部存储访问权限的准则等。 尝试了谷歌文档中的所有提示和技巧,但它对我不起作用。一旦我单击“备份”按钮和“确定”,应用程序就会崩溃。我没有更多的想法来解决这个问题。现在我请教各位专家。我对每个提示都很满意。

  • Android 11
  • TargetSDK 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) 

【问题讨论】:

  • 你在运行时获得了MANAGE_EXTERNAL_STORAGE的权限吗??
  • 写入允许写入的位置,例如Context上的getExternalFilesDir()
  • The app crashes, once I clicked to "Backup" button 这并没有告诉我们任何事情。更好地判断哪个代码语句导致异常。
  • Utils.getStorageDir() 我们不知道会是哪个目录。
  • @blackapps:添加了 Utils.getStorageDir()。 Backup.java 中的 doInBackground() 发生异常。

标签: java android realm android-permissions android-external-storage


【解决方案1】:

尝试在清单中添加

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

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

【讨论】:

    【解决方案2】:

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

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

    例如,尝试在公共文档目录中创建您自己的文件夹。

    您最好检查一下 mkdir() 是否确实创建了想要的文件夹。

    【讨论】:

      猜你喜欢
      • 2023-02-19
      • 1970-01-01
      • 2020-12-29
      • 2021-04-14
      • 2017-07-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-08-17
      相关资源
      最近更新 更多