美文网首页
Android 数据存储详解(一):SharedPreferen

Android 数据存储详解(一):SharedPreferen

作者: BlueSocks | 来源:发表于2022-11-14 15:55 被阅读0次

    SharedPreferences

    SharedPreferences 存储的是xml文件,文件中是键值对的方式
    SharedPreferences 是轻量级存储类,存储在 "data/data/当前应用包名/shared_prefs/文件名.xml"
    SharedPreferences 是线程安全的,因为加了 synchronized
    apply 同步写回内存,然后把异步写回磁盘的任务放到一个单线程的队列中等待调度
    commit 和apply一样的写回操作,只是要等待异步任务返回,才返回
    apply/commit 是把全部数据进行一次写操作,所以单个文件不应该过大,如果实在需要很多内容,可以分为不同的文件

    1.获取 SharedPreferences

    getSharedPreferences("data_activity",Context.MODE_PRIVATE) // activity中使用
    applicationContext.getSharedPreferences("data_application",Context.MODE_PRIVATE)
    PreferenceManager.getDefaultSharedPreferences(context) // 过期了,不建议使用

    SharedPreferences getSharedPreferences(String name, int mode)
    name: 文件的名字
    mode:使用 MODE_PRIVATE 或者 MODE_APPEND 另外两个高版本不能使用 
    Context.MODE_PRIVATE: 指定该SharedPreferences数据只能被本应用程序读、写;
    Context.MODE_WORLD_READABLE:  指定该SharedPreferences数据能被其他应用程序读,但不能写;
    Context.MODE_WORLD_WRITEABLE:  指定该SharedPreferences数据能被其他应用程序读;
    Context.MODE_APPEND:该模式会检查文件是否存在,存在就往文件追加内容,否则就创建新文件;
    

    2.SharedPreferences基础使用

    val hashSet = HashSet<String>()
            hashSet.add("ss")
            hashSet.add("aa")
            hashSet.add("dd")
            // 1.获取/创建 SharedPreferences 对象
            val sp = getSharedPreferences("data_activity", Context.MODE_PRIVATE)
            // 2.获取/创建 Editor 对象
            val editor: Editor = sp.edit()
            btn_put.setOnClickListener {
                // 3.放入内容
                editor.putString("name", "lucky")
                editor.putInt("age", 22)
                editor.putFloat("weight", 60f)
                editor.putBoolean("isBig", false)
                editor.putLong("nn", 23L)
                editor.putStringSet("setkey", hashSet)
                // 4.提交 commit,推荐apply commit同步,apply异步,防止阻塞UI线程
                editor.apply()
            }
            
            下方 xml 文件内容
            
            btn_get.setOnClickListener {
                // 5. 获取值
                val name = sp.getString("name", "")
                Log.d("sp_sp", name)
            }
            btn_remove.setOnClickListener {
                // 6. 移除指定key的 项
                editor.remove("weight")
                editor.apply()
            }
            btn_clear.setOnClickListener {
                // 7. 清空数据
                editor.clear()
                editor.apply()
            }
            
            //注册 sp数据变化监听 增加,删除,更新 会回调,清空不会回调
             sp.registerOnSharedPreferenceChangeListener(listener)
             
             val listener = SharedPreferences.OnSharedPreferenceChangeListener { sharedPreferences, key ->
            Log.d("sp_sp", "sharedPreferences:" + sharedPreferences.all+"   key:"+key)
        }
            // 反注册
             sp.unregisterOnSharedPreferenceChangeListener(listener)
    
    添加内容后:
    <?xml version='1.0' encoding='utf-8' standalone='yes' ?>
    <map>
        <long name="nn" value="23" />
        <string name="name">lucky</string>
        <float name="weight" value="60.0" />
        <boolean name="isBig" value="false" />
        <int name="age" value="22" />
        <set name="setkey">
            <string>ss</string>
            <string>aa</string>
            <string>dd</string>
        </set>
    </map>
    移除内容后:
    <?xml version='1.0' encoding='utf-8' standalone='yes' ?>
    <map>
        <long name="nn" value="23" />
        <string name="name">lucky</string>
        <boolean name="isBig" value="false" />
        <int name="age" value="22" />
        <set name="setkey">
            <string>ss</string>
            <string>aa</string>
            <string>dd</string>
        </set>
    </map>
    清空内容后:
    <?xml version='1.0' encoding='utf-8' standalone='yes' ?>
    <map />
    

    3.SharedPreferences kotlin 进阶使用

    @Suppress("UNCHECKED_CAST")
    class SpHandler<T>(private val key: String = "", private val defaultValue: T) :
        ReadWriteProperty<Any, T> {
        init {
            Log.d("sp_sp", "初始化")
        }
        val sp: SharedPreferences by lazy {
            MyAppLication.context.getSharedPreferences("sp_sp", Context.MODE_PRIVATE)
        }
        fun clear(){
            sp.edit().apply {
                clear()
                apply()
            }
        }
        fun remove(key: String){
            sp.edit().apply{
                remove(key)
                apply()
            }
        }
        override fun getValue(thisRef: Any, property: KProperty<*>): T {
            val keyName = if (key.isEmpty()) property.name else key
            return with(sp) {
                when (defaultValue) {
                    is Long -> getLong(keyName, defaultValue) as T
                    is String -> getString(keyName, defaultValue) as T
                    is Float -> getFloat(keyName, defaultValue) as T
                    is Int -> getInt(keyName, defaultValue) as T
                    is Boolean -> getBoolean(keyName, defaultValue) as T
                    else -> throw IllegalArgumentException("没有对应的类型,请检查一下")
                }
            }
        }
        override fun setValue(thisRef: Any, property: KProperty<*>, value: T) {
            val keyName = if (key.isEmpty()) property.name else key
            with(sp.edit()) {
                when (value) {
                    is Long -> putLong(keyName, value)
                    is String -> putString(keyName, value)
                    is Float -> putFloat(keyName, value)
                    is Int -> putInt(keyName, value)
                    is Boolean -> putBoolean(keyName, value)
                    else -> throw IllegalArgumentException("没有对应的类型,请检查一下")
                }
                apply()
            }
        }
    }
    class SharedPreferencesActivity : AppCompatActivity() {
        lateinit var sp: SharedPreferences
        var name by SpHandler(defaultValue = "")
        var age by SpHandler(defaultValue = 10)
        override fun onCreate(savedInstanceState: Bundle?) {
        
          btn_good_add1.setOnClickListener {
                name = "hello" // 等效于 putString(key,value)
                // getString 时,直接使用 name 就表示获取的值
            }
            btn_good_add2.setOnClickListener {
                age = 111
            }
            btn_good_clear.setOnClickListener {
               SpHandler(defaultValue = "").clear()
            }
        
        }
    }
    

    内部存储
    内部存储是存储在Android文件系统的特殊位置 /data/user/0/应用的包名/files
    /data/user/0/应用的包名/files 是通过api获取的地址,其实际地址在文件目录中是 /data/data/应用的包名/files
    内部存储不需要权限

    1.查看可用空间

    通过调用 getFreeSpace() 或 getTotalSpace() 确定是否有足够的可用空间,而不引发 IOException
    在 activity中 可 filesDir.freeSpace 获取

    2.写入文件
    val file = File(filesDir,"fileDemo.txt") // 用于指定 创建文件的地址和名字
            // 1. 调用 openFileOutput() 以获取目录中的文件的 FileOutputStream
            openFileOutput(file.name, Context.MODE_PRIVATE).use {
                // 2. 写入内容
                it.write("写入内容".toByteArray())
            }
    
    3.读取文件内容
    // 1. 通过  openFileInput 获取 FileInputStream
            // 2. 通过 readBytes 读取数据
            val readBytes = openFileInput("fileDemo.txt").readBytes()
            // 3. 将bytes转为String
            val content = String(readBytes)
            Log.d("internal_s", content)
    
    4. 删除文件
    val file = File(filesDir, "fileDemo.txt")
            file.delete()
            // deleteFile("fileDemo.txt")
    
    5. 文件追加内容
    i++
            val file = File(filesDir,"fileDemo2.txt") // 用于指定 创建文件的地址和名字
            // 1. 调用 openFileOutput() 以获取目录中的文件的 FileOutputStream
            openFileOutput(file.name, Context.MODE_APPEND).use {
                // 2. 写入内容
                it.write("写入内容 ${i} ".toByteArray())
            }
            
            只需要 mode 改为  Context.MODE_APPEND 就是追加模式
    
    6. 文件夹中文件数
    val listFiles = File(filesDir.absolutePath).listFiles()
                Log.d("internal_s", "listFiles:${listFiles.size}")
    
    7. 内部存储读写另外一种通用方法
    写操作
    // 创建 文件 目录
    val parentFile = File(filesDir.path)
    parentFile.mkdirs()
    // 创建文件
    val file = File(parentFile,"/music.txt")
    if (!file.exists()) {
        file.createNewFile()
    }
    FileWriter(file).use {
        it.write("wwwwwewfxfsd\nsdfdsfsdfsdf")
        // it.append("xsadas")
    }
    读操作 
    val exists = file.exists()
    val isFile = file.isFile
    if (exists && isFile) {
                
    }
    val charArray = CharArray(1024)
    val stringBuffer = StringBuffer()
    FileReader(file).use {
        while (true) {
            val len = it.read(charArray)
            if (len < 0) {
                break
            }
            stringBuffer.append(String(charArray, 0, len))
        }
        Log.d("SD_CARD", stringBuffer.toString())
    }
    

    外部存储
    外部存储主要是用于 与其他应用共享文件,但是在7.0后,需要使用contentProvider
    外部存储通常就是我们认为的 sdcard目录下的文件存储
    外部存储 需要申请 权限 READ_EXTERNAL_STORAGE 或者 WRITE_EXTERNAL_STORAGE

    1.申请权限
    AndroidManifest.xml中 添加如下权限
        <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
        <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
    

    Android 6.0后 需要动态申请权限

    if (checkSelfPermission != PackageManager.PERMISSION_GRANTED) {
                val permissions = arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE)
                requestPermissions(permissions, 99)
            }
            
            
    override fun onRequestPermissionsResult(
            requestCode: Int,
            permissions: Array<out String>,
            grantResults: IntArray
        ) {
            super.onRequestPermissionsResult(requestCode, permissions, grantResults)
            if (requestCode == 99) {
                if (grantResults.isNotEmpty()
                    && grantResults[0] == PackageManager.PERMISSION_GRANTED
                ) {
                    Toast.makeText(this, "已经申请了读写权限", Toast.LENGTH_LONG).show()
                }
            }
        }
    
    2.使用公有外部存储

    app卸载后,不会被清理
    路径:/storage/emulated/0/自己命名的文件目录/自己命名的文件名字

    写操作
    // 1.判断 SDcard状态
    val state = Environment.getExternalStorageState()
     if (state == Environment.MEDIA_MOUNTED){
       
     }
    // 2. 获取/创建 外部文件夹 虽然这个方法过期了,但还是可以用
    val publicDir = Environment.getExternalStoragePublicDirectory("myfie")
    // 3. 创建 文件目录
    val parentFile = File(publicDir.path)
    parentFile.mkdirs()
    // 4. 创建文件
    val file = File(parentFile,"/222.txt")
    if (!file.exists()) {
        file.createNewFile()
    }
    // 5. 写内容到文件
    FileWriter(file).use {
        it.write("wwwwwewfxfsd\nsdfdsfsdfsdf")
        // it.append("xsadas")
    }
    读操作
    // 1. 判断文件是否存在,是否是文件类型
    val exists = file.exists()
    val isFile = file.isFile
    if (exists && isFile) {
                
    }
    // 2 .创建接收的 容器 
    val charArray = CharArray(1024) // 根据文件中的数据选择不同的容器 这里文件中是String
    val stringBuffer = StringBuffer() // 根据文件中的数据选择不同的容器 这里文件中是String
    // 3 .读文件
     FileReader(file).use {
        while (true) {
            val len = it.read(charArray)
            if (len < 0) {
                break
        }
        stringBuffer.append(String(charArray, 0, len))
    }
    Log.d("SD_CARD", stringBuffer.toString())
    
    3.使用私有外部存储

    app卸载,会跟随被清理
    路径:/storage/emulated/0/Android/data/包名/files/Music // Music: Environment.DIRECTORY_MUSIC

    // 获取/创建 外部文件夹 (私有文件夹)Environment.类型
    val music = getExternalFilesDir(Environment.DIRECTORY_MUSIC)
    // 其他和公有外部文件存储一样
    

    注意:
    不管内部存储还是外部存储都是文件存储,所以,在文件上的操作是相通的

    未完待续......

    来自:https://www.yuque.com/linxiaomo-wcgu0/wwowft/gukbhy

    相关文章

      网友评论

          本文标题:Android 数据存储详解(一):SharedPreferen

          本文链接:https://www.haomeiwen.com/subject/yhljxdtx.html