1

I need to create files and read/write to external storage and the files should persist after uninstallation of the application. It should work on Android 12.

import android.content.Context;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;

public class FileUtil {

    public static void writeFileToExternalStorage(Context context, String fileName, String content) {
        File file = new File(context.getExternalFilesDir(null), fileName);

        try {
            FileOutputStream outputStream = new FileOutputStream(file);
            outputStream.write(content.getBytes());
            outputStream.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}


String fileName = "my_file.txt";
String content = "Hello";

FileUtil.writeFileToExternalStorage(getApplicationContext(), fileName, content);

File my_file.txt will be deleted after uninstallation of the app. I need it to persist. I will be writing text files and images related to it (e.g. one subdirectory of the app directory would contain files f1.txt, img1.jpg, f2.txt, img2.jpg ... fn.txt, imgn.jpg). I don't want to use database or shared preferences for it. How can I do it?

In this video there is said, that everytime the app reads/writes to a file, the user has to go through dialog, but this is what I don't want. It's very uncomfortable for the user.

xralf
  • 3,312
  • 45
  • 129
  • 200
  • 1
    Related video: [Everything about storage on Android](https://youtu.be/jcO6p5TlcGs) Realize that older versions of Android allowed for unannounced files to persist after uninstall which would result in clutter and tracking cookies, see old Google IO video on [What’s new in shared storage (Google I/O'19)](https://youtu.be/3EtBw5s9iRY) Do realize that unless the user deletes cache/app storage or uninstalls, user app data (local storage) can persist during app upgrade. – Morrison Chang Jun 11 '23 at 22:33
  • Thank you. I will probably let the data delete after uninstall. I'm glad to hear that the data will persist during app upgrade. – xralf Jun 12 '23 at 16:02

5 Answers5

2

You're totally right that Context#getExternalFilesDir returns app that will be uninstalled with your app.

For API 29 and above:

Documentation says that for Documents we should use Storage Access Framework.

You can create a new file using Intent#ACTION_CREATE_DOCUMENT

Similar to this:

// Request code for creating a PDF document.
const val CREATE_FILE = 1

private fun createFile(pickerInitialUri: Uri) {
    val intent = Intent(Intent.ACTION_CREATE_DOCUMENT).apply {
        addCategory(Intent.CATEGORY_OPENABLE)
        type = "application/text"
        putExtra(Intent.EXTRA_TITLE, "file.txt")
    }
    startActivityForResult(intent, CREATE_FILE)
}

You will see something similar to

enter image description here

After executing the command and receiving the result you can access the file using uri from Intent#getData. With a uri you can use contentResolver.openOutputStream(destination) to update the file's content.

Update 1: this is a similar code in Java

    private void createFile() {
        Intent intent = new Intent(Intent.ACTION_CREATE_DOCUMENT);
        intent.addCategory(Intent.CATEGORY_OPENABLE);
        intent.setType("application/text");
        intent.putExtra(Intent.EXTRA_TITLE, "file.txt");

        launcher.launch(intent);
    }

And writing to the file:

OutputStream stream = getContentResolver().openOutputStream(uri);
PrintWriter writer = new PrintWriter(stream);

writer.println("Hello World");
writer.close();

For API below 29:

I see that documentation offers to use Environment#getExternalStoragePublicDirectory, especially, if files should be shared.

You can also access the root of the device using Environment#getExternalStorageDirectory. However, the usage is discouraged in favour of getExternalStoragePublicDirectory as files saved in root can pollute user's space.

Applications should not directly use this top-level directory, in order to avoid polluting the user's root namespace. Any files that are private to the application should be placed in a directory returned by Context.getExternalFilesDir, which the system will take care of deleting if the application is uninstalled. Other shared files should be placed in one of the directories returned by getExternalStoragePublicDirectory(String).

  • And where should I save the files, it contains text files and images. E.g. the main directory would be `myapp` and it would contains subdirectories `a`, `b`, `c` etc. and in `a` will be files f1.txt, img1.jpg ... fn.txt, imgn.jpg. – xralf Jun 04 '23 at 22:19
  • But `getExternalStoragePublicDirectory` is deprecated. I won't work on Android 12. – xralf Jun 04 '23 at 22:23
  • I see, the method is indeed deprecated. Let me check other methods – Alexander Dadukin Jun 04 '23 at 22:26
  • @xralf I believe you can try to use `Storage Access Framework` instead – Alexander Dadukin Jun 04 '23 at 22:40
  • Thank you, but I'm using text files and images and it is in subdirectories as well. I'm using Java as well. – xralf Jun 04 '23 at 22:47
  • I think you can save images under the same folder using the same api as well: you need to change file name and content to application/image. – Alexander Dadukin Jun 04 '23 at 22:51
  • Could you please send me a link where is it described more thoroughly and in Java? I can't find anything on it. I will do these actions - create directory, create file, read file, append to file, delete file. – xralf Jun 04 '23 at 22:56
  • Yeah, will check later this week and will try to provide something to help you. Sorry if the answer is confusing – Alexander Dadukin Jun 05 '23 at 00:04
  • Hey @xralf, this is POC: https://gist.github.com/st235/488d8d6855dc43704f76d11094f91708 – Alexander Dadukin Jun 05 '23 at 07:37
  • I tried the code, but I would like to create a file without this launcher. It should be e.g. in directory `my_app_main_dir/a`. Then I would like to read file from directory `my_app_main_dir/a` and append a text to it. I'd like to have the possibility to delete the file as well. You can look [here](https://github.com/lucidl/tedegraph-mobile/blob/main/main.py) (line 244, function add_bookmark()) to my Android Python project. This is the most complex operations I will need, nothing more. I'm trying to rewrite my project to Java for latest Android devices. – xralf Jun 05 '23 at 13:29
1

The only place where you should create the subdirectory for your files is in public Download or Documents directory.

You will use getExternalPublicDirectory() to get the path. Use it. Deprecated or not it works on all Android versions.

Other public directories are not possible as they will not take files with different extensions.

The files will persist. You did not tell who and how those files will be used later.

If you reinstall your app use SAF to let the user choose your subdir.

blackapps
  • 8,011
  • 2
  • 11
  • 25
  • >> "You did not tell who and how those files will be used later." I don't want the user to loose the data after uninstall or update to a new version. It should be something, that he/she can delete manually later. – xralf Jun 05 '23 at 05:33
0

On Android 12, to manage storage SAF is the recommended way.

But if you don't want to use then the only way is requesting permission MANAGE_EXTERNAL_STORAGE which allows full storage access. For more details check out this link.

Note: MANAGE_EXTERNAL_STORAGE is a sensitive permission so check out this Google play notice before using it.

rex50
  • 41
  • 4
0
  • You can save the file to path Environment.getExternalStorageDirectory() + "/your_folder", However user can manual delete file. Refer
Cuong Nguyen
  • 970
  • 6
  • 17
-1

In Android, files saved to the external storage directory provided by context.getExternalFilesDir() are considered part of the application's private external storage. By default, these files are deleted when the application is uninstalled. However, if you want the files to persist even after uninstallation, you can save them to a different location on the external storage.

Here's an updated version of your code that saves files to a custom directory on the external storage

import android.content.Context;
import android.os.Environment;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;

public class FileUtil {

    public static void writeFileToExternalStorage(Context context, String directoryName, String fileName, String content) {
        File directory = new File(Environment.getExternalStorageDirectory(), directoryName);
        if (!directory.exists()) {
            directory.mkdirs(); // Create the directory if it doesn't exist
        }

        File file = new File(directory, fileName);

        try {
            FileOutputStream outputStream = new FileOutputStream(file);
            outputStream.write(content.getBytes());
            outputStream.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

Usage:

String directoryName = "my_files_directory";
String fileName = "my_file.txt";
String content = "Hello";

FileUtil.writeFileToExternalStorage(getApplicationContext(), directoryName, fileName, content);

In this code, the Environment.getExternalStorageDirectory() method provides the root directory of the external storage. By creating a custom directory within this root directory, you can save your files there. This custom directory will persist even after the application is uninstalled, allowing the files to remain intact.

Please note that starting from Android 10 (API level 29), there are restrictions on accessing external storage, and you'll need to request the necessary permissions from the user. Additionally, consider handling the storage access permission gracefully in case it is revoked by the user in the future.

Regarding the video you mentioned, it seems to be referring to the Scoped Storage changes introduced in Android 10. Scoped Storage imposes certain restrictions on accessing files outside the application's private directories. However, the approach described above should work for Android 12 as well, as it saves the files within the application's private external storage, which is not subject to Scoped Storage restrictions.

I hope I have been of assistance to you.

Amr Shekh Zen
  • 41
  • 1
  • 8