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 来获取规定好的公有目录文件夹进行数据存放。如无必要,可以不用申请读写存储的权限。
|