简体   繁体   中英

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. 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.

  • Android 11
  • TargetSDK 31

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

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) 

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";

On an Android 11+ device apps cannot create their own folders in root of external storage.

Instead try to create your own folder in public Documents dorectory for instance.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM