Android 数据存储

举报
yd_260845686 发表于 2022/10/25 10:42:07 2022/10/25
【摘要】 Android 数据存储数据可以存储在以下几个地方应用专属存储空间:存储仅供应用使用的文件,可以存储到内部存储卷中的专属目录或外部存储空间中的其他专属目录。使用内部存储空间中的目录保存其他应用不应访问的敏感信息。(内部存储卷其他应用不可访问, 外部存储空间在 storage/emulated/0/Android/data/包名 )共享存储:存储您的应用打算与其他应用共享的文件,包括媒体、文档...

Android 数据存储

数据可以存储在以下几个地方

  • 应用专属存储空间:存储仅供应用使用的文件,可以存储到内部存储卷中的专属目录或外部存储空间中的其他专属目录。使用内部存储空间中的目录保存其他应用不应访问的敏感信息。(内部存储卷其他应用不可访问, 外部存储空间在 storage/emulated/0/Android/data/包名 )

  • 共享存储:存储您的应用打算与其他应用共享的文件,包括媒体、文档和其他文件。

  • 偏好设置:以键值对形式存储私有原始数据。

  • 数据库:使用 Room 持久性库将结构化数据存储在专用数据库中。

使用SharedPreference保存键值对数据

SharedPreference 共享参数

一般用于保存偏好设置

保存文件路径 /data/data/应用包名/shared_prefs/文件名.xml

// 从 SharePreferences xml 文件中获取共享参数对象
SharedPreferences shared = getSharedPreferences("share", MODE_PRIVATE);
// 获得 xml 文件编辑器对象
SharedPreferences.Editor editor = shared.edit();
// 使用编辑器存储数据
// 添加 String 键值( DeveloperName对应 Kevin )
editor.putString("DeveloperName","Kevin");
// 提交编辑器的修改
// editor.commit(); or editor.apply();
// 二者区别在于apply是异步写入, commit是同步写入,可能会导致ANR
editor.commit();
// 使用共享参数对象读取数据
String DeveloperName = shared.getString("DeveloperName","");

SQLite

SQLite 数据库

使用数据库流程

  1. 创建或打开一个数据库 SQLiteDatabase

  2. 操作数据库 : 增,查,删,改以及执行 SQL 语句

  3. 关闭数据库 SQLiteDataBase

使用 Room 操作 SQLite

如需在应用中使用 Room,请将以下依赖项添加到应用的 build.gradle 文件

dependencies {
  def room_version = "2.2.6"
​
  implementation "androidx.room:room-runtime:$room_version"
  annotationProcessor "androidx.room:room-compiler:$room_version"
​
  // optional - RxJava support for Room
  implementation "androidx.room:room-rxjava2:$room_version"
​
  // optional - Guava support for Room, including Optional and ListenableFuture
  implementation "androidx.room:room-guava:$room_version"
​
  // optional - Test helpers
  testImplementation "androidx.room:room-testing:$room_version"
}
  • Room 的三个组件

    • Database : 数据库holder,持有者

    • Data entities : 数据实体,要存储的类

    • Data access objects ( DAOs ) :数据库访问方法

  • 三个组件之间的关系

    • 开发者使用 DAO的方法 将 Data entities的对象 放入 Database

    • 下面这个图为谷歌官方图

    • image-20210131113357612


  • 使用例子(Sample)

    1. 定义Data entity , Data access object 以及 Database 具体对象

      Data entity (更多使用细节 使用 Room 实体定义数据 )

      @Entity
      public class User {
          @PrimaryKey
          public int uid;
      ​
          @ColumnInfo(name = "first_name")
          public String firstName;
      ​
          @ColumnInfo(name = "last_name")
          public String lastName;
      }

      Data access object (更多使用细节 使用 Room DAO 访问数据 )

      @Dao
      public interface UserDao {
          @Query("SELECT * FROM user")
          List<User> getAll();
      ​
          @Query("SELECT * FROM user WHERE uid IN (:userIds)")
          List<User> loadAllByIds(int[] userIds);
      ​
          @Query("SELECT * FROM user WHERE first_name LIKE :first AND " +
                 "last_name LIKE :last LIMIT 1")
          User findByName(String first, String last);
      ​
          @Insert
          void insertAll(User... users);
      ​
          @Delete
          void delete(User user);
      }

      Database ( 更多使用细节见google官方文档 )

      @Database(entities = {User.class}, version = 1)
      public abstract class AppDatabase extends RoomDatabase {
          public abstract UserDao userDao();
      }
    2. 使用 Dao 操作数据库

      创建或者打开数据库

      AppDatabase db = Room.databaseBuilder(getApplicationContext(),
              AppDatabase.class, "database-name").build();

      获取数据库访问方法

      UserDao userDao = db.userDao(); // 获取 Dao 对象
      List<User> users = userDao.getAll(); // 获取数据库所有对象

      使用 Data access object 增,查,删,改

