Android数据存储技术

一、文件存储

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:orientation="vertical"android:layout_width="match_parent"android:layout_height="match_parent" ><EditTextandroid:id="@+id/editText"android:layout_width="match_parent"android:layout_height="wrap_content"android:hint="Type something here"/>
</LinearLayout>
package com.jpc.filepersistencetestimport android.content.Context
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.EditText
import android.widget.Toast
import java.io.BufferedReader
import java.io.BufferedWriter
import java.io.InputStreamReader
import java.io.OutputStreamWriter
import java.lang.StringBuilderclass MainActivity : AppCompatActivity() {private lateinit var editText: EditTextoverride fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_main)editText = findViewById<EditText>(R.id.editText)val fileData = load()if (fileData.isNotEmpty()) {// 调用EditText的setText()方法将内容填充到EditText//里,并调用setSelection()方法将输入光标移动到文本的末尾位置以便继续输入editText.setText(fileData)editText.setSelection(fileData.length)Toast.makeText(this, "数据已恢复", Toast.LENGTH_SHORT).show()}}// 在销毁时保存数据到文件中
//    override fun onDestroy() {
//        super.onDestroy()
//        val inputText = editText.text.toString()
//        save(inputText)
//    }private fun save(inputText: String){// 保存数据到TXT文件中,文件名dataval output = openFileOutput("data", Context.MODE_PRIVATE)val writer = BufferedWriter(OutputStreamWriter(output))writer.use {it.write(inputText)}}// 从文件中加载数据private fun load():String {val builder = StringBuilder()val input = openFileInput("data")val reader = BufferedReader(InputStreamReader(input))reader.use {reader.forEachLine {builder.append(it)}}return builder.toString()}
}

二、SharedPreferences存储

不同于文件的存储方式,SharedPreferences是使用键值对的方式来存储数据的。当保存一条数据的时候,需要给这条数据提供一个对应的键,这样在读取数据的时候就可以通过这个键把相应的值取出来。SharedPreferences还支持多种不同的数据类型存储,如果存储的数据类型是整型,那么读取出来的数据也是整型的;如果存储的数据是一个字符串,那么读取出来的数据仍然是字符串。

<Buttonandroid:id="@+id/btn_save_SharedPreferences"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="保存数据"/>
// 指定SharedPreferences的文件名为data,并得到了//SharedPreferences.Editor对象。接着向这个对象中添加了3条不同类型的数据,最//后调用apply()方法进行提交,从而完成了数据存储的操作val btnSave: Button = findViewById<Button>(R.id.btn_save_SharedPreferences)btnSave.setOnClickListener{val editor = getSharedPreferences("data", Context.MODE_PRIVATE).edit()editor.putString("name", "Tom")editor.putInt("age", 28)editor.putBoolean("married", false)editor.apply()}
<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
<map><string name="name">Tom</string><boolean name="married" value="false" /><int name="age" value="28" />
</map>

SharedPreferences对象中提供了一系列的get方法,用于读取存储的数据,每种get方法都对应了SharedPreferences.Editor中的一种put方法,比如读取一个布尔型数据就使用getBoolean()方法,读取一个字符串就使用getString()方法。这些get方法都接收两个参数:第一个参数是键,传入存储数据时使用的键就可以得到相应的值了;第二个参数是默认值,即表示当传入的键找不到对应的值时会以什么样的默认值进行返回。

