Android 系统的存储空间有内外之分,内部的存储空间只能够被当前应用所访问,用户和其他应用都不行。外部存储则可以被用户通过自带的文件管理器可以查看。 
 
内部存储 
 
 内部存储是 Android 系统内部的存储空间,由 Android 系统管理,普通用户无法查看该存储空间内的文件。 下面介绍常用的 3 种读写内部存储空间的方法 
 
 SharedPreference 
 
 SharedPreferences 是 Android 平台上一个轻量级的存储类,主要是保存一些常用的配置, 它提供了 Android 平台常规的 Long长整形、Int整形、String字符串型的保存。 
 它所保存的文件是以 xml 的格式存储在 /data/data//sharedprefs 目录下。使用方式如下 
// test 为 xml 文件的名字,第二个参数为模式 
val share = getSharedPreferences("test", Context.MODE_PRIVATE) 
    share.edit() 
        .putBoolean("testBoolean", true) 
        .apply() 
 
按照上述代码,将会生成 /data/data//sharedprefs/test.xml 文件,文件内容如下 
 
![]()  
 
 
可以看出,在代码中设定的 testBoolean 对应值 true 都被保存下来了。 
 
 
SQLite 
SQLite 为 Android 所提供的数据库工具,能够以数据库的形式存储数据,使用方法如下 
 
 
class MySQLite(mContext:Context?,name:String?,factory: SQLiteDatabase.CursorFactory?,version:Int): SQLiteOpenHelper(mContext,name,factory, version) { 
     override fun onCreate(db: SQLiteDatabase?) { 
    } 
     override fun onUpgrade(db: SQLiteDatabase?, oldVersion: Int, newVersion: Int) { 
    } 
} 
 
 
首先得继承 SQLiteOpenHelper 这个类,继承后需要实现 onCreate 和 onUpgrade 方法。在使用时,直接创建该类即可。 
 
val test = MySQLite(this,"test.db",null,1) 
 
在第一次创建时,会进入 onCreate 方法,可以执行数据库表创建的工作,传入的文件名 "test.db" 也会被创建。 
需要注意的是,在之后的使用时,传入的 version 如果和第一次使用的 version 不同,就会执行 onUpgrade 方法。在这个方法中,可以对数据表进行操作。 
最终 db 类如下 
 
class MySQLite(mContext:Context?,name:String?,factory: SQLiteDatabase.CursorFactory?,version:Int): SQLiteOpenHelper(mContext,name,factory, version) { 
    val CREATE_TEST = ("create table test ( " 
            + " id integer primary key autoincrement," 
            + " name text)") 
    override fun onCreate(db: SQLiteDatabase?) { 
        db?.execSQL(CREATE_TEST) 
    } 
    override fun onUpgrade(db: SQLiteDatabase?, oldVersion: Int, newVersion: Int) { 
        db?.execSQL("drop table if exists test") 
        onCreate(db) 
    } 
} 
 
但此时,在手机的内部存储中,还是没有看到 test.db 的文件了,那是因为在实际使用前,都不会去执行 onCreate 的方法。 
 
实际使用 
先在 MySQLite 类中添加新增和查询数据的方法 
 
fun search(){ 
    // 查询数据 
    val queryData = writableDatabase.query("test",null,null,null,null,null,null,null) 
    if(queryData.moveToFirst()){ 
        // 遍历数据 
        do{ 
            val name = queryData.getString(queryData.getColumnIndex("name")) 
            val id = queryData.getString(queryData.getColumnIndex("id")) 
            println(name) 
            println(id) 
        }while(queryData.moveToNext()) 
    } 
    queryData.close() 
} 
fun add(data: ContentValues){ 
    // 添加数据 
    writableDatabase.insert("test",null,data) 
} 
 
在 MainActivity 中调用 
 
fun test(){ 
val helper = MySQLite(this,"test.db",null,1) 
        val data = ContentValues() 
        data.put("name","Test") 
        helper.add(data) 
        helper.search() 
} 
 
在调用该方法后,就可以在手机的内部存储中看到 test.db 文件,并且此时控制台输出了查询的结果。 
 
![]()  
 
![]()  
 
 
文件 
Android 系统中提供了 openFileOutput 和 openFileInput 方法来读写内部存储空间的文件。 
 
吸入的文件默认存储在内部存储空间的 /data/data//files 目录中,可以通过 Android Studio 来查看这些文件。 
 
openFileOutput 方法是写入数据,接受的第一个参数时文件名,如果不存在则先新建一个。第二个参数则是写入模式,现在只接受 Context.MODE_PRIVATE 和 Context.MODE_APPEND。 
 
前者表示每次写入都会覆盖当前内容,后者则是在当前基础上添加数据。使用如下: 
 
val file = openFileOutput("a.txt", Context.MODE_PRIVATE) 
file.write("123456".toByteArray()) 
 