Room entities 详细使用方法
  1. 主键(Primary Key)

    @Entity
    public class User {
        @PrimaryKey
        public int id;
​
        public String firstName;
        public String lastName;
    }
    

要存储类中的属性,必须将属性设为 public , 或者给出 getter 和 setter

每个 entity 必须将一个属性定义为主键(Primary Key) , 且每一个实例对象必须有不同的主键值才可以放入数据库 , 就是说每个 entity 的对象都要有一个唯一的编号,不然不好找

如果您想让 Room 为实体分配自动 ID,则可以设置 @PrimaryKeyautoGenerate 属性。如果实体具有复合主键,您可以使用 @Entity 注释的 primaryKeys 属性,如以下代码段所示:

   @Entity(primaryKeys = {"firstName", "lastName"})
    public class User {
        public String firstName;
        public String lastName;
    }
    
  1. 数据库表名称

默认情况下,Room 将类名称用作数据库表名称。如果希望表具有不同的名称,请设置 @Entity 注释的 tableName 属性,如以下代码段所示:

    @Entity(tableName = "users")
    public class User {
        // ...
    }
    

表名称不区分大小写

  1. 数据库列名称

Room 将属性名称用作数据库中的列名称。如果希望列具有不同的名称,请将 @ColumnInfo 注释添加到字段,如以下代码段所示:

    @Entity(tableName = "users")
    public class User {
        @PrimaryKey
        public int id;
​
        @ColumnInfo(name = "first_name")
        public String firstName;
​
        @ColumnInfo(name = "last_name")
        public String lastName;
    }
    
  1. 忽略属性

默认情况下,Room 会为实体中定义的每个属性创建一个列。如果实体(要存储的类)中有您不想保留的属性,则可以使用 @Ignore 为这些字段添加注释,如以下代码段所示:

    @Entity
    public class User {
        @PrimaryKey
        public int id;
​
        public String firstName;
        public String lastName;
​
        @Ignore
        Bitmap picture;
    }   

