参考文档:Save files on device storage
目录
1. 定义
在Android中,将文件存储在设备上分为内部存储(internal storage)和外部存储(external storage)。引用官方文档,这些名称来自Android的早期,当时大多数设备提供内置的非易失性存储器(内部存储),以及可移动存储介质,如micro SD卡(外部存储)。现在,许多设备将永久存储空间划分为单独的“内部”和“外部”分区。因此,即使没有可移动存储介质,这两个存储空间也始终存在,无论外部存储是否可移动,API行为都是相同的。
1.1 什么是内部存储
Internal Storage 译为内部存储,是设备内存中私有数据的存储方式。默认情况下,将文件保存并加载到内部存储对应用程序是私有的,其他应用程序将无法访问这些文件。当用户卸载应用程序时,与应用程序关联的内部存储文件也被删除。
文件存储在 /data/data/包名/ 目录下,如:/data/data/包名/files 、/data/data/包名/cache、/data/data/包名/shared_prefs
如果你进行 adb 操作来查看该目录的时候遇到 Permission denied,此时说明的权限不够,你需要切换到 root 用户来进行查看(命令 adb root 来切换到 root 用户),或者当你不想切换到 root 用户时,可以通过 run-as 包名 命令来查看该目录,例如:run-as com.jere.test,如下图所示:
1.2 什么是外部存储
External Storage 译为外部存储,当你想存储与其他应用程序共享或允许用户通过电脑访问的文件时,这时就可以使用外部存储方式。外部存储通常可以通过可移动设备来实现,比如SD卡。Android 使用路径表示这些设备,例如/sdcard。
外部存储的文件分为公有文件和私有文件:
公有文件:共享提供给其他应用程序和用户。当用户卸载该应用程序时,这些共有文件不会被删除并且对用户仍然是可用的。例如,应用程序捕获的照片应该保存为公共文件。文件存储在目录 /sdcard/ 下,需要获得写入与读取权限,通过Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM) 方法来访问应用程序目录中文件。
私有文件:不与其他应用程序共享,当用户卸载应用程序时会清理这些文件。文件存储在目录 /sdcard/Android/data/包名/ 下,如:/sdcard/Android/data/com.jere.test/files、/sdcard/Android/data/com.jere.test/cache。通过Context.getExternalFilesDir()、Context.getExternalCacheDir() 访问应用程序目录中的文件,此方法不需要获取写入与读取权限。
1.3 内部存储与外部存储的区别
内部存储:
- 它始终可用。
- 此处保存的文件只能在你的应用内访问。
- 当用户卸载您的应用时,系统会从内部存储中删除所有应用程序的文件。
- 不需要获得系统权限就可以执行写入与读取操作
如果您想确保用户和其他应用程序都无法访问您的文件,则最好使用内部存储。
外部存储:
- 它并不总是可用的,因为用户可以将外部存储装载为USB存储器,并在某些情况下将其从设备中移除。
- 它是世界可读的,因此保存在此处的文件可能会在您的控制之外被读取。
- 当用户卸载您的应用程序时,只有将应用程序的文件保存在包名目录中(/sdcard/Android/data/包名/)时,系统才会从此处删除应用程序的文件,用 Context.getExternalFilesDir() 方法来获取目录路径。
对于不需要访问限制的文件以及要与其他应用程序共享或允许用户使用计算机访问的文件,外部存储是最佳位置
2. 实践例子Demo
用两个简单的例子来展示,内部存储与外部存储。
2.1 内部存储Demo
写入文件步骤:
- 调用 openFileOutput() 方法来获取写入内部目录中文件的 FileOutputStream 对象。
- 调用 FileOutputStream.write() 方法,写入数据。
- 调用 FileOutputStream.close() 方法,关闭数据流。
读取文件步骤:
- 调用 openInputStream() 方法来获取 FileInputStream 对象。
- 调用 FileInputStream.read() 方法,读取数据。
- 调用 FileInputStream.close() 方法,关闭数据流。
实践:点击 Write File 按钮将 EditText 里的内容保存,然后点击 Read File 按钮读取文件,并将内容显示出来。
详细代码:
public class InternalStorageTestActivity extends AppCompatActivity implements View.OnClickListener {
public static final String FILE_NAME = "jereTest.txt";
private EditText mInputContentEt;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_internal_storage_test);
Button writeBtn = findViewById(R.id.write_btn);
Button readBtn = findViewById(R.id.read_btn);
writeBtn.setOnClickListener(this);
readBtn.setOnClickListener(this);
mInputContentEt = findViewById(R.id.input_content_et);
}
@Override
public void onClick(View view) {
switch (view.getId()) {
case R.id.write_btn:
writeFile(mInputContentEt.getText().toString());
break;
case R.id.read_btn:
readFile();
break;
default:
break;
}
}
private void writeFile(String fileContent) {
FileOutputStream fileOutputStream = null;
try {
fileOutputStream = openFileOutput(FILE_NAME, Context.MODE_PRIVATE);
fileOutputStream.write(fileContent.getBytes());
fileOutputStream.close();
Toast.makeText(this, "Write File Successful", Toast.LENGTH_SHORT).show();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
private void readFile() {
try {
FileInputStream fileInputStream = openFileInput(FILE_NAME);
int c;
StringBuilder temp = new StringBuilder();
while ( (c = fileInputStream.read()) != -1) {
temp.append(Character.toString((char) c));
}
fileInputStream.close();
Toast.makeText(this, temp.toString(), Toast.LENGTH_SHORT).show();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
adb 命令查看刚刚所保存的文件所处位置:
2.2 外部存储Demo
详细代码:
public class ExternalStorageTestActivity extends AppCompatActivity implements View.OnClickListener {
private File mExternalFile;
private EditText mInputContentTv;
private TextView mDisplayResponseTv;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_external_storage_test);
mInputContentTv = findViewById(R.id.external_storage_input_content_et);
mDisplayResponseTv = findViewById(R.id.external_storage_display_tv);
Button writeBtn = findViewById(R.id.external_storage_write_btn);
Button readBtn = findViewById(R.id.external_storage_read_btn);
writeBtn.setOnClickListener(this);
readBtn.setOnClickListener(this);
if (!isExternalStorageWritable()) {
writeBtn.setEnabled(false);
readBtn.setEnabled(false);
} else {
//子目录
String subFileDirPath = "JereTestFile";
//文件名
String fileName = "MyExternalStorageTest.txt";
mExternalFile = new File(getExternalFilesDir(subFileDirPath), fileName);
}
}
/**
* 检查外部存储是否可用来读写
*/
public boolean isExternalStorageWritable() {
String state = Environment.getExternalStorageState();
return Environment.MEDIA_MOUNTED.equals(state);
}
@Override
public void onClick(View view) {
switch (view.getId()) {
case R.id.external_storage_write_btn:
writeFile();
break;
case R.id.external_storage_read_btn:
readFile();
break;
default:
break;
}
}
private void writeFile() {
try {
FileOutputStream fos = new FileOutputStream(mExternalFile);
fos.write(mInputContentTv.getText().toString().getBytes());
fos.close();
Toast.makeText(ExternalStorageTestActivity.this,
"saved file to External Storage successful",
Toast.LENGTH_SHORT)
.show();
} catch (IOException e) {
e.printStackTrace();
}
}
private void readFile() {
StringBuilder responseData = new StringBuilder();
try {
FileInputStream fis = new FileInputStream(mExternalFile);
DataInputStream in = new DataInputStream(fis);
BufferedReader br = new BufferedReader(new InputStreamReader(in));
String strLine;
while ((strLine = br.readLine()) != null) {
responseData.append(strLine);
}
in.close();
} catch (IOException e) {
e.printStackTrace();
}
mDisplayResponseTv.setText(responseData);
}
}
adb 查看保存的文件所处位置
源码请看?:android 文件存储Demo