val btnRestore = findViewById<Button>(R.id.btn_restore_SharedPreferences)btnRestore.setOnClickListener{val prefs = getSharedPreferences("data", Context.MODE_PRIVATE)val name = prefs.getString("name", "")val age = prefs.getInt("age", 0)val married = prefs.getBoolean("married", false)Toast.makeText(this, "我的信息:${name}-${age}-${married}", Toast.LENGTH_SHORT).show()}
1、记住密码功能
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:orientation="vertical"android:layout_width="match_parent"android:layout_height="match_parent"><LinearLayoutandroid:orientation="horizontal"android:layout_width="match_parent"android:layout_height="wrap_content"><CheckBoxandroid:id="@+id/rememberPass"android:layout_width="wrap_content"android:layout_height="wrap_content" /><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:textSize="18sp"android:text="Remember password" /></LinearLayout><Buttonandroid:id="@+id/login"android:layout_width="match_parent"android:layout_height="60dp"android:text="Login" />
</LinearLayout>
class LoginActivity : BaseActivity() {override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_login)val prefs = getPreferences(Context.MODE_PRIVATE)
www.blogss.cnval isRemember = prefs.getBoolean("remember_password", false)if (isRemember) {// 将账号和密码都设置到文本框中val account = prefs.getString("account", "")val password = prefs.getString("password", "")accountEdit.setText(account)passwordEdit.setText(password)rememberPass.isChecked = true}login.setOnClickListener {val account = accountEdit.text.toString()val password = passwordEdit.text.toString()// 如果账号是admin且密码是123456,就认为登录成功if (account == "admin" && password == "123456") {val editor = prefs.edit()if (rememberPass.isChecked) { // 检查复选框是否被选中editor.putBoolean("remember_password", true)editor.putString("account", account)editor.putString("password", password)} else {// 如果没有被选中,就简单地调用一下clear()方法,
// 将SharedPreferences文件中的数据全部清除掉。editor.clear()}editor.apply()val intent = Intent(this, MainActivity::class.java)startActivity(intent)finish()} else {Toast.makeText(this, "account or password is invalid",Toast.LENGTH_SHORT).show()}}}
}

三、 SQLite数据库存储

显然,文件存储和SharedPreferences存储只适用于保存一些简单的数据和键值对。
但是,Android系统是内置了数据库的,SQLite是一款轻量级的关系型数据库,它的运算速度非常快,占用资源很少,通常只需要几百KB的内存就足够了,因而特别适合在移动设备上使用。SQLite不仅支持标准的SQL语法,还遵循了数据库的ACID事务。

1、创建数据库
package com.jpc.filepersistencetestimport android.content.Context
import android.database.sqlite.SQLiteDatabase
import android.database.sqlite.SQLiteOpenHelper
import android.widget.Toastclass MyDatabaseHelper(private val context: Context, name: String, version: Int):SQLiteOpenHelper(context, name, null, version) {// 建表语句private val createBook = "create table Book (" +"id integer primary key autoincrement," +"name text," +"author text," +"price real," +"pages integer)"override fun onCreate(db: SQLiteDatabase?) {db?.execSQL(createBook)Toast.makeText(context, "建表成功", Toast.LENGTH_SHORT).show()}override fun onUpgrade(db: SQLiteDatabase?, oldVersion: Int, newVersion: Int) {}
}
// 构建了一个MyDatabaseHelper对象,并且通过构造函数的参//数将数据库名指定为BookStore.db,版本号指定为1,然后在“Create Database”按钮的点击//事件里调用了getWritableDatabase()方法。val btnCreateDataBase = findViewById<Button>(R.id.btn_create_database)val databaseHelper = MyDatabaseHelper(this, "BookStore.db", 1)btnCreateDataBase.setOnClickListener{databaseHelper.writableDatabase}
2、升级数据库

SQLiteOpenHelper的构造方法里接收的第四个参数,它表示当前数据库的版本号,之前我们传入的是1,现在只要传入
一个比1大的数,就可以让onUpgrade()方法得到执行了。

package com.jpc.filepersistencetestimport android.content.Context
import android.database.sqlite.SQLiteDatabase
import android.database.sqlite.SQLiteOpenHelper
import android.widget.Toastclass MyDatabaseHelper(private val context: Context, name: String, version: Int):SQLiteOpenHelper(context, name, null, version) {// 建表语句private val createBook = "create table Book (" +"id integer primary key autoincrement," +"name text," +"author text," +"price real," +"pages integer)"private val createCategory = "create table Category (" +"id integer primary key autoincrement," +"category_name text," +"category_code integer)"override fun onCreate(db: SQLiteDatabase?) {db?.execSQL(createBook)db?.execSQL(createCategory)Toast.makeText(context, "建表成功", Toast.LENGTH_SHORT).show()}override fun onUpgrade(db: SQLiteDatabase?, oldVersion: Int, newVersion: Int) {// 先删除已经存在的表db?.execSQL("drop table if exists Book")db?.execSQL("drop table if exists Category")// 再重新创建onCreate(db)}
}
// 第一次创建数据库,版本version为1// val databaseHelper = MyDatabaseHelper(this, "BookStore.db", 1)// 升级数据库,将版本改为比上一次大的数值,就会调用onUpgrade方法val databaseHelper = MyDatabaseHelper(this, "BookStore.db", 2)btnCreateDataBase.setOnClickListener{databaseHelper.writableDatabase}
3、添加数据

