Android NDK开发详解应用数据和文件之使用 Room 引用复杂数据

Android NDK开发详解应用数据和文件之使用 Room 引用复杂数据


)
Room 提供了在基元类型和盒装类型之间进行转换的功能,但不允许在实体之间进行对象引用。本文档介绍了如何使用类型转换器,以及 Room 为何不支持对象引用。

使用类型转换器

有时,您需要应用将自定义数据类型存储在单个数据库列中。您可以通过提供类型转换器来支持自定义类型,类型转换器是 Room 的方法,用于告知 Room 如何将自定义类型与 Room 可以保留的已知类型相互转换。您可以使用 @TypeConverter 注解来识别类型转换器。
注意:Room 2.3 及更高版本包含一个用于持久性枚举的默认类型转换器。现有的类型转换器优先于默认类型转换器,但如果您还没有为枚举定义类型转换器,则无需专门定义。

假设您需要在 Room 数据库中保留 Date 的实例。Room 不知道如何保留 Date 对象,因此您需要定义类型转换器:
Kotlin

class Converters {
  @TypeConverter
  fun fromTimestamp(value: Long?): Date? {
    return value?.let { Date(it) }
  }

  @TypeConverter
  fun dateToTimestamp(date: Date?): Long? {
    return date?.time?.toLong()
  }
}

Java

public class Converters {
  @TypeConverter
  public static Date fromTimestamp(Long value) {
    return value == null ? null : new Date(value);
  }

  @TypeConverter
  public static Long dateToTimestamp(Date date) {
    return date == null ? null : date.getTime();
  }
}

此示例定义了两种类型转换器方法:一种用于将 Date 对象转换为 Long 对象,另一种用于执行从 Long 到 Date 的反向转换。由于 Room 知道如何保留 Long 对象,因此可以使用这些转换器保留 Date 对象。

接下来,您将 @TypeConverters 注解添加到 AppDatabase 类,以便 Room 知道您已定义的转换器类:
Kotlin

@Database(entities = [User::class], version = 1)
@TypeConverters(Converters::class)
abstract class AppDatabase : RoomDatabase() {
  abstract fun userDao(): UserDao
}

Java

@Database(entities = {User.class}, version = 1)
@TypeConverters({Converters.class})
public abstract class AppDatabase extends RoomDatabase {
  public abstract UserDao userDao();
}

定义这些类型转换器后,您就可以在实体和 DAO 中使用自定义类型,就像使用基元类型一样:
Kotlin

@Entity
data class User(private val birthday: Date?)

@Dao
interface UserDao {
  @Query("SELECT * FROM user WHERE birthday = :targetDate")
  fun findUsersBornOnDate(targetDate: Date): List<User>
}

Java

@Entity
public class User {
  private Date birthday;
}

@Dao
public interface UserDao {
  @Query("SELECT * FROM user WHERE birthday = :targetDate")
  List<User> findUsersBornOnDate(Date targetDate);
}

在本示例中,由于您为 AppDatabase 添加了 @TypeConverters 注解,因此 Room 可以在所有位置使用定义的类型转换器。不过,您也可以使用 @TypeConverters 为您的 @Entity 或 @Dao 类添加注解,以便将类型转换器的范围限定为特定的实体或 DAO。

控件类型转换器初始化

通常,Room 会为您处理类型转换器的实例化。不过,有时您可能需要将其他依赖项传递给类型转换器类,这意味着您需要应用直接控制类型转换器的初始化。在这种情况下,您可以为转换器类添加 @ProvidedTypeConverter 注解:
Kotlin

@ProvidedTypeConverter
class ExampleConverter {
  @TypeConverter
  fun StringToExample(string: String?): ExampleType? {
    ...
  }

  @TypeConverter
  fun ExampleToString(example: ExampleType?): String? {
    ...
  }
}

Java

@ProvidedTypeConverter
public class ExampleConverter {
  @TypeConverter
  public Example StringToExample(String string) {
    ...
  }

  @TypeConverter
  public String ExampleToString(Example example) {
    ...
  }
}

然后,除了在 @TypeConverters 中声明转换器类之外,您还可以使用 RoomDatabase.Builder.addTypeConverter() 方法将转换器类的实例传递给 RoomDatabase 构建器:
Kotlin

val db = Room.databaseBuilder(...)
  .addTypeConverter(exampleConverterInstance)
  .build()

Java

AppDatabase db = Room.databaseBuilder(...)
  .addTypeConverter(exampleConverterInstance)
  .build();

了解 Room 为何不允许对象引用

要点:Room 不允许在实体类之间进行对象引用。因此,您必须明确请求您的应用所需的数据。

映射从数据库到相应对象模型之间的关系是一种常见做法,非常适合服务器端。即使程序在访问字段时加载字段,服务器仍然可以正常工作。

但在客户端,这种延迟加载是不可行的,因为它通常发生在界面线程上,并且在界面线程上查询磁盘上的信息会导致严重的性能问题。界面线程通常需要大约 16 毫秒来计算和绘制 Activity 的更新后的布局,因此,即使查询只用了 5 毫秒,您的应用仍然可能会用尽剩余的时间来绘制框架,从而导致明显的显示故障。如果有一个并行运行的单独事务,或者设备正在运行其他磁盘密集型任务,则查询可能需要更多时间才能完成。不过,如果您不使用延迟加载,则应用会抓取一些不必要的数据,从而导致内存消耗问题。

对象关系型映射通常将决定权留给开发者,以便他们可以针对自己的应用用例执行最合适的操作。开发者通常会决定在应用和界面之间共享模型。但是,这种解决方案不能很好地扩展,因为界面会不断发生变化,共享模型会出现开发者难以预测和调试的问题。

例如,假设界面加载了 Book 对象的列表,其中每本图书都有一个 Author 对象。您最初可能设计让查询使用延迟加载,从而让 Book 实例检索作者。对 author 字段的第一次检索会查询数据库。一段时间后,您会发现还需要在应用界面中显示作者姓名。您可以轻松访问此名称,如以下代码段所示:
Kotlin

authorNameTextView.text = book.author.name

Java

authorNameTextView.setText(book.getAuthor().getName());

但是,这种看似无害的更改会导致在主线程上查询 Author 表格。

如果您事先查询作者信息,则在您不再需要这些数据时,就会很难更改数据的加载方式。例如,如果应用界面不再需要显示 Author 信息,则应用会有效地加载不再显示的数据,从而浪费宝贵的内存空间。如果 Author 类引用其他表格(例如 Books),则应用的效率会进一步下降。

如需使用 Room 同时引用多个实体,请改为创建包含每个实体的 POJO,然后编写用于联接相应表格的查询。这种结构合理的模型与 Room 强大的查询验证功能相结合,可让您的应用在加载数据时消耗较少的资源,从而改善应用的性能和用户体验。

本页面上的内容和代码示例受内容许可部分所述许可的限制。Java 和 OpenJDK 是 Oracle 和/或其关联公司的注册商标。

最后更新时间 (UTC):2023-04-02。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

五一编程

程序之路有我与你同行

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值