如果实体类继承了父类的属性,则使用 @Entity 属性的 ignoredColumns 属性来忽略:

    @Entity(ignoredColumns = "picture")
    public class RemoteUser extends User {
        @PrimaryKey
        public int id;
​
        public boolean hasVpn;
    }
    
  1. 让表支持搜索功能,以后再学习(https://developer.android.google.cn/training/data-storage/room/defining-data#java)

使用 Dao 访问数据库
  1. 插入 insert

    @Dao
    public interface UserDao {
            @Insert(onConflict = OnConflictStrategy.REPLACE)
        //OnConflictStrategy.ABORT(默认)在发生冲突时回滚事务
            //OnConflictStrategy.REPLACE 现有行替换为新行
            //OnConflictStrategy.IGNORE 持现有行  
        public void insertUsers(User... users);
        @Insert
        public void insertBothUsers(User user1, User user2);
        @Insert
        public void insertUsersAndFriends(User user, List<User> friends);
    }

    如果 @Insert 方法只接收 1 个参数,则它可以返回 long,这是插入项的新 rowId

    如果参数是数组或集合,则应返回 long[]List<Long>

  2. 更新 Update

    @Dao
    public interface UserDao {
        @Update
        public void updateUsers(User... users);
    }

    传入的对象的会替换在数据库中有相同 id 的那一行

    可以让此方法返回一个 int 值,以指示数据库中更新的行数

  3. 删除 delete

        @Dao
        public interface MyDao {
            @Delete
            public void deleteUsers(User... users);
        }
        

    可以让此方法返回一个 int 值,以指示从数据库中删除的行数

  4. 查询 query

    • 简单查询

    •     @Dao
          public interface MyDao {
              @Query("SELECT * FROM user")
              public User[] loadAllUsers();
          }
          
  5. 总结一下

  • 现在还没有学过数据库,说一下我自己的理解

  • Room 可以把类的对象存储到数据库中

  • 每一个对象占一行(row),每一个对象中被存储的属性占一列

  • 行 : 对象 列:属性 属性1 属性 2 ...
    对象 1(PrimaryKey) 对象 1 的属性1 的值 对象 1 的属性2的值
    对象 2(PrimaryKey) 对象 2 的属性1 的值 对象 2的属性2 的值
    ...
  • 这样就组成一张表,一张表就是一个数据库

  • 要操作这个数据库就使用 Dao 中的方法




持久化存储

参考: csdn 博客1

文件系统

android系统使用的是虚拟文件系统(VFS)

VFS的目录是以/为根节点,根节点下又有不同的节点

同一个存储设备经过分区后,不同的分区可以挂载到不同的节点上,如手机的内置存储卡。

根目录/的文件夹

data目录 用户程序目录 dev目录 设备文件 etc目录 系统主要配置文件 mnt目录 挂载点目录 proc目录 运行时文件目录 sys目录 Linux 内核文件目录 system目录 Android系统文件目录 init.rc文件 Android系统启动脚本 default.prop文件 系统属性配置文件

/system目录

/system/app 存放系统自带的应用,默认不能删除 /system/bin Android中可执行的linux指令文件(ELF) /system/etc host:主机名和ip地址的映射 /system/fonts Android中自带的字体 /system/framework 存放谷歌提供的java api /system/lib 核心功能的类库,C/C++文件 /system/media/audio 存放Android的音效文件 /system/tts 语音发声引擎,默认不支持中文 /system/usr 用户设备的配置信息,键盘编码和按键编码的映射 /system/xbin 是专为开发人员准备的二进制指令


android的内存和外存

android的内存和外存的定义是对于应用来说的, 不是对于闪存和sd卡来说的

外存专为保存应用的持久性文件而设计

内存是为了保存应用的缓存文件

具体见文章后面, 和普通的pc不太一样


20180326105931844.png

内部存储空间的访问

  • 应用专属存储空间

    存储仅供应用使用的文件,

    可以存储到内部存储卷中的专属目录或外部存储空间中的其他专属目录。

    使用内部存储空间中的目录保存其他应用不应访问的敏感信息。


    访问应用内部存储空间不需要权限 , 使用 getFilesDir()getCacheDir() 方法

    • getFilesDir(): 返回应用程序的内部files目录的File对象。

    • getCacheDir():返回一个应用程序内部临时文件目录的File对象,这个临时文件在不需要时会被删除,在系统可用存储很低时也会被系统删除。

    访问应用外部存储空间从android4. 4 起不需要权限, 使用 getExternalFilesDir()getExternalCacheDir() 方法

    // 实测发现对于没有sd卡的手机来说 , 其对应的内存和外存如下
    ​
    // /data/user/0/com.example.databindingdemo/files
    System.out.println(getFilesDir().getAbsolutePath());
    ​
    // /storage/emulated/0/Android/data/com.example.databindingdemo/files
    System.out.println(getExternalFilesDir(null).getAbsolutePath());
    ​


// File 对象可以代表文件和目录
​
// 获取应用在内部存储的文件目录
File dir = context.getFilesDir();
​
// 创建文件
File file = new File(context.getFilesDir().toString()+ "/ test.txt");
if(!file.exists()) {
  try {
    file.createNewFile();
  } catch (IOException e) {
    e.printStackTrace();
  }
}
​


【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。