文件操作
- 内部存储
- 外置SD卡
- 内置SD卡:/storage/emulated/0,其中又分为私有目录(Android/)和公共目录



apk
- res/raw下的和assert下的,这些数据只能读取,不能写入。单个文件大小不能超过1M。
- res/raw不可以有目录结构,而assets则可以有目录结构。
- res/raw中的文件会被映射到R.java文件中,访问的时候直接使用资源ID即R.id.filename;assets文件夹下的文件不会被映射到R.java中,访问的时候需要AssetManager类。
- 读取
| InputStream is = getResources().openRawResource(R.string.app_name); |
| InputStream is = getResources().getAssets().open("file.txt"); |
内部存储
-
内部存储 和外部存储以是否是应用的安装目录来划分,内部存储是在应用的安装目录下,外部存储 在应用的安装目录外。
-
内部存储的文件是应用的私有文件,其他应用不能访问。
-
data是应用安装目录,需要root权限才能查看
-
应用访问自己的内部存储不需要权限,访问外部存储有可能需要申请权限。
-
应用卸载后,文件会被移除。
-
创建模式
- MODE_APPEND:即向文件尾写入数据
- MODE_PRIVATE:即仅打开文件可写入数据
- MODE_WORLD_READABLE:所有程序均可读该文件数据,api17废弃
- MODE_WORLD_WRITABLE:即所有程序均可写入数据,api17废弃
-
获取内部存储的方式
| |
| File file1 = getDir("newFolder", MODE_PRIVATE); |
| |
| |
| File file2 = getDir("", MODE_PRIVATE); |
| |
| |
| File file3 = getFilesDir(); |
| |
| |
| File file4 = getCacheDir(); |
| |
| |
| String s = getApplicationInfo().dataDir; |
| File file = new File(context.getFilesDir(), filename); |
| |
| |
| String fileContents = "Hello world!"; |
| |
| |
| |
| try (FileOutputStream fos = openFileOutput("myfile", Context.MODE_PRIVATE)) { |
| fos.write(fileContents.getBytes()); |
| } catch (IOException e) { |
| e.printStackTrace(); |
| } |
| |
| File file = new File(getFilesDir(), "myfile"); |
| Log.d(TAG, String.valueOf(file.exists())); |
| Log.d(TAG, file.getAbsolutePath()); |
| |
| |
| String fileName = "myfile"; |
| FileInputStream fis = context.openFileInput(fileName); |
| InputStreamReader inputStreamReader = |
| new InputStreamReader(fis, StandardCharsets.UTF_8); |
| StringBuilder stringBuilder = new StringBuilder(); |
| try (BufferedReader reader = new BufferedReader(inputStreamReader)) { |
| String line = reader.readLine(); |
| while (line != null) { |
| stringBuilder.append(line).append('\n'); |
| line = reader.readLine(); |
| } |
| } catch (IOException e) { |
| e.printStackTrace(); |
| } finally { |
| String contents = stringBuilder.toString(); |
| Log.d(TAG, "content:"+contents); |
| } |
| |
| String[] files = fileList(); |
| for (String file : files) { |
| Log.d(TAG, file); |
| } |
| File directory = context.getFilesDir(); |
| |
| File file = new File(directory, filename); |
| |
| |
| |
| |
| File.createTempFile(filename, null, context.getCacheDir()); |
| |
| File cacheFile = new File(context.getCacheDir(), filename); |
| |
| cacheFile.delete(); |
| |
| context.deleteFile(cacheFileName); |
外部存储

