Android 常用数据存储方式有 SharedPreferences 存储数据 (虽然还是属于内部存储)、文件存储(内部,外部)、SQLite 数据库存储、ContentProvider 存储数据、网络存储数据等几种。本篇博客主要是介绍 Shared Preference 的原理与使用,区分内部与外部文件存储,以及它们的使用方式。那就从清除缓存与清除数据到底清除了什么这个问题开始吧!
内部存储 InternalStorage
在 Android 开发中,内存 Memory、内部存储 InternalStorage、外部存储 ExternalStorage 这三者有啥区别呢? 在我们打开手机设置 -> 应用管理,随便选择一个软件,然后会看到一个是清除缓存的按钮,一个清除数据的按钮,那么当点击清除缓存的时候清除的是哪里的数据?当点击清除数据的时候又是清除的哪里的数据呢? 打开 Device File Explorer 会看到如下目录结构:

其实在使用 SharedPreferenced 的时候,将数据持久化存储于本地,其实就是存在这个文件中的 xml 文件里,App 里边的数据库文件就存储于 databases 文件夹中,还有我们的普通数据存储在 files 中,缓存文件存储在 cache 文件夹中,存储在这里的文件我们都称之为内部存储。
下面来说说使用内部存储的代表 ——SharedPreferences,SharedPreferences 也是在开发中使用的比较多的一种方案,用于存放一些类似登录的配置等信息。
SharedPreferences
1、用于存放一些类似登录的配置信息
2、本质上是一个 xml 文件,是通过类似键值对的方式存放信息
3、位于程序私有目录中,即 data/data/[packageName]/shared_prefs
SharedPreferences 的操作模式
1、MODE_APPEND:追加方式存储
2、MODE_PRIVATE:私有方式存储,其他应用无法访问
3、MODE_WORLD_READABLE:可被其他应用读取
4、MODE_WORLD_WRITEABLE:可被其他应用写入
SharedPreferences 使用方式:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
| @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate (savedInstanceState); setContentView (R.layout.activity_main); etUserName = findViewById (R.id.et_username); etPassword = findViewById (R.id.et_password); SharedPreferences login_info = getSharedPreferences ("login_info", MODE_PRIVATE); etUserName.setText (login_info.getString ("user_name", "")); etPassword.setText (login_info.getString ("password", "")); }
public void userLogin(View view) { String userName = etUserName.getText ().toString (); String password = etPassword.getText ().toString (); if(TextUtils.isEmpty (userName) || TextUtils.isEmpty (password)){ Toast.makeText (MainActivity.this, " 输入不完整 & quot;, Toast.LENGTH_SHORT).show (); }
SharedPreferences loginInfoSP = getSharedPreferences ("login_info", MODE_PRIVATE); SharedPreferences.Editor editor = loginInfoSP.edit (); editor.putString ("user_name", userName); editor.putString ("password", password); boolean commit = editor.commit (); Log.i (TAG, "userLogin: commitRet = " + commit);
if(!("admin".equals (userName) && "123456".equals (password))){ Toast.makeText (MainActivity.this, " 用户名或密码错误 & quot;, Toast.LENGTH_SHORT).show (); }else { Toast.makeText (MainActivity.this, " 登录成功 & quot;, Toast.LENGTH_SHORT).show (); } }
|
所以其实不难发现,SharedPreference 存取数据非常简单,存数据只需要四步,取数据只要两步。

通过查看设备上的文件也可以发现,SharedPreference 本质上就是一个 xml 文件而已,是通过类似键值对的方式存放信息。
获得内部存储目录
Context.getFileDir (),获取 /data/data/ 包名 /files
Context.getCacheDir (),获取 /data/data/ 包名 /cache,下面通过代码演示一下:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| public void saveToFileDir() { File filesDir = MainActivity.this.getFilesDir (); File myFileTxt = new File(filesDir, "myFile.txt"); try (BufferedWriter writer = new BufferedWriter(new FileWriter(myFileTxt))){ writer.write (" 这是内部存储文件目录的文件内容!"); } catch (IOException e) { e.printStackTrace (); } }
public void saveToCacheDir() { File cacheDir = MainActivity.this.getCacheDir (); File myCacheTxt = new File(cacheDir, "myCache.txt"); try (BufferedWriter writer = new BufferedWriter(new FileWriter(myCacheTxt))){ writer.write (" 这是内部存储缓存文件内容!"); } catch (IOException e) { e.printStackTrace (); } }
|
外部存储 ExternalStorage
ExternalStorage 是我们平时操作最多的,外部存储一般就是我们上面看到的 storage 或者 mnt 文件夹,不同厂家有可能不一样,请见下图:

一般来说,在 storage 文件夹中有一个 sdcard 文件夹,这个文件夹中的文件又分为两类,一类是公有目录,还有一类是私有目录,其中的公有目录有九大类,比如 DCIM、DOWNLOAD 等这种系统为我们创建的文件夹,私有目录就是 Android 这个文件夹,这个文件夹打开之后里边有一个 data 文件夹,打开这个 data 文件夹,里面有许多应用包名组成的文件夹,那些就是对应的应用程序的私有外部存储区域 (Android/data/ 应用包名)。比如浏览器的私有外部存储空间:

下面是一个存储与读取的例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:padding="20dp" android:orientation="vertical" tools:context=".MainActivity">
<EditText android:id="@+id/et_content" android:layout_width="match_parent" android:layout_height="wrap_content" android:minLines="10" />
<Button android:id="@+id/btn_save" android:onClick="save" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text=" 保存 & quot; />
<Button android:onClick="read" android:id="@+id/btn_read" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text=" 读取 & quot; />
<TextView android:id="@+id/tv_show" tools:text="Content Text!" android:layout_width="match_parent" android:layout_height="wrap_content"/> </LinearLayout>
|
MainActivity.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89
| public class MainActivity extends AppCompatActivity { private static final String TAG = "MainActivity"; private EditText etContent; private TextView tvShow;
private static final int REQUEST_EXTERNAL_STORAGE = 1001;
public static void verifyStoragePermissions(Activity activity) { int writePermission = ActivityCompat.checkSelfPermission (activity, Manifest.permission.WRITE_EXTERNAL_STORAGE); int readPermission = ActivityCompat.checkSelfPermission (activity, Manifest.permission.READ_EXTERNAL_STORAGE);
if (writePermission != PackageManager.PERMISSION_GRANTED || readPermission != PackageManager.PERMISSION_GRANTED) { ActivityCompat.requestPermissions ( activity, new String[]{Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE}, REQUEST_EXTERNAL_STORAGE ); } } @Override public void onRequestPermissionsResult(int requestCode, @NonNull String [] permissions, @NonNull int[] grantResults) { super.onRequestPermissionsResult (requestCode, permissions, grantResults); if(REQUEST_EXTERNAL_STORAGE == requestCode){ } }
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate (savedInstanceState); setContentView (R.layout.activity_main); File externalStorageDirectory = Environment.getExternalStorageDirectory (); Log.i (TAG, "onCreate: externalStorageDirectory = " + externalStorageDirectory.getAbsolutePath ()); etContent = findViewById (R.id.et_content); tvShow = findViewById (R.id.tv_show); verifyStoragePermissions (this); }
public void save(View view) { String content = etContent.getText ().toString (); if(TextUtils.isEmpty (content)) { Toast.makeText (MainActivity.this, " 输入为空 & quot;, Toast.LENGTH_SHORT).show (); return; }
if(Environment.MEDIA_MOUNTED.equals (Environment.getExternalStorageState ())){ String absolutePath = Environment.getExternalStorageDirectory ().getAbsolutePath (); String descPath = absolutePath + "/input_content.txt"; File descFile = new File(descPath); Log.i (TAG, "save: descFile = " + descFile.getAbsolutePath ()); try { BufferedWriter writer = new BufferedWriter(new FileWriter(descFile, true)); writer.write (content); writer.close (); Toast.makeText (MainActivity.this, " 写入成功 & quot;, Toast.LENGTH_SHORT).show (); } catch (IOException e) { e.printStackTrace (); } }else{ Toast.makeText (MainActivity.this, " 存储设备未挂载 & quot;, Toast.LENGTH_SHORT).show (); } }
public void read(View view) { String absolutePath = Environment.getExternalStorageDirectory ().getAbsolutePath (); String descPath = absolutePath + "/input_content.txt"; try { BufferedReader bufferedReader = new BufferedReader(new FileReader(new File(descPath))); String line; StringBuilder builder = new StringBuilder(); while((line = bufferedReader.readLine ()) != null){ builder.append (line).append ("\n"); } tvShow.setText (builder.toString ()); } catch (IOException e) { e.printStackTrace (); } } }
|
manifest 文件中别忘记声明权限:
1 2 3
| <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
|

获得外部存储目录
通过 Environment.getExternalStorageDirectory (),我们很容易获得外部存储的根目录,通过拼接,很容易拿到 DOWNLOAD、DCIM、MUSIC、MOVIES、LOST.DIR 等公有目录,如何拿到外部存储的私有目录呢?
Context.getExternalFilesDir (String type),通常用于需要长时间保存的数据,获取到 SDCard/Android/data/ 包名 /files/ 目录。
Context.getExternalCacheDir (),通常用于需要临时保存的数据,获取到 SDCard/Android/data/ 包名 /cache/ 目录。下面演示一下向私有空间和缓存空间分别存入一个文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| public void saveToPrivate(View view) { File externalPrivateDir = MainActivity.this.getExternalFilesDir (Environment.DIRECTORY_DOWNLOADS); File myDownloadTxt = new File(externalPrivateDir, "myDownload.txt"); try { BufferedWriter writer = new BufferedWriter(new FileWriter(myDownloadTxt)); writer.write (" 这是下载目录的文件内容!"); writer.close (); } catch (IOException e) { e.printStackTrace (); }
}
public void saveToCache(View view) { File externalPrivateDir = MainActivity.this.getExternalCacheDir (); File myCacheTxt = new File(externalPrivateDir, "myCache.txt"); try { BufferedWriter writer = new BufferedWriter(new FileWriter(myCacheTxt)); writer.write (" 这是缓存文件内容!"); writer.close (); } catch (IOException e) { e.printStackTrace (); } }
|

所以总结一下就是:要获取外部存储就公用空间就用 Environment,获取外部存储私有空间就用 Context 对象。
清除缓存与清除数据
清除缓存
反射调用接口:PackageManager.deleteApplicationCacheFiles
它会清除以下项目:
1、清除 data/data/ 应用包名 /cache/ 下的所有文件
2、清除 data/data/ 应用包名 /code_cache/ 下的所有文件
3、清除 mnt/sdcard/Android/data/ 应用包名 / 下的 cache 文件夹
清除数据
反射调用接口:ActivityManager.clearApplicationUserData
它会清除以下项目:
1、清除 data/data/ 应用包名 / 下的所有文件和文件夹
2、清除 mnt/sdcard/Android/data/ 应用包名
3、清除 mnt/sdcard/Android/media/ 应用包名
4、清除应用包名对应的 App 所有运行时权限的授权
总结一下内部存储与外部存储
