//写入数据
private fun saveFile() {
//将文件写入内部存储空间时,只能在本应用的目录中写入,不能写入其他应用的目录中
val file = File("data/data/com.wzh.a01_filestore/innerFile.txt")
BufferedWriter(FileWriter(file)).use {
it.write("你好啊")
}
}
//读取文件数据
private fun readFile() {
val file = File("data/data/com.wzh.a01_filestore/innerFile.txt")
val sb = StringBuilder()
BufferedReader(FileReader(file)).useLines {
it.iterator().apply {
while (this.hasNext()){
sb.append(this.next())
}
}
}
textView.text = sb.toString()
}
获取files文件夹路径context.getFilesDir()
cache文件路径context.getCacheDir()
系统管理应用界面的清除缓存,会清除cache文件夹下的东西,清除数据,会清除整个包名目录下的东西
2.2之前:sdcard
2.2~4.2:mnt/sdcard
4.3开始:storage/sdcard
val file = File("sdcard/info.txt")
BufferedWriter(FileWriter(file)).use {
it.write("你好啊")
}
//需要申请权限,6.0后需要动态申请权限
Environment.getExternalStorageDirectory() 这个方法一定能获取到sdcard的路径的
Environment.getExternalStorageState() sd卡状态。
常见的状态:
MEDIA_MOUNTED:sd卡可用
MEDIA_UNMOUNTED:sd卡存在,但是没有挂载
MEDIA_REMOVED:sd卡不存在
MEDIA_CHECKING:sd卡正在遍历
if (Environment.getExternalStorageState() == Environment.MEDIA_MOUNTED) {}
val path = Environment.getExternalStorageDirectory().absolutePath
//构造一个新的StatF来查看path处的文件系统的统计信息
val statFs = StatFs(path)
//获取总区块
val blockCount = statFs.blockCountLong
//每个区块的大小
val blockSizeLong = statFs.blockSizeLong
//可用空间的总区块
val availableBlocksLong = statFs.availableBlocksLong
val text = Formatter.formatFileSize(this, blockCount * blockSizeLong)
btn_total_space.text = text
- 第二组rwx:与文件拥有者同一用户组的用户(grouper)
- 第三组rwx:其他用户(other)的权限
context.openFileOutput(“文件名”,模式),相当于data/data/包名/files
创建其他用户可读写的模式,是非常危险的,google已经把后两个废弃了
val fos = openFileOutput("info.txt", Context.MODE_WORLD_READABLE or Context.MODE_WORLD_WRITEABLE)
fos.write("哈哈".toByteArray())
fos.close()
//高版本已经废弃
//Caused by: java.lang.SecurityException: MODE_WORLD_READABLE no longer supported
private fun writeXml(){
val xs = Xml.newSerializer()
//设置xml的输出
xs.setOutput(openFileOutput("sms.xml",Context.MODE_PRIVATE),"UTF-8")
//生成头节点
//encoding 编码属性
//standalone 是否需要别的文件约束,true表示独立的,不需要约束
xs.startDocument("utf-8",true)
//生成开始标签
xs.startTag(null,"smss")
val list = listOf(
Sms("haha", "123", 1),
Sms("haha2", "12345", 2),
Sms("haha3", "123123123", 1)
)
list.forEach {sms->
xs.startTag(null,"sms")
xs.startTag(null,"body")
xs.text(sms.body)
xs.endTag(null,"body")
xs.startTag(null,"number")
xs.text(sms.number)
xs.endTag(null,"number")
xs.startTag(null,"type")
xs.text(sms.type.toString())
xs.endTag(null,"type")
xs.endTag(null,"sms")
}
xs.endTag(null,"smss")
//生成尾节点
xs.endDocument()
}
private lateinit var list: MutableList<Sms>
private fun readXml(): String {
val stream = openFileInput("sms.xml")
//获取xmlpull解析器,读取一行,解析一行
val xp = Xml.newPullParser()
//设置输入
xp.setInput(stream, "utf-8")
//获取当前节点事件类型
var eventType = xp.eventType
var sms: Sms? = null
//如果不是结束节点,遍历获取
while (eventType != XmlPullParser.END_DOCUMENT) {
when (eventType) {
//开始节点
XmlPullParser.START_TAG -> {
//当前节点的名称
when (xp.name) {
"smss" -> {
list = mutableListOf()
}
"sms" -> {
sms = Sms()
}
"body" -> {
//获取文本节点
sms?.body = xp.nextText()
}
"number" -> {
sms?.number = xp.nextText()
}
"type" -> {
sms?.type = xp.nextText().toInt()
}
}
}
//结束节点
XmlPullParser.END_TAG -> {
sms?.let {
if (xp.name == "sms") {
list.add(it)
}
}
}
}
//把指针移动到下个节点,并返回该节点的事件类型
eventType = xp.next()
}
val sb = StringBuilder()
list.forEach {
sb.append(it)
}
return sb.toString()
}
private fun writeJson(){
val list = listOf(
Sms("haha", "123", 1),
Sms("haha2", "12345", 2),
Sms("haha3", "123123123", 1)
)
val ja = JSONArray()
list.forEach {
val jo = JSONObject()
jo.put("body",it.body)
jo.put("number",it.number)
jo.put("type",it.type)
ja.put(jo)
}
openFileOutput("sms.json",Context.MODE_PRIVATE).use {
it.write(ja.toString().toByteArray())
}
}
private fun readJson():String{
val sb = StringBuilder()
openFileInput("sms.json").use {
BufferedReader(InputStreamReader(it)).useLines {
val iterator = it.iterator()
while (iterator.hasNext()){
sb.append(iterator.next())
}
}
}
return sb.toString()
}
class MyOpenHelper(context: Context?, name: String?) : SQLiteOpenHelper(context, name, null, 1) {
override fun onCreate(db: SQLiteDatabase?) {
}
override fun onUpgrade(db: SQLiteDatabase?, oldVersion: Int, newVersion: Int) {
}
}
val openHelper = MyOpenHelper(appContext, "my.db")
//创建数据库,如果数据库不存在时,先创建,再打开,如果存在就直接打开
val sqliteDb = openHelper.writableDatabase//可读可写的
//getWritableDatabase():打开可读写的数据库
//getReadableDatabase():在磁盘空间不足时打开只读数据库,否则打开可读写数据库
在sqlite中的oncreate方法中执行sql语句,在sqlite中,数据类型不是很重要,不管定义什么类型,在底层都会使用char接收
db?.execSQL("CREATE TABLE person(_id integer primary key autoincrement,name char(10),phone char(20))")
@Test
fun insert() {
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
assertEquals("com.wzh.a01_filestore", appContext.packageName)
val helper = MyOpenHelper(appContext, "my.db")
val db = helper.readableDatabase
db.execSQL("INSERT INTO person(name,phone) VALUES(?,?)", arrayOf("张三", "123132131435"))
db.close()
}
@Test
fun delete() {
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
assertEquals("com.wzh.a01_filestore", appContext.packageName)
val helper = MyOpenHelper(appContext, "my.db")
val db = helper.readableDatabase
db.execSQL("DELETE FROM person where _id = ?", arrayOf("1"))
db.close()
}
@Test
fun update() {
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
assertEquals("com.wzh.a01_filestore", appContext.packageName)
val helper = MyOpenHelper(appContext, "my.db")
val db = helper.readableDatabase
db.execSQL("UPDATE person SET phone = ? WHERE _id = ?", arrayOf("8966878", 2))
db.close()
}
@Test
fun select() {
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
assertEquals("com.wzh.a01_filestore", appContext.packageName)
val helper = MyOpenHelper(appContext, "my.db")
val db = helper.readableDatabase
val cursor = db.rawQuery("SELECT phone FROM person", null)
cursor?.apply {
val list = mutableListOf<String>()
while (this.moveToNext()){
val phone = this.getString(getColumnIndex("phone"))
list.add(phone)
}
list.forEach {
println(it)
}
}
}
/**
* @param table 表名
* @param nullColumnHack 这个字段没什么用
* @param values 插入保存的字段
* @return -1表示插入失败
*/
public long insert(String table, String nullColumnHack, ContentValues values)
@Test
fun insertApi() {
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
assertEquals("com.wzh.a01_filestore", appContext.packageName)
val helper = MyOpenHelper(appContext, "my.db")
val db = helper.readableDatabase
val cv = ContentValues()
cv.put("name", "李四")
cv.put("phone", "4535345345")
//table:表名
//nullColumnHack:这个字段基本没用,传null
//values:保存的字段
val rowId = db.insert("person", null, cv)
//返回值如果是-1,则插入失败
if (rowId != -1L) {
//成功
} else {
//插入失败
}
}
/**
* @param table 表名
* @param whereClause 条件
* @param whereArgs 条件参数的值
* @return 被影响的行数
*/
public int delete(String table, String whereClause, String[] whereArgs)
fun deleteApi() {
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
assertEquals("com.wzh.a01_filestore", appContext.packageName)
val helper = MyOpenHelper(appContext, "my.db")
val db = helper.readableDatabase
//返回影响的行数
val rowCount = db.delete("person", "_id = ?", arrayOf("2"))
}
/**
* @param table 表名
* @param values 修改的字段值
* @param whereClause 条件
* @param whereArgs 条件参数的值
* @return 被影响的行数
*/
public int update(String table, ContentValues values, String whereClause, String[] whereArgs)
@Test
fun updateApi() {
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
assertEquals("com.wzh.a01_filestore", appContext.packageName)
val helper = MyOpenHelper(appContext, "my.db")
val db = helper.readableDatabase
//要修改的值
val cv = ContentValues()
cv.put("name", "马云")
//返回影响的行数
val rowCount = db.update("person", cv, "_id = ?", arrayOf("3"))
}
/**
* @param table 表名
* @param columns 查询的列名,如果查全部的话,传入null
* @param selection 查询条件
* @param selectionArgs 条件参数的值数组
* @param groupBy 分组
* @param having having
* @param orderBy 排序
* @param limit 分页
* @return 被影响的行数
*/
public Cursor query(String table, String[] columns, String selection,
String[] selectionArgs, String groupBy, String having,
String orderBy, String limit)
@Test
fun selectApi() {
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
assertEquals("com.wzh.a01_filestore", appContext.packageName)
val helper = MyOpenHelper(appContext, "my.db")
val db = helper.readableDatabase
val cursor =
db.query("person", arrayOf("name"), "_id = ?", arrayOf("3"), null, null, null, null)
while (cursor.moveToNext()) {
val name = cursor.getString(cursor.getColumnIndex("name"))
println("name:${name}")
}
}
//开启事务
db.beginTransaction()
//设置事务执行成功,如果这行代码没执行,就会回滚
db.setTransactionSuccessful()
//关闭事务,提交数据
db.endTransaction()
@Test
fun transaction() {
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
assertEquals("com.wzh.a01_filestore", appContext.packageName)
val helper = MyOpenHelper(appContext, "my.db")
val db = helper.readableDatabase
try {
//开启事务
db.beginTransaction()
val cv = ContentValues()
cv.put("name", "哈哈")
db.update("person", cv, "_id = ?", arrayOf("3"))
cv.clear()
cv.put("name","呵呵")
db.update("person", cv, "_id = ?", arrayOf("4"))
//设置事务执行成功,如果这行代码没执行,就会回滚
db.setTransactionSuccessful()
}catch (e:Exception){
e.printStackTrace()
}finally {
//关闭事务,提交数据
db.endTransaction()
}
}
apply plugin: 'kotlin-kapt'
def room_version = "2.2.5"
implementation "androidx.room:room-runtime:$room_version"
kapt "androidx.room:room-compiler:$room_version"
定义一个抽象类继自
RoomDatabase
Room.inMemoryDatabaseBuilder(MainApp.application, MsgDatabase::class.java).build()
/**
* entities 对应表的实体字节码数组
* version 数据库版本号,升级时,需要增加
* exportSchema 导出数据库的信息,true为开启,默认是为true,可以指定注解处理器导出的目录,需要指定目录的属性是room.schemaLocation
* 在对应的build.gradle中的defaultConfig下使用
* javaCompileOptions{
annotationProcessorOptions{
arguments=["room.schemaLocation":"$projectDir/schemas".toString()]
}
}
*/
@Database(entities = [Msg::class], version = 1, exportSchema = true)
abstract class MsgDatabase() : RoomDatabase() {
companion object {
private var db: MsgDatabase? = null
/**
* 获取当前实例
*/
fun getInstance(context: Context): MsgDatabase {
if (db == null) {
synchronized(MsgDatabase::class) {
if (db == null) {
db = createDatabase(context)
}
}
}
return db!!
}
/**
* 初始化数据库
*/
private fun createDatabase(context: Context): MsgDatabase {
return Room.databaseBuilder(context, MsgDatabase::class.java, "msg.db")
.allowMainThreadQueries()//是否允许在主线程进行查询,一般都是在子线程中
.addCallback(object : RoomDatabase.Callback() {
override fun onCreate(db: SupportSQLiteDatabase) {
super.onCreate(db)
println("activity create database")
}
override fun onOpen(db: SupportSQLiteDatabase) {
super.onOpen(db)
println("activity open database")
}
}).build()
//.setQueryExecutor()//设置查询的线程池,一般使用默认的就行
//.setJournalMode()//设置日志模式,默认使用JournalMode.AUTOMATIC
//.fallbackToDestructiveMigration()//数据库升级异常后的回滚
//.fallbackToDestructiveMigrationFrom()//数据库升级异常后根据指定版本进行回滚
//.addMigrations(MsgDatabase.migration)//数据库升级
}
/**
* 升级
* @param startVersion 开始版本
* @param endVersion 结束版本
*/
private val migration = object : Migration(1, 2) {
override fun migrate(database: SupportSQLiteDatabase) {
//操作数据库语句
//database.update()
//database.delete()
//database.execSQL()
}
}
}
/**
* 获取表对应的dao
* 此dao需要创建一个接口,使用@Dao注解
*/
abstract fun getMsgDao(): MsgDao
}
@Entity
data class Msg(
val name: String,
@ColumnInfo
val phone: String,
val money: Float
) {
@PrimaryKey(autoGenerate = true)
var id: Int = 0 //自增长时,需要初始化为0
}
创建一个接口,使用@Dao注解进行描述,这个类就是提供操作数据库表的类
@Dao
interface MsgDao {
/**
* 插入
* @param onConflict 冲突策略
* @return 如果返回-1,则插入失败
*/
@Insert(entity = Msg::class, onConflict = OnConflictStrategy.REPLACE)
fun insertApi(msg: Msg): Long
/**
* 删除
* @return 影响的行数
*/
@Delete(entity = Msg::class)
fun deleteApi(msg: Msg): Int
/**
* 更新
* @return 影响的行数
*/
@Update(entity = Msg::class, onConflict = OnConflictStrategy.REPLACE)
fun updateApi(msg: Msg): Int
/**
* 查询
*/
@Query("SELECT * FROM msg")
fun selectApi(): List<Msg>
}
注意:需要调用到数据库的操作方法,才会创建数据库,也就是调用到Dao中的方法。
/**
* 根据id删除
*/
@Query("DELETE FROM msg WHERE id = :id")
fun deleteMsgById(id: Int): Int
/**
* 根据名称更新数据
*/
@Query("UPDATE msg SET phone = :phone WHERE name = :name")
fun updateMsgByName(name: String, phone: String): Int
@Database(entities:Array
数据库创建抽象类的注解。>,version:Int,exportSchema:Boolean)
entities
:被@Entity标记的实体对象数组。
version
版本号。
exportSchema
是否输出日志,默认是true,导出数据库的信息,true为开启,默认是为true,可以指定注解处理器导出的目录,需要指定目录的属性是room.schemaLocation
在对应的build.gradle中的defaultConfig下使用
javaCompileOptions{
annotationProcessorOptions{
arguments=[“room.schemaLocation”:“$projectDir/schemas”.toString()]
@Dao
标记在接口
@Entity(tableName:String,indices:Array
表对应的实体对象。,inheritSuperIndices:Boolean,primaryKeys:Array ,foreignKeys:Array ,ignoredColumns:Array )
indices
= {@Index(value = “key”, unique = false)}//本表索引,用于大量数据的查询优化,unique有时候需要保证数据表的某个或者某些字段只有唯一的记录,可以通过设置@Index注解的unique属性实现。以下实例代码实现了避免有两条记录包含一样的key值。
inheritSuperIndices
= false//如果 该值为true,那么父类中标记的indices{}索引也会算作该表的索引
primaryKeys
= {“key”}//主键,一些策略逻辑会用到,比如插入一条数据时如果已存在,则更新否则算新的插入,那么怎么判断 ,数据库中是否已存在该条数据呢?就判断提供的主键,在表中是否已存在
foreignKeys
= {
//外键,一般用于多表数据查询.可以配置多个外键
//ForeignKey用来设置关联表数据更新时所进行的操作,比如可以在@ForeignKey注解中设置onDelete=CASCADE,这样当Cache表中某个对应记录被删除时,ForeignTable表的所有相关记录也会被删除掉。
//对于@Insert(OnConflict=REPLACE)注解,SQLite是进行REMOVE和REPLACE操作,而不是UPDATE操作,这个可能影响到foreign key的约束。
//value:关联查询的表的Java.class,这里给定ForeignTable.class
//parentColumns:与之关联表ForeignTable表中的列名
//childColumns:本表的列的名称,必须要和parentColumns个数一致。这两个可以理解为根据cache表中的那个字段去比对ForeignTable表中的那个字段,认为是有关联关系的数据。
//onDelete:关联表中某条记录被delete或update时,本表应该怎么做:
// NO_ACTION:什么也不做,
// RESTRICT:本表跟parentColumns有关系的数据会立刻删除或更新,但不允许一对多的关系,
// SET_NULL:本表所跟parentColumns有关系的数据被设置为null值,
// SET_DEFAULT:本表所有跟parentColumns有关系的数据被设置为默认值,也是null值
// CASCADE:本表所有跟parentColumns有关系的数据一同被删除或更新
//onUpdate:本表中某条记录被更新时,与之关联的表应该怎么做
//deferred:本表某条记录变更时,与之关联表的数据变更是否要立即执行,还是等待本表事务处理完再来处理关联表。默认是同时处理。
// @ForeignKey(value = ForeignTable.class,
// parentColumns = “foreign_key”,
// childColumns = “key”,
// onDelete = 1,
// onUpdate = 1,
// deferred = false)}
ignoredColumns
= {“data”}//本表中 那些字段 不需要 映射到表中
@PrimaryKey(autoGenerate:Boolean)
//必须要有,且不为空,autoGenerate 主键的值是否由Room自动生成,默认false
@ColumnInfo(name = "_data")
,指定该字段在表中的列的名字
@Embedded
对象嵌套
@TypeConverter
类型转换器,例如在对象中存在列表类型,Date日期类型,可以只用此注解将类型转换成可以保存到数据库的数据
//类型转换,根据自己的项目需要,转换成对应的数据
class PermissionConverters {
@TypeConverter
fun listToJson(value: ArrayList<PersonPermissionInfo>?): String {
return GsonUtils.toJson(value)
}
@TypeConverter
fun jsonToList(value: String): ArrayList<PersonPermissionInfo>? {
return GsonUtils.fromJson(value,
object : TypeToken<ArrayList<PersonPermissionInfo>>() {}.type)
}
}
@TypeConverters(clazz:Array
将类型转换器注解在数据库上)
@TypeConverters(PermissionConverters::class)
abstract class XXX: RoomDatabase()