一、Android存储分为:
内部存储(Internal Storage)和外部存储(External Storage)
特点
:
- 内部存储:随应用卸载被删除
- 外部存储:
1.公有目录:存放一些下载的视频文件等,比如还有movies,fictures,music等公有的一些文件目录
2.私有目录:随应用卸载被删除
下面来具体介绍一下内部存储和外部存储的具体路径:
1、内部存储:不需要任何权限
- /data/data/< applicationId,就是包名 >/shared_prefs
- /data/data/< applicationId >/databases
- /data/data/< applicationId >/files 通过context.getFilesDir() 获取该目录
- /data/data/< applicationId >/cache 通过context.getCacheDir() 获取该目录
Android SDK提供了几个常见的内部存储文件的权限
- Context.MODE_PRIVATE :私有方式存储,其他应用无法访问,覆盖旧的同名文件
- Context.MODE_APPEND:私有方式存储,若有旧的同名文件,则在该文件上追加数据
2、外部存储:
- 公有目录:Environment.getExternalStoragePublicDirectory(int type),这里的type类型不能为空,可以是DIRECTORY_MUSIC,DIRECTORY_MOVIES等 通过这个方法获取公有目录下相对应的文件
Environment.getExternalStorageDirectory()也可以获取。
注:Environment是操作SD卡的工具类
//publicDirectory路径为:/storage/emulated/0/Movies String publicDirectory = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MOVIES).getAbsolutePath(); //sdPath路径为: /storage/emulated/0 String sdPath = Environment.getExternalStorageDirectory().getAbsolutePath();
- 私有目录:
/mnt/sdcard/Android/data/data/< applicationId >/cache 通过getExternalCacheDir().getAbsolutePath()得到的路径为:
/storage/emulated/0/Android/data/包名/cache等价于上面的路径。
/mnt/sdcard/Android/data/data/< applicationId >/files 通过getExternalFilesDir().getAbsolutePath()方法可以获取到路径为:
/storage/emulated/0/Android/data/包名/files等价于上面的路径。
注意:
外部存储的私有目录文件会随着应用的卸载而被删除,公有目录则不会被删除。
再看一下具体区别:
下面演示内部存储数据的一个实例
一、activity_internal.xml布局文件
<?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".InternalActivity"> <EditText android:id="@+id/edit_username" android:layout_width="match_parent" android:layout_height="wrap_content" android:hint="请输入用户名" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toTopOf="parent" /> <EditText android:id="@+id/edit_password" android:layout_width="match_parent" android:layout_height="wrap_content" android:hint="请输入密码" android:inputType="textPassword" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toBottomOf="@id/edit_username" /> <Button android:id="@+id/btn_save_userMessage" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="保存用户信息" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toBottomOf="@id/edit_password" /> <Button android:id="@+id/btn_read" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="读取用户信息" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toBottomOf="@id/btn_save_userMessage" /> <TextView android:id="@+id/tv_message" android:layout_width="match_parent" android:layout_height="wrap_content" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toBottomOf="@id/btn_read" /> </androidx.constraintlayout.widget.ConstraintLayout>
二、InternalActivity类,实现具体的存储和读取操作
public class InternalActivity extends AppCompatActivity implements View.OnClickListener { private EditText edit_username; private EditText edit_password; private Button btn_save_userMessage; private Button btn_read; private TextView tv_message; //文件名 private String fileName = "test"; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_internal); edit_username = findViewById(R.id.edit_username); edit_password = findViewById(R.id.edit_password); btn_save_userMessage = findViewById(R.id.btn_save_userMessage); btn_read = findViewById(R.id.btn_read); tv_message = findViewById(R.id.tv_message); btn_save_userMessage.setOnClickListener(this); btn_read.setOnClickListener(this); } @Override public void onClick(View v) { switch (v.getId()) { case R.id.btn_save_userMessage: String username = edit_username.getText().toString(); String password = edit_password.getText().toString(); if (TextUtils.isEmpty(username)) { Toast.makeText(this, "用户名不能为空!", Toast.LENGTH_SHORT).show(); return; } if (TextUtils.isEmpty(password)) { Toast.makeText(this, "密码不能为空!", Toast.LENGTH_SHORT).show(); return; } FileOutputStream fos = null; try { //第一个参数:文件名 //第二个参数:表示文件输出的类型 这里选择Context.MODE_PRIVATE每次生成相同的文件名,则覆盖原有的文件 fos = openFileOutput(fileName, Context.MODE_PRIVATE); String nameAndPassword = username + "." + password; byte[] bytes = nameAndPassword.getBytes(); fos.write(bytes); } catch (Exception e) { e.printStackTrace(); } finally { if (fos != null) { try { fos.close(); } catch (IOException e) { e.printStackTrace(); } } } Toast.makeText(this, "保存成功!", Toast.LENGTH_SHORT).show(); break; case R.id.btn_read: FileInputStream fis = null; try { fis = openFileInput(fileName); //fis.available() 判断文件有多少个字节 byte[] bytes = new byte[fis.available()]; while (fis.read(bytes) != -1) { String message = new String(bytes); String[] split = message.split("\\."); tv_message.setText("用户名:" + split[0] + "\n" + "密码:" + split[1]); } } catch (Exception e) { e.printStackTrace(); } break; default: break; } } }
效果演示如下:
下面演示外部存储数据的一个实例
一、布局页面activity_external.xml
<?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".ExternalActivity"> <EditText android:id="@+id/edit_username" android:layout_width="match_parent" android:layout_height="wrap_content" android:hint="请输入用户名" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toTopOf="parent" /> <EditText android:id="@+id/edit_password" android:layout_width="match_parent" android:layout_height="wrap_content" android:hint="请输入密码" android:inputType="textPassword" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toBottomOf="@id/edit_username" /> <Button android:id="@+id/btn_save_userMessage" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="保存用户信息到外部存储" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toBottomOf="@id/edit_password" /> <Button android:id="@+id/btn_read" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="从外部存储读取用户信息" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toBottomOf="@id/btn_save_userMessage" /> <TextView android:id="@+id/tv_message" android:layout_width="match_parent" android:layout_height="wrap_content" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toBottomOf="@id/btn_read" /> </androidx.constraintlayout.widget.ConstraintLayout>
二、ExternalActivity类实现保存和读取数据的功能。
注意
:在使用外部存储之前,需要调用Environment类的getExternalStorageState()方法来检查存储介质状态,是否可用。还是仅可以读,不能写等。
public class ExternalActivity extends AppCompatActivity implements View.OnClickListener { private EditText edit_username; private EditText edit_password; private Button btn_save_userMessage; private Button btn_read; private TextView tv_message; //文件名 private String fileName = "test"; //外部状态可用 boolean mExternalStorageAvailable = false; //外部状态可写 boolean mExternalStorageWrite = false; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_external); edit_username = findViewById(R.id.edit_username); edit_password = findViewById(R.id.edit_password); btn_save_userMessage = findViewById(R.id.btn_save_userMessage); btn_read = findViewById(R.id.btn_read); tv_message = findViewById(R.id.tv_message); btn_save_userMessage.setOnClickListener(this); btn_read.setOnClickListener(this); } @Override public void onClick(View v) { switch (v.getId()) { case R.id.btn_save_userMessage: String username = edit_username.getText().toString(); String password = edit_password.getText().toString(); if (TextUtils.isEmpty(username)) { Toast.makeText(this, "用户名不能为空!", Toast.LENGTH_SHORT).show(); return; } if (TextUtils.isEmpty(password)) { Toast.makeText(this, "密码不能为空!", Toast.LENGTH_SHORT).show(); return; } //首先判断外部存储设备的状态 checkExternalStorage(); OutputStream outputStream = null; try { if (mExternalStorageWrite) { outputStream = new FileOutputStream(new File(getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS), fileName)); String nameAndPassword = username + "." + password; byte[] bytes = nameAndPassword.getBytes(); outputStream.write(bytes); } } catch (Exception e) { e.printStackTrace(); } finally { if (outputStream != null) { try { outputStream.close(); } catch (IOException e) { e.printStackTrace(); } } } Toast.makeText(this, "保存成功!", Toast.LENGTH_SHORT).show(); break; case R.id.btn_read: checkExternalStorage(); FileInputStream fis = null; try { //第一个参数:文件目录 第二个参数:文件名 //getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS)对应的路径为: // /storage/emulated/0/Android/data/com.example.customviewproject/files/Download File file = new File(getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS), fileName); if (mExternalStorageAvailable) { fis = new FileInputStream(file); //判断当前文件的字节个数 byte[] bytes = new byte[fis.available()]; while (fis.read(bytes) != -1) { String message = new String(bytes); String[] split = message.split("\\."); tv_message.setText("用户名:" + split[0] + "\n" + "密码:" + split[1]); } } } catch (Exception e) { e.printStackTrace(); } finally { if (fis != null) { try { fis.close(); } catch (IOException e) { e.printStackTrace(); } } } break; default: break; } } private void checkExternalStorage() { //获取外部存储器的状态 String state = Environment.getExternalStorageState(); //主要有两种状态 //外部状态可用 if (Environment.MEDIA_MOUNTED.equals(state)) { //外部存储器可用 表示既可以写 也可以读 mExternalStorageAvailable = true; mExternalStorageWrite = true; } else if (Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) { //外部存储器可用 表示仅可以读 不可写 mExternalStorageAvailable = true; mExternalStorageWrite = false; } else {//表示为不可用的状态 mExternalStorageAvailable = false; mExternalStorageWrite = false; } } }
效果演示:
操作assets目录下的图片文件,进行内部存储与读取操作,代码如下
public class InnerFileActivity extends AppCompatActivity { private ImageView imageView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_inner_file); imageView = findViewById(R.id.imageView); } public void save(View view) { //操作assets目录下的文件 AssetManager manager = getAssets(); try { //得到assets目录下图片的输入流 \src\main\assets\logo.png InputStream is = manager.open("logo.png"); //输出流指向的路径为:/data/data/com.example.customviewproject/files/logo.png FileOutputStream fileOutputStream = openFileOutput("logo.png", Context.MODE_PRIVATE); byte[] buffer = new byte[1024]; int len; while ((len = is.read(buffer)) != -1) { fileOutputStream.write(buffer, 0, len); } fileOutputStream.close(); is.close(); Toast.makeText(this, "保存成功!", Toast.LENGTH_SHORT).show(); } catch (IOException e) { e.printStackTrace(); } } public void read(View view) { /** * 图片保存的路径为: /data/data/com.example.customviewproject/files/logo.png */ //1.得到图片文件路径 //filesPath为: /data/user/0/com.example.customviewproject/files String filesPath = getFilesDir().getAbsolutePath(); String imagePath = filesPath + "/logo.png"; //根据图片路径得到bitmap对象 Bitmap bitmap = BitmapFactory.decodeFile(imagePath); imageView.setImageBitmap(bitmap); } }