在 Android Studio 中可以查看到该文件 
![]()  
 
![]()  
 
 
openFileInput 则是读取文件,使用如下 
 
val data = openFileInput("a.txt").bufferedReader().readLine() 
 
println(data) 
 
 
控制台输出为 123456 
 
 
外部存储 
考虑到普通用户无法查看到内部存储的文件的问题,提供了外部存储的方式来存放数据。 
  
应用私有目录 
应用私有目录是谷歌提供给 APP 在外部存储上的目录,其目录为 Android/data//。在访问该目录时, app 不需要申请读写存储的权限也可以直接创建文件。( 为应用的包名) 
 
私有目录在应用被卸载时,也会被清理掉,所以不应该存放与应用无关的数据。例如用户所保存的图片,文件,否则会导致应用删除时,用户保存的数据也随之丢失。 
 
所以对于这一类在 APP 卸载时仍要保留的数据,需要存放到应用公共目录。 
常用的访问私有目录的 API 有两个 
- getCacheDir()
 - getExternalFilesDir(type:String)
 
  第一个方法获取到的路径为 Android/data//cache,是存放缓存文件的路径。 
 
第二个方法则是接收一个字符串,使用 Environment 中提供的字符串变量,指向对应的私有目录。例如 
 
val path = getExternalFilesDir(Environment.MEDIA_SHARED) 
println(path) 
 
 
控制台会输出 /storage/emulated/0/Android/data//files/shared 
 
在获取到路径后,可以创建新的文件,并对其进行读写。 
  
fun file(){ 
    // 获取私有目录路径 
    val path =  getExternalFilesDir(Environment.MEDIA_SHARED) 
    // 要新增的文件 
    val add = "/test.txt" 
    // 创建 File 类 
    val file = File(path?.path + add) 
    // 判断是否是文件 
    if (file.isFile){ 
        println(file.readText()) 
    }else{ 
        // 是否创建失败 
        if (!file.createNewFile()) { 
            println("创建文件:$file 失败") 
        }else{ 
        // 创建成功则写入数据 
            file.writeText("123456") 
        } 
    } 
} 
 
在第一次打开 APP 时,会执行 file 函数并且会创建 test.txt 文件,在创建成功后,会在该文件写入 123456 。 
 
此时关闭 APP 再次打开,一样会执行 file 函数,但因为文件已经创建,所以会直接读取该文件。控制台输出为 123456。 
 
 
在文件管理工具中也可以查看到新增的文件。 
 
![]()  
 
![]()  
 
 
应用公共目录 
 
相对于私有目录,公共目录需要申请读写存储的权限,并且该目录下的文件也可以被其它具有读写存储权限的 APP 访问。使用方法如下 
读写权限 
对于公共目录,要先进行权限申请。在 AndroidManifest.xml 文件中进行权限申请。 
 
 
  
  
  
除此之外,由于读写存储权限是危险权限,在 Android 6.0 以上的系统中,需要动态的进行申请。 
 
/** 
 * 动态申请权限 
 */ 
private fun requestPermission() { 
    // 检查是否拥有,有就直接用,没有就申请 
    if (permissionCheck == PackageManager.PERMISSION_DENIED) { 
        ActivityCompat.requestPermissions(this, arrayOf(WRITE_EXTERNAL_STORAGE,READ_EXTERNAL_STORAGE), 1000) 
    } else { 
        // 已经有权限,无需申请 
    } 
} 
/** 
 * 权限申请结果 
 */ 
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array, grantResults: IntArray) { 
    when (requestCode) { 
        1000 -> { 
            // 判断申请结果 
        } 
    } 
} 
 
路径获取 
如果的到用户的同意,就具有对外部存储进行读写的权限了。获取外部存储目录的方法有两个 
 
    1. Environment.getExternalStorageDirectory() 
    2. Environment.getExternalStoragePublicDirectory(type:String) 
其中第一个方法不接受参数,直接指向外部存储的根目录 /storage/emulated/0/,第二个方法接受一个字符串,使用 Environment 中提供的字符串变量,指向对应的公共目录。例如 
 
 
val path = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES) 
println(path) 
 
控制台会输出 /storage/emulated/0/Pictures ,指向的是公共目录 Picture。 
 
 
在获取到目录后并且申请了权限,则新建文件,写入文件,读取文件的方法都和使用私有目录时一样。 
 
 
使用注意 
- 由于目录是外部存储,也就是说用户可以随意删除更改,所以在代码编写时,需要对目录进行空判定,避免在读写被用户删除的文件时报错。
 - 假如 APP 具有读写存储的权限,为了更好的用户体验,应该避免随意在用户的外部存储上随意创建文件,导致目录混乱;应按照 Android 提供的 API 来获取规定好的公有目录文件夹进行数据存放。如无必要,可以不用申请读写存储的权限。
 
  
 |