调用SQLiteOpenHelper的getReadableDatabase()或
getWritableDatabase()方法是可以用于创建和升级数据库的,不仅如此,这两个方法还都会返回一个SQLiteDatabase对象,借助这个对象就可以对数据进行CRUD操作了。
SQLiteDatabase中提供了一个 insert() 方法,专门用于添加数据。它接收3个参数:第一个参数是表名,我们希望向哪张表里添加数据,这里就传入该表的名字;第二个参数用于在未指定添加数据的情况下给某些可为空的列自动赋值NULL,一般我们用不到这个功能,直接传入null即可;第三个参数是一个ContentValues对象,它提供了一系列的put()方法重载,用于向ContentValues中添加数据,只需要将表中的每个列名以及相应的待添加数据传入即可。

<Buttonandroid:id="@+id/btn_add_data"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="向表添加数据"/>
// 添加数据val btnAddData = findViewById<Button>(R.id.btn_add_data)btnAddData.setOnClickListener{val db = databaseHelper.writableDatabaseval data1 = ContentValues().apply {// 第一条数据// 不需要给id赋值,因为id自增put("name", "The Da Vinci Code")put("author", "Dan Brown")put("pages", 454)put("price", 16.96)}// 插入第一条数据db.insert("Book", null, data1)val values2 = ContentValues().apply {// 开始组装第二条数据put("name", "The Lost Symbol")put("author", "Dan Brown")put("pages", 510)put("price", 19.95)}db.insert("Book", null, values2) // 插入第二条数据}
4、修改数据

SQLiteDatabase中提供了一个非常好用的update()方法,用于对数据进行更新。这个方法接收4个参数:第一个参数和insert()方法一样,也是表名,指定更新哪张表里的数据;第二个参数是ContentValues对象,要把更新数据在这里组装进去;第三、第四个参数用于约束更新某一行或某几行中的数据,不指定的话默认会更新所有行。

// 修改数据val btnUpdate = findViewById<Button>(R.id.btn_update_data)btnUpdate.setOnClickListener{val db = databaseHelper.writableDatabaseval contentValues = ContentValues()contentValues.put("price", 10.99)// 第三、第四个参数来指定具体更新//哪几行。第三个参数对应的是SQL语句的where部分,表示更新所有name等于?的行,而?是一//个占位符,可以通过第四个参数提供的一个字符串数组为第三个参数中的每个占位符指定相应//的内容,arrayOf()方法是Kotlin提供的一种用于便捷创建数组的内置方法db.update("Book", contentValues, "name = ?", arrayOf("The Da Vinci Code"))}
5、删除数据

SQLiteDatabase中提供了一个delete()方法,专门用于删除数据。这个方法接收3个参数:第一个参数仍然是表名,这个没什么好说的;第二、第三个参数用于约束删除某一行或某几行的数据,不指定的话默认会删除所有行。

// 删除数据val btnDelete = findViewById<Button>(R.id.btn_delete_data)btnDelete.setOnClickListener{val db = databaseHelper.writableDatabase// 指定删除条件,即where字句,如果不指定就会删除表中所有的数据db.delete("Book", "pages > ?", arrayOf("500"))}
6、查询数据

SQLiteDatabase中还提供了一个query()方法用于对数据进行查询。这个方法的参数非常复杂,最短的一个方法重载也需要传入7个参数。那我们就先来看一下这7个参数各自的含义吧。第一个参数不用说,当然还是表名,表示我们希望从哪张表中查询数据。
第二个参数用于指定去查询哪几列,如果不指定则默认查询所有列。第三、第四个参数用于约束查询某一行或某几行的数据,不指定则默认查询所有行的数据。第五个参数用于指定需要去group by的列,不指定则表示不对查询结果进行group by操作。第六个参数用于对group by之后的数据进行进一步的过滤,不指定则表示不进行过滤。第七个参数用于指定查询结果的排序方式,不指定则表示使用默认的排序方式。调用query()方法后会返回一个Cursor对象,查询到的所有数据都将从这个对象中取出。
在这里插入图片描述

        // 查询数据val btnQuery = findViewById<Button>(R.id.btn_query_data)btnQuery.setOnClickListener{val db = databaseHelper.writableDatabase// 查询Book表中的所有数据val cursor = db.query("Book", null, null, null, null, null, null)if (cursor.moveToFirst()) {do {// 遍历Cursor对象,取出数据并打印// 要检查参数,不然报错var index1 = if (cursor.getColumnIndex("name") >= 0) cursor.getColumnIndex("name") else 0val name = cursor.getString(index1)index1 = if (cursor.getColumnIndex("author") >= 0) cursor.getColumnIndex("author") else 0val author = cursor.getString(index1)index1 = if (cursor.getColumnIndex("pages") >= 0) cursor.getColumnIndex("pages") else 0val pages = cursor.getInt(index1)index1 = if (cursor.getColumnIndex("price") >= 0) cursor.getColumnIndex("price") else 0val price = cursor.getDouble(index1)Toast.makeText(this, "查询到数据:${name}-${author}-${pages}-${price}", Toast.LENGTH_SHORT).show()}while (cursor.moveToNext())}// 需要关闭Cursor对象cursor.close()}