-
对于支持外插SD卡的设备,外部存储包括两部分:内置存储卡和外置SD卡。
-
Android 4.3 以下,只能通过Context.getExternalFilesDir(type) 来获取外部存储在内置存储卡分区的私有目录,无法获取外置SD卡。
-
Android 4.3 开始,可以通过Context.getExternalFilesDirs(type) 获取一个File数组,包含了内置存储卡分区和外置SD的私有目录地址。
-
可以使用兼容库的静态方法 ContextCompate.getExternalFilesDirs() 兼容 4.3。
-
对外部存储空间访问所需的权限
| <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> |
| <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/> |
版本 |
存储位置 |
是否需要读写权限 |
Android 4.4以前 |
外部存储(公共目录和私有目录) |
需要 |
Android 4.4以后 |
外部存储(公共目录) |
需要 |
Android 4.4以后 |
外部存储(私有目录) |
不需要 |
Android 9(API 级别 28)或更低版本的设备上,只要其他应用具有相应的存储权限,任何应用都可以访问外部存储空间中的应用专属文件。
Android 10(API 级别 29)及更高版本为目标平台的应用在默认情况下被赋予了对外部存储空间的分区访问权限(即分区存储)。此类应用只能访问外部存储空间上的应用专属目录,以及本应用所创建的特定类型的媒体文件。
| |
| private boolean isExternalStorageWritable() { |
| return Environment.getExternalStorageState() == Environment.MEDIA_MOUNTED; |
| } |
| |
| |
| private boolean isExternalStorageReadable() { |
| return Environment.getExternalStorageState() == Environment.MEDIA_MOUNTED || |
| Environment.getExternalStorageState() == Environment.MEDIA_MOUNTED_READ_ONLY; |
| } |
| |
| public void requestPermission() { |
| if (ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { |
| |
| ActivityCompat.requestPermissions(MainActivity.this, |
| new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, 1); |
| } |
| if (ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { |
| ActivityCompat.requestPermissions(MainActivity.this, |
| new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, 1); |
| } |
| } |
| |
| @Override |
| public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { |
| |
| switch (requestCode) { |
| case 1: |
| if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { |
| Log.d(TAG, "已授权"); |
| } else { |
| Toast.makeText(this, "已拒绝权限", Toast.LENGTH_SHORT).show(); |
| } |
| break; |
| default: |
| } |
| } |
- 在没有可移除外部存储空间的设备上,请使用以下命令启用虚拟卷,以测试外部存储空间可用性逻辑
| adb shell sm set-virtual-disk true |
| File file = getExternalFilesDir(null); |
| Log.d(TAG, "" + file); |
| |
| file = getExternalCacheDir(); |
| Log.d(TAG, "" + file); |
| |
| |
| Environment.getExternalStorageDirectory().getAbsolutePath(); |
| |
| |
| |
| File[] files = getExternalFilesDirs(null); |
| Log.d(TAG, ""+ files[0]); |
| Log.d(TAG, ""+ files[1]); |
| |
| files = getExternalCacheDirs(); |
| Log.d(TAG, ""+ files[0]); |
| Log.d(TAG, ""+ files[1]); |
- 内置sd卡路径:/storage/emulated/0才是最终源头,/mnt/sdcard和/sdcard是指向它的一个软连接而已。
- /mnt/sdcard指向/storage/self/primary

- /sdcard指向/storage/self/primary

- /storage/self/primary指向/storage/emulated/0

| |
| |
| File[] externalStorageVolumes = |
| ContextCompat.getExternalFilesDirs(getApplicationContext(), null); |
| |
| File primaryExternalStorage = externalStorageVolumes[0]; |
| |
| |
| File appSpecificExternalDir = new File(context.getExternalFilesDir(null), filename); |
| |
| File externalCacheFile = new File(context.getExternalCacheDir(), filename); |
| |
| externalCacheFile.delete(); |
| @Nullable |
| File getAppSpecificAlbumStorageDir(Context context, String albumName) { |
| |
| |
| File file = new File(context.getExternalFilesDir( |
| Environment.DIRECTORY_PICTURES), albumName); |
| if (file == null || !file.mkdirs()) { |
| Log.e(LOG_TAG, "Directory not created"); |
| } |
| return file; |
| } |
| getDatabasePath():>/data/user/0/com.example.myfile/databases/sample.db |
| getCacheDir():>/data/user/0/com.example.myfile/cache |
| getFilesDir():>/data/user/0/com.example.myfile/files |
| getDir("mydir"):>/data/user/0/com.example.myfile/app_webview/Web Data |
| getPackageCodePath():>/data/app/~~yaBwJJfuCBkU_v4FpprZuA==/com.example.myfile-MU9Z1oQkiOSdUFS7RKD0gw==/base.apk |
| getPackageResourcePath():/data/app/~~yaBwJJfuCBkU_v4FpprZuA==/com.example.myfile-MU9Z1oQkiOSdUFS7RKD0gw==/base. |
| getExternalFilesDir():/storage/emulated/0/Android/data/com.example.myfile/files |
| getExternalFilesDirs():---/storage/emulated/0/Android/data/com.example.myfile/files |
| getExternalCacheDir():/storage/emulated/0/Android/data/com.example.myfile/cache |
| getExternalCacheDirs():---/storage/emulated/0/Android/data/com.example.myfile/cache |
| getObbDir():/storage/emulated/0/Android/obb/com.example.myfile |
| getObbDirs():---/storage/emulated/0/Android/obb/com.example.myfile |
| Environment.getExternalStorageState():mounted |
| Environment.getExternalStorageDirectory():/storage/emulated/0 |
| Environment.getDownloadCacheDirectory():/data/cache |
| Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MUSIC):/storage/emulated/0/Music |
| Environment.getRootDirectory():/system |
查询可用空间
getAllocatableBytes()
的返回值可能大于设备上的当前可用空间量。这是因为系统已识别出可以从其他应用的缓存目录中移除的文件。
| |
| private static final long NUM_BYTES_NEEDED_FOR_MY_APP = 1024 * 1024 * 10L; |
| |
| StorageManager storageManager = |
| getApplicationContext().getSystemService(StorageManager.class); |
| UUID appSpecificInternalDirUuid = storageManager.getUuidForPath(getFilesDir()); |
| long availableBytes = |
| storageManager.getAllocatableBytes(appSpecificInternalDirUuid); |
| if (availableBytes >= NUM_BYTES_NEEDED_FOR_MY_APP) { |
| storageManager.allocateBytes( |
| appSpecificInternalDirUuid, NUM_BYTES_NEEDED_FOR_MY_APP); |
| } else { |
| Intent storageIntent = new Intent(); |
| storageIntent.setAction(ACTION_MANAGE_STORAGE); |
| |
| } |
| package com.example.myfile; |
| |
| import android.app.ActivityManager; |
| import android.content.Context; |
| import android.os.Environment; |
| import android.os.StatFs; |
| import android.os.storage.StorageManager; |
| import android.text.TextUtils; |
| import android.text.format.Formatter; |
| import android.util.Log; |
| |
| import java.io.File; |
| import java.lang.reflect.Method; |
| |
| public class StorageInfo { |
| private static final int INTERNAL_STORAGE = 0; |
| private static final int EXTERNAL_STORAGE = 1; |
| private static final String TAG = "wmj"; |
| |
| |
| public static String getRAMInfo(Context context) { |
| long totalSize = 0; |
| long availableSize = 0; |
| |
| ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE); |
| |
| ActivityManager.MemoryInfo memoryInfo = new ActivityManager.MemoryInfo(); |
| activityManager.getMemoryInfo(memoryInfo); |
| totalSize = memoryInfo.totalMem; |
| availableSize = memoryInfo.availMem; |
| |
| return "内存可用/总共:" |
| + Formatter.formatFileSize(context, availableSize) |
| + "/" + Formatter.formatFileSize(context, totalSize); |
| } |
| |
| |
| public static boolean isSDCardMount() { |
| return Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED); |
| } |
| |
| |
| public static String getROMInfo(Context context) { |
| |
| StatFs statFs = new StatFs(Environment.getDataDirectory().getPath()); |
| long totalCounts = statFs.getBlockCountLong(); |
| long availableCounts = statFs.getAvailableBlocksLong(); |
| long size = statFs.getBlockSizeLong(); |
| long availROMSize = availableCounts * size; |
| long totalROMSize = totalCounts * size; |
| |
| return "内置sd卡可用/总共:" |
| + Formatter.formatFileSize(context, availROMSize) + "/" |
| + Formatter.formatFileSize(context, totalROMSize); |
| } |
| |
| |
| public static String getSDCardInfo(Context context) { |
| |
| if (isSDCardMount()) { |
| |
| |
| |
| |
| |
| |
| File[] files = context.getExternalFilesDirs(null); |
| StatFs statFs = new StatFs(files[1].getAbsolutePath()); |
| long totalCounts = statFs.getBlockCountLong(); |
| long availableCounts = statFs.getAvailableBlocksLong(); |
| long size = statFs.getBlockSizeLong(); |
| long availROMSize = availableCounts * size; |
| long totalROMSize = totalCounts * size; |
| |
| return "外置sd卡可用/总共:" |
| + Formatter.formatFileSize(context, availROMSize) + "/" |
| + Formatter.formatFileSize(context, totalROMSize); |
| } else { |
| return "无外置SD卡"; |
| } |
| } |
| |
| |
| public static String getStorageInfo(Context context, int type) { |
| String path = getStoragePath(context, type); |
| |
| if (!isSDCardMount() || TextUtils.isEmpty(path) || path == null) { |
| return "无外置SD卡"; |
| } |
| |
| File file = new File(path); |
| StatFs statFs = new StatFs(file.getPath()); |
| |
| long blockCount = statFs.getBlockCountLong(); |
| long availableBlocks = statFs.getAvailableBlocksLong(); |
| long blockSize = statFs.getBlockSizeLong(); |
| long totalSpace = blockSize * blockCount; |
| long availableSpace = availableBlocks * blockSize; |
| |
| return "可用/总共:" |
| + Formatter.formatFileSize(context, availableSpace) + "/" |
| + Formatter.formatFileSize(context, totalSpace); |
| } |
| |
| |
| public static String getStoragePath(Context context, int type) { |
| StorageManager sm = (StorageManager) context.getSystemService(Context.STORAGE_SERVICE); |
| try { |
| Method getPathsMethod = sm.getClass().getMethod("getVolumePaths", (Class<?>[]) null); |
| String[] path = (String[]) getPathsMethod.invoke(sm, (Object[]) null); |
| switch (type) { |
| case INTERNAL_STORAGE: |
| return path[type]; |
| case EXTERNAL_STORAGE: |
| if (path.length > 1) { |
| return path[type]; |
| } else { |
| return null; |
| } |
| default: |
| break; |
| } |
| } catch (Exception e) { |
| e.printStackTrace(); |
| } |
| return null; |
| } |
| } |
| Log.d(TAG, "外置sd卡是否加载:" + StorageInfo.isSDCardMount()); |
| Log.d(TAG, StorageInfo.getRAMInfo(this)); |
| Log.d(TAG, StorageInfo.getROMInfo(this)); |
| Log.d(TAG, StorageInfo.getSDCardInfo(this)); |
| Log.d(TAG, "内置sd卡"+StorageInfo.getStorageInfo(this, 0)); |
| Log.d(TAG, "外置sd卡"+StorageInfo.getStorageInfo(this, 1)); |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步