It is known that the storage permission on Android has been changing, from Android to add file Provider, to Android10 to add partition storage, Google for storage permission management is more and more strict. Let’s talk about storage Api compatibility on Android.
1. Application storage space
The application saves data in the following ways:
- Files and media data can be stored in application-specific storage space and public storage space.
- Short data or preferences can be saved through sharePreference
- The database
External storage
SDcard exists in the previous mobile phone, but many mobile phones have cancelled SDcard,Android introduced a mapping mechanism to create a virtual SDcard, we see through the file manager path storage/emulated/0 is virtual SDcard, This is what we call “external storage space” or “public storage space”
All read/write permission requests applied by the APP are for external storage space permissions
Internal storage
The memory store is the exclusive directory of the app, which is not accessible to other apps. It is suitable for storing sensitive files. Path to be accessed through the system API: /data/user/0/app_packageName/… /data/date/app_packageName/…
Partition storage
Partition storage is actually an app in the external storage space to build a corresponding directory, the app does not need to apply for permission to access, if the application for read and write permission, means that the application for external space all access permissions, before Android10, partition storage directory is possible to be accessed by other apps. Starting with Android11, other apps are not accessible, in what some call a sandbox mode. It’s on the website
2. Permission change record
Dynamic permissions are introduced in Android6.0
To apply for read and write permissions, declare in the manifest.xml:
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
Copy the code
Before Android6.0, you only need to declare that you can read and write files. After Android6.0, you need to apply for dynamic permissions, which is to agree with the user. Permission to apply for the callback is very boring, can go to see RxPermission lot, EasyPermission such libraries.
The permissions of Android7.0 have changed
Since Android7.0, uris of the type file:// are forbidden. Attempting to use this URI directly triggers a FileUriExposedException. It is recommended to use the FileProvider to create a content:// URI
Android10 tries to introduce partitioned storage
The above mentioned partition storage is actually the external storage space of the APP directory, the APP can be accessed without permission. Can be disabled on Android10: android: requestLegacyExternalStorage = “true”, but won’t work on Android11 oh, system will automatically ignore mew: ahaha
When Google first tried to introduce partitioned storage, I was pretty dumbfounded
3. Use FileProvider
Setup1:
Create an XML directory in the RES directory and create the filepaths. XML file in the XML directory
<? The XML version = "1.0" encoding = "utf-8"? > <paths> <! --> <files-path name="int_root" path="/" /> <! --> <cache-path name="app_cache" path="/" /> <! - 3, corresponding to the root directory of the external memory card: Environment. External.getexternalstoragedirectory () - > < external - path name = "ext_root path ="/"/" > <! - 4, and the corresponding external memory card APP public directory under the root directory: Context. GetExternalFileDir (String) - > < external files - path name = "ext_pub path ="/"/" > <! - 5, and the corresponding external memory card under the root directory of the APP cache directory: Context. GetExternalCacheDir () - > < external cache - the path name = "ext_cache path ="/"/" > <! - 6, corresponding external memory card under the root directory of the APP cache directory: Context. GetExternalMediaDirs () - > < external media - path name = "ext_media path ="/"/" > < / paths >Copy the code
Setup2:
Declare in androidmanifest.xml:
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:requestLegacyExternalStorage="true"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.AndroidStorageDemo">
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="com.alexlu.androidstorage.fileProvider"
android:enabled="true"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/filepaths" />
</provider>
</application>
Copy the code
Pay attention to the android: authorities = “com. Alexlu. Androidstorage. FileProvider” modify for their own package name,
Setup3:
Use the same package name as the one registered in the configuration file:
val file = File(xxpath)
val uri = FileProvider.getUriForFile(context, "com.alexlu.androidstorage.fileProvider", file);
Copy the code
Why build an XML file?
The name parameter in the XML is the name of the folder you defined. If the path is empty or /, it represents all the paths
Meaning of different methods in XML
Here is a one-to-one correspondence:
<files-path/> --> Context.getFilesDir()
<cache-path/> --> Context.getCacheDir()
<external-path/> --> Environment.getExternalStorageDirectory()
<external-files-path/> --> Context.getExternalFilesDir(String)
<external-cache-path/> --> Context.getExternalCacheDir()
<external-media-path/> --> Context.getExternalMediaDirs()
Copy the code
Let’s take a look at what the different Api paths look like:
Log.d(TAG,Environment.getExternalStorageDirectory().absolutePath) ///storage/emulated/0 Log.d(TAG,Environment.getRootDirectory().absolutePath) ///system Log.d(TAG,Environment.getDataDirectory().absolutePath) ///data Log.d(TAG,Environment.getDownloadCacheDirectory().absolutePath) ///data/cache if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.R) { Log.d(TAG,"getDownloadCacheDirectory():"+Environment.getStorageDirectory().absolutePath) ///storage } Log.d(TAG,this.filesDir.absolutePath) ///data/user/0/com.alexlu.androidstorage/files Log.d(TAG,this.cacheDir.absolutePath) ///data/user/0/com.alexlu.androidstorage/cache Log.d(TAG,this.codeCacheDir.absolutePath) ///data/user/0/com.alexlu.androidstorage/code_cache Log.d(TAG,"externalCacheDir:"+this.externalCacheDir? .absolutePath) ///storage/emulated/0/Android/data/com.alexlu.androidstorage/cache this.externalMediaDirs.forEach { Log.d(TAG,"externalMediaDirs:"+it.absolutePath) ///storage/emulated/0/Android/media/com.alexlu.androidstorage } Log.d(TAG,"getExternalFilesDir:"+this.getExternalFilesDir(null)? .absolutePath) ///storage/emulated/0/Android/data/com.alexlu.androidstorage/filesCopy the code
Storage /emulated/0 represents the external storage space, /data/user/0 represents the memory storage space, and the package name description is the APP private directory or partition storage directory.
We can create the file happily through the above method
Example:
Use an internal private directory
The internal private space of an App is small. Therefore, you are advised to perform this operation with caution. Check the available space before reading and writing. You are advised to save large files in an external partition directory.
To obtain the remaining available space, you are advised to view StorageStatsManager
Obtain the cache file path in the private memory directory
fun getAppCachePath(context: Context, subDir:String? =null):String{ val path = StringBuilder(context.cacheDir.absolutePath) subDir? .let { path.append(File.separator).append(it).append(File.separator) } val dir = File(path.toString()) if (! dir.exists()) dir.mkdir() return path.toString() }Copy the code
Obtain the path of files files in the internal private directory
fun getAppFilePath(context: Context, subDir:String? =null): String { val path = StringBuilder(context.filesDir.absolutePath) subDir? .let { path.append(File.separator).append(it).append(File.separator) } val dir = File(path.toString()) if (! dir.exists()) dir.mkdir() return path.toString() }Copy the code
You can choose to create subdirectories, where subDis represents the subdirectory folder name
Let’s save two images to its directory:
Val file = file (fileutil.getAppCachePath (this),"${System.currentTimemillis ()}.jpg") Val file2 = File(fileutil.getAppFilepath (this,"image"),"${system.currentTimemillis ()}.jpg") OperatePicUtil.saveBitmap2File(this,bitmap,file) OperatePicUtil.saveBitmap2File(this,bitmap,file2)Copy the code
(See Demo for the OperatePicUtil utility class)
We can see in the Android12 virtual machine save no problem:
Use an external public directory
Environment. External.getexternalstoragedirectory () after Android10 identified as obsolete, which means that the API is very good, but I don’t want to let you use the actual test on Android12 still useful.
/** * TODO external directory -Pictures * @param subDir subdirectory folder name * @return */ fun getExternalPicturesPath(subDir:String? =null): String{ val path = StringBuilder(Environment.getExternalStorageDirectory().absolutePath) .append(File.separator) .append(Environment.DIRECTORY_PICTURES) subDir? .let { path.append(File.separator).append(it).append(File.separator) } val dir = File(path.toString()) if (! Dir.exists ()) dir.mkdir() return path.tostring ()} /** * TODO external directory -Download * @param subDir Subdirectory folder name * @return */ fun getExternalDownloadPath(subDir:String? =null): String{ val path = StringBuilder(Environment.getExternalStorageDirectory().absolutePath) .append(File.separator) .append(Environment.DIRECTORY_DOWNLOADS) subDir? .let { path.append(File.separator).append(it).append(File.separator) } val dir = File(path.toString()) if (! dir.exists()) dir.mkdir() return path.toString() }Copy the code
Use partitions to store directories
Interface definition: Obtain the file,cache, and media directories respectively. Type indicates the subdirectory name, which can be null
@Override
public File getExternalFilesDir(String type) {
return mBase.getExternalFilesDir(type);
}
@Override
public File[] getExternalFilesDirs(String type) {
return mBase.getExternalFilesDirs(type);
}
@Override
public File getExternalCacheDir() {
return mBase.getExternalCacheDir();
}
@Override
public File[] getExternalCacheDirs() {
return mBase.getExternalCacheDirs();
}
@Override
public File[] getExternalMediaDirs() {
return mBase.getExternalMediaDirs();
}
Copy the code
Obtain the partition storage directory:
/** * TODO partition storage -File directory * @param context * @param subDir folder name * @return */ fun getExternalAppFilePath(context: context) Context,subDir: String? =null):String{ val path = context.getExternalFilesDir(subDir)? .absolutePath val dir = File(path.toString()) if (! dir.exists()) dir.mkdir() return path.toString() }Copy the code
Save the image to the partition storage directory:
val file = File(FileUtil.getExternalAppFilePath(this),"${System.currentTimeMillis()}.jpg")
PicturesUtil.saveBitmap2File(context = this,bitmap = bitmap,file = file)
Copy the code
We can print the file path as:
/sdcard/Android/data/com.alexlu.androidstorage/files
Copy the code
“Clear Storage Space” and “Clear Cache” in the App Settings interface
- Clear the storage space: Clear the app all saved files, preferences, database and other information, but not including the external public directory files, equivalent to the app uninstallation
- Clear cache: clears the cache
External partitioned storage
andInternal private storage
In thecache
directory
Other file Operations
For example, write other types of files, save documents, audio files, insert pictures into the system album. Please check out the Github Demo for details. If there is an error, please point out and welcome to click Star