7、使用SQL语句操作数据库
db.execSQL("insert into Book (name, author, pages, price) values(?, ?, ?, ?)",arrayOf("The Da Vinci Code", "Dan Brown", "454", "16.96")
)
db.execSQL("insert into Book (name, author, pages, price) values(?, ?, ?, ?)",arrayOf("The Lost Symbol", "Dan Brown", "510", "19.95")
)db.execSQL("update Book set price = ? where name = ?", arrayOf("10.99", "The Da Vinci Code"))db.execSQL("delete from Book where pages > ?", arrayOf("500"))val cursor = db.rawQuery("select * from Book", null)
8、使用事务
class MainActivity : AppCompatActivity() {override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_main)val dbHelper = MyDatabaseHelper(this, "BookStore.db", 2)...replaceData.setOnClickListener {val db = dbHelper.writableDatabasedb.beginTransaction() // 开启事务try {db.delete("Book", null, null)
if (true) {// 手动抛出一个异常,让事务失败throw NullPointerException()}val values = ContentValues().apply {put("name", "Game of Thrones")put("author", "George Martin")put("pages", 720)put("price", 20.85)}db.insert("Book", null, values)db.setTransactionSuccessful() // 事务已经执行成功} catch (e: Exception) {e.printStackTrace()} finally {db.endTransaction() // 结束事务}}}
}
9、升级数据库的最佳写法
class MyDatabaseHelper(val context: Context, name: String, version: Int):SQLiteOpenHelper(context, name, null, version) {private val createBook = "create table Book (" +" id integer primary key autoincrement," +"author text," +"price real," +"pages integer," +"name text," +"category_id integer)"private val createCategory = "create table Category (" +"id integer primary key autoincrement," +"category_name text," +"category_code integer)"override fun onCreate(db: SQLiteDatabase) {db.execSQL(createBook)db.execSQL(createCategory)}override fun onUpgrade(db: SQLiteDatabase, oldVersion: Int, newVersion: Int) {if (oldVersion <= 1) {db.execSQL(createCategory)}if (oldVersion <= 2) {db.execSQL("alter table Book add column category_id integer")}}
}

可以看到,首先我们在Book表的建表语句中添加了一个category_id列,这样当用户直接安装第3版的程序时,这个新增的列就已经自动添加成功了。然而,如果用户之前已经安装了某一版本的程序,现在需要覆盖安装,就会进入升级数据库的操作中。在onUpgrade()方法里,我们添加了一个新的条件,如果当前数据库的版本号是2,就会执行alter命令,为Book表新增一个category_id列。这里请注意一个非常重要的细节:每当升级一个数据库版本的时候,onUpgrade()方法里都一定要写一个相应的if判断语句。为什么要这么做呢?这是为了保证App在跨版本升级的时候,每一次的数据库修改都能被全部执行。比如用户当前是从第2版升级到第3版,那么只有第二条判断语句会执行,而如果用户是直接从第1版升级到第3版,那么两条判断语句都会执行。使用这种方式来维护数据库的升级,不管版本怎样更新,都可以保证数据库的表结构是最新的,而且表中的数据完全不会丢失。

10、简化写法
// 简化后的写法getSharedPreferences("data", Context.MODE_PRIVATE).edit{putString("name", "Tom")putInt("age", 28)putBoolean("married", false)}
// 使用更新的函数val values = contentValuesOf("name" to "Game of Thrones", "author" to "George Martin","pages" to 720, "price" to 20.85)db.insert("Book", null, values)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/298608.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

52岁TVB前绿叶退隐8年转做司仪晒流利英文。

现年52岁的陈霁平&#xff08;Maria&#xff09;在1995年参选港姐后加入TVB&#xff0c;离巢后转型做专业司仪&#xff0c;精通多国语言的她更成为司仪界的抢手货。 日前陈霁平分享了担任活动主持的近照&#xff0c;身穿高衩晚装的她身形Fit爆&#xff0c;皮肤依然白滑紧致&…

ChatGPT 的核心 GPT 模型:探究其生成式预训练变换架构的革新与应用潜力

GPT&#xff08;Generative Pre-trained Transformer&#xff09;模型是一种深度学习模型&#xff0c;由OpenAI于2018年首次提出&#xff0c;并在随后的几年中不断迭代发展&#xff0c;包括GPT-2、GPT-3以及最新的GPT-4。GPT模型在自然语言处理&#xff08;NLP&#xff09;领域…

Zookeeper学习一

初识 Zookeeper Zookeeper 是 Apache Hadoop 项目下的一个子项目&#xff0c;是一个树形目录服务&#xff08;B树&#xff09;。 Zookeeper 翻译过来就是 动物园管理员&#xff0c;他是用来管 Hadoop&#xff08;大象&#xff09;、Hive(蜜蜂)、Pig(小 猪)的管理员。简称zk …

海康摄像头插件嵌入iframe时视频播放插件位置问题

参考&#xff1a;https://juejin.cn/post/6857670423971758094 原因&#xff1a;没有按照iframe相对位置计算视频插件位置。 解决&#xff1a; $(window).on(resize, resize);function resize(){// 解决iframe中嵌入海康插件初始化问题:// 1. 获取iframe相比于窗口的偏移量;c…

解决JavaWeb中IDEA2023新版本无法创建Servlet的问题

出现问题&#xff1a;IDEA右键创建Servlet时&#xff0c;找不到选项 原因分析&#xff1a;IDEA的2023版的已经不支持Servlet了&#xff0c;如果还要使用的话&#xff0c;需要自己创建模板使用 创建模板 右击设置&#xff0c;选择&#xff08;File and Code Templates&#x…

电脑上音频太多,播放速度又不一致,如何批量调节音频播放速度?

批量调节音频速度是现代音频处理中的一个重要环节&#xff0c;尤其在音乐制作、电影剪辑、有声书制作等领域&#xff0c;它能够帮助制作者快速高效地调整音频的播放速度&#xff0c;从而满足特定的制作需求。本文将详细介绍批量调节音频速度的方法、技巧和注意事项&#xff0c;…

Redis各个方面入门详解

目录 一、Redis介绍 二、分布式缓存常见的技术选型方案 三、Redis 和 Memcached 的区别和共同点 四、缓存数据的处理流程 五、Redis作为缓存的好处 六、Redis 常见数据结构以及使用场景 七、Redis单线程模型 八、Redis 给缓存数据设置过期时间 九、Redis判断数据过期的…

华大单片机新建工程步骤

1.新建文件夹&#xff0c;比如00_LED 2.拷贝 hc32f460_ddl_Rev2.2.0\driver 到 00_LED 3.拷贝 hc32f460_ddl_Rev2.2.0\mcu\common 到 00_LED 4.拷贝 hc32f460_ddl_Rev2.2.0\example\ev_hc32f460_lqfp100_v2\gpio\gpio_output\source 到 00_LED 5.拷贝 hc32f460_ddl_Rev2.2.…

解决Quartus与modelsim联合仿真问题:# Error loading design解决,是tb文件中没加:`timescale 1ns/1ns

解决Quartus与modelsim联合仿真问题&#xff1a;# Error loading design解决&#xff0c;是tb文件中没加&#xff1a;timescale 1&#xff0c;一直走下来&#xff0c;在modelsim中出现了下面问题2&#xff0c;rtl文件、tb文件2.1&#xff0c;rtl代码2.2&#xff0c;tb测试2.3&a…

软件杯 深度学习乳腺癌分类

文章目录 1 前言2 前言3 数据集3.1 良性样本3.2 病变样本 4 开发环境5 代码实现5.1 实现流程5.2 部分代码实现5.2.1 导入库5.2.2 图像加载5.2.3 标记5.2.4 分组5.2.5 构建模型训练 6 分析指标6.1 精度&#xff0c;召回率和F1度量6.2 混淆矩阵 7 结果和结论8 最后 1 前言 &…

使用 Clickhouse 集成的表引擎同步数据方式详解

Clickhouse作为一个列式存储分析型数据库&#xff0c;提供了很多集成其他组件的表引擎数据同步方案。 官网介绍 一 Kafka 表引擎 使用Clickhouse集成的Kafka表引擎消费Kafka写入Clickhouse表中。 1.1 流程图 1.2 建表 根据上面的流程图需要建立三张表&#xff0c;分别Click…

算法设计与分析实验报告java实现(排序算法、三壶谜题、交替放置的碟子、带锁的门)

一、 实验目的 1&#xff0e;加深学生对算法设计方法的基本思想、基本步骤、基本方法的理解与掌握&#xff1b; 2&#xff0e;提高学生利用课堂所学知识解决实际问题的能力&#xff1b; 3&#xff0e;提高学生综合应用所学知识解决实际问题的能力。 二、实验任务 1、排序算法…

clickhouse sql使用2

1、多条件选择 multiIf(cond_1, then_1, cond_2, then_2, …, else) select multiIf(true,0,1) 当第一条件不成立看第二条件判断 第一个参数条件参数&#xff0c;第二参数条件成立时走 2、clickhouse 在计算时候长出现NaN和Infinity异常处理 isNaN()和isInfinite()处理

设置Chrome打开链接在新标签页显示

Chrome版本 版本 123.0.6312.106&#xff08;正式版本&#xff09; &#xff08;64 位&#xff09; 下面这两个页面都有设置按钮&#xff1a; https://www.google.com/?pli1或者https://www.google.com/?hlzh-CN 要先退出账号&#xff0c;要不然看不到右下角的 “设置” 。…

LNMP环境:揭秘负载均衡与高可用性设计

lb1: 192.168.8.5 lb2: 192.168.8.6 web1:192.168.8.7 web2:192.168.8.8 php-fpm: 192.168.8.9 mysql: 192.168.8.10 nfs:192.168.8.11 分别插入镜像 8.5-8.8 分别安装nginx,并设置启动 8.9 安装php 8.10 安装mysql 先配置一台web服务器然后同步 设置网站根目录 cp -…

微信小程序短链接工具推荐

现在微信小程序大行其道&#xff0c;但工作中大部分人选择了短链接的方式来推广微信小程序&#xff0c;那么微信小程序短链接工具哪个好?今天就分享一篇从网上看到的关于《微信小程序短链接工具推荐》文&#xff0c;作者是souki&#xff0c;一起来看看吧! 一、缩链 1、生成方…

Kubernetes(k8s):部署、使用 metrics-server

Kubernetes&#xff08;k8s&#xff09;&#xff1a;部署、使用 metrics-server 一、metrics-server简介二、部署metrics-server2.1、 下载 Metrics Server 部署文件2.2、修改metrics-server.yaml 文件2.3、 部署 Metrics Server2.4、 检查 Metrics Server 三、使用 Metrics Se…

mac 切换 jdk

查看 mac 上都有哪些版本 /usr/libexec/java_home -V看准版本切换 按前缀切换 比如 export JAVA_HOME/usr/libexec/java_home -v 1.8这样会随机一个 1.8 的 如果想再确定一个比如 openjdk export JAVA_HOME/usr/libexec/java_home -v 1.8.0_292这个方式是临时的&#xff0c…

基于PID-UKF/AUKF锂电池SOC估计

&#xff08;1&#xff09;对UKF进行改进&#xff0c;引入PID控制器 参考文献&#xff1a;https://doi.org/10.1155/2021/6665509 模型&#xff1a;Thevenin模型 电池类型&#xff1a;钴酸锂 工况&#xff1a;DST工况和FUDS工况 MATLAB版本&#xff1a;R2021b 在这篇参考文献…

vulhub中Apache Solr RemoteStreaming 文件读取与SSRF漏洞复现

Apache Solr 是一个开源的搜索服务器。在Apache Solr未开启认证的情况下&#xff0c;攻击者可直接构造特定请求开启特定配置&#xff0c;并最终造成SSRF或任意文件读取。 访问http://your-ip:8983即可查看Apache Solr后台 1.访问http://your-ip:8983/solr/admin/cores?indexI…