为什么@PrimaryKey val id: Int? = null 在创建 Room 实体时有效吗?

2024-03-05

我正在关注有关如何使用 Jetpack Compose 创建笔记应用程序的教程。这是tutorial https://youtu.be/8YPXv7xKh2w?t=1227。教程中有一点是他创建了这个实体:

@Entity
data class Note(
    val title: String,
    val content: String,
    val timestamp: Long,
    val color: Int,
    @PrimaryKey val id: Int? = null
)

请注意,他对主键使用了可为空的值,并且他没有使用(autoGenerate = true).

我本以为那是行不通的。首先是因为主键永远不应该为空,其次是因为如果没有 autoGenerate id 会如何表现?他们不是都有相同的id吗?

我在创建房间实体时更常看到的代码是这样的:

@PrimaryKey(autoGenerate = true) val id: Int = 0

如何@PrimaryKey注释到底有效吗?为什么这两种方法都有效?哪一款更值得推荐呢?

编辑:我想澄清一下,教程中使用的代码可以编译并正确保存到数据库中。


我本以为那是行不通的。首先是因为主键永远不应该为空,其次是因为如果没有 autoGenerate id 会如何表现?

房间实际使用null,将 0 转换为null如果 autogenerate 为 true,则在 INTEGER PRIMARY KEY 的特殊情况下,SQLite 会生成一个值null已指定。

  • 由于 SQLite 要求 INTEGER PRIMARY KEY 为整数,因此它处理 null,在这种特殊情况下,生成整数值。这可以解释为

rowid 表的 PRIMARY KEY 约束(只要它不是真正的主键或整数主键)实际上与 UNIQUE 约束是一样的。因为它不是真正的主键,所以 PRIMARY KEY 的列允许为 NULL,这违反了所有 SQL 标准。

  • 加粗了我认为推断 INTEGER PRIMARY KEY 不能为空的异常。
  • from https://www.sqlite.org/rowidtable.html https://www.sqlite.org/rowidtable.html

如果 autogenerate 为 true,则生成的代码(根据/来自下面的演示)包括:-

"INSERT OR IGNORE INTO `AutoGenTrueTable` (`id`,`name`) VALUES (nullif(?, 0),?)"

虽然在 autogenerate 为 false 的情况下,生成的代码改为使用:-

"INSERT OR IGNORE INTO `AutoGenFalseTable` (`id`,`name`) VALUES (?,?)"
  • 请注意,OR IGNORE是因为onConflictStrategy的IGNORE.

  • 后一个示例,即 autogenerate 为 false,因此如果 id 字段为 0,则将使用值 0

  • 生成的java可以通过Android Studio的Android View轻松找到。 DAO 位于与接口/抽象类同名但后缀为 的类中_Impl.

  • Room 使用的通过 Android SQLite API 的绑定将 null 转换为null关键字(令牌)。

    • 与名称相同的类@Database带注释的类,但带有_Impl后缀,还会有其他有用的代码,例如在createAllTables可以找到创建表的 SQL 的方法。

他们不是都有相同的id吗?

NO因为主键是隐式唯一的,因此如果 id 是主键并且无论自动生成是 true 或 false,则 id 永远不会与同一个表中的另一个 id 相同。

If autogenerate is true那么 Room 还将 0 转换为未提供的值,因此 0 会产生生成的值。

However如果您指定值 0 且如果自动生成为假 (默认显式或隐式)那么 0 将用于 id,不允许多次使用,但可以由onConflictStrategy的插入物。

The DEMO下面说明了上面的内容(请注意,使用了 IGNORE onConflictStrategy,因此错误的重复 0 id 将被忽略).

关于整数主键的一些知识 (e.g. @PrimaryKey val whatever:Int或者更正确地说Long) 又名的别名rowid column.

  • 可以使用 Byte、Short 等,因为它们是整数类型,但它们的用途有限。
  • Long 更正确,因为该值可以与 64 位有符号整数一样大(Int不够大,Long是的,在很多情况下这不是问题)。

如果某列专门为 INTEGER(Room 在编译时确定)并且 是主键(在列或表级别),则该列是特殊的通常隐藏列的别名,rowid,所有表都有(除了WITHOUT ROWID 表,Room 不通过注释支持)。

  • 请注意,rowid列始终存在(除非该表是一个WITHOUT ROWID表,这也是Room不支持的)并且总是分配一个整数值(无论是否指定或暗示INTEGER PRIMARY KEY)。

  • 虽然rowid用于 SQLite 接受其他别名参见https://www.sqlite.org/rowidtable.html https://www.sqlite.org/rowidtable.html了解更多rowids

此类列必须是 INTEGER 类型值(除了 rowid 列或其别名之外,任何列类型实际上都可以存储任何类型的值,尽管 Room 不支持这一点)。此外,如果在插入时没有为此类列指定值,则该值将由 SQLite 生成。这通常比最高值大 1rowid对于那张桌子。

因此,只要情况是没有为该列提供值,就会生成该值(并且很可能比最高值大 1)。

如果房间的autogenerate=true然后使用添加 SQLiteAUTOINCREMNET表定义/模式的关键字。这改变了该值的生成方式,因为它是两个值中较大的一个,其中一个是最高的rowid在表中,另一个是最高记录/曾经使用过的rowidvalue,如果具有最高 rowid 的行已被删除,则该值可能高于最高 rowid。

  • 请注意,这假设 sqlite_sequence 表除了 SQLite 对表的处理之外没有更改(它可以被操纵但要小心)

简而言之AUTOINCREMENT添加一个约束/规则,表示生成的值必须大于任何使用的值。然而,这要求最高的分配值必须存储在其他地方。 SQLite 将此附加值存储在名为的表中sqlite_序列每个表有 1 行。获取和维护这样的值会产生开销,因此 SQLite 文档指出:-

  • AUTOINCRMENT 关键字会带来额外的 CPU、内存、磁盘空间和磁盘 I/O 开销,如果不是严格需要,则应避免使用。通常不需要它。 see https://www.sqlite.org/autoinc.html https://www.sqlite.org/autoinc.html

使用 null 或 0 与autogenerate=true, Room 不提供值,因此生成值。如果提供任何其他值,则使用该值(如果行已存在,这将导致唯一冲突,独特的冲突处理onConFlictStrategy相应注释的参数('@Insert' 或@Update) 插入或更新时)

  • 如果使用 INSERT 或 UPDATE 查询,则实际的 SQLite OR ??????可以指定冲突操作,例如“插入或忽略......”

如前所述,如果没有 autogenerate=true,0将用作值(请参见下面的演示),因此为了避免自动增量的开销/浪费/低效率,那么该字段应该可以为空,并且使用 null 来生成值。

  • Java 中,基元(int、long)的默认值为 0 并且不能为 null,现在/过去有点不同,并且有一些问题。

DEMO


也许考虑下面的演示,其中使用了 3 个表(实体),其中一个使用了 autogenerate=true,另外 2 个表(实体)未指定,因此隐含了 autogenerate=false。其他两者之间的区别在于,第一个不允许 id 为空且默认 id 为 0,第二个允许且 id 默认值为 null。

The 3 @Entity带注释的类是:-

@Entity
data class AutoGenTrueTable(
    @PrimaryKey(autoGenerate = true)
    val id: Long=0,
    val name: String
)
@Entity
data class AutoGenFalseTable(
    @PrimaryKey
    val id: Long=0,
    val name: String
)
@Entity
data class AutoGenFalseNullableTable(
    @PrimaryKey
    val id: Long?=null,
    val name: String
)

为了演示 sqlite_sequence (从中提取所有数据),然后是一个 POJO:-

data class SQLiteSequence(
    val name: String,
    val seq: Long
)

an @Dao带注释的界面(适合插入、专门删除和提取数据):-

@Dao
interface AllDAOs {
    @Insert(onConflict = OnConflictStrategy.IGNORE)
    fun insert(autoGenTrueTable: AutoGenTrueTable): Long
    @Insert(onConflict = OnConflictStrategy.IGNORE)
    fun insert(autoGenFalseTable: AutoGenFalseTable): Long
    @Insert(onConflict = OnConflictStrategy.IGNORE)
    fun insert(autoGenFalseNullableTable: AutoGenFalseNullableTable): Long

    @Query("DELETE FROM autogentruetable WHERE id >=:highAsOrHigherThanId")
    fun deleteFromAGTTByHighIds(highAsOrHigherThanId: Long)
    @Query("DELETE FROM autogenfalsetable WHERE id >=:highAsOrHigherThanId")
    fun deleteFromAGFTByHighIds(highAsOrHigherThanId: Long)
    @Query("DELETE FROM autogenfalsenullabletable WHERE id >=:highAsOrHigherThanId")
    fun deleteFromFalseNullableByHighIds(highAsOrHigherThanId: Long)

    @Query("SELECT * FROM autogentruetable")
    fun getAllFromAutoGenTrue(): List<AutoGenTrueTable>
    @Query("SELECT * FROM autogenfalsetable")
    fun getAllFromAutoGenFalse(): List<AutoGenFalseTable>
    @Query("SELECT * FROM autogenfalsenullabletable")
    fun getAllFromAutoGenFalseNullable(): List<AutoGenFalseNullableTable>
    @Query("SELECT * FROM sqlite_sequence")
    fun getAllFromSQLiteSequence(): List<SQLiteSequence>
}

一个相当直截了当的@Database带注释的抽象类,允许使用主线程以简化演示:-

@Database(entities = [AutoGenTrueTable::class,AutoGenFalseTable::class,AutoGenFalseNullableTable::class], exportSchema = false, version = 1)
abstract class TheDatabase: RoomDatabase() {
    abstract fun getAllDAOs(): AllDAOs

    companion object {
        private var instance: TheDatabase? = null
        fun getInstance(context: Context): TheDatabase {
            if (instance == null) {
                instance = Room.databaseBuilder(context, TheDatabase::class.java, "the_database.db")
                    .allowMainThreadQueries()
                    .build()
            }
            return instance as TheDatabase
        }
    }
}

最后是一些插入/提取和删除数据的活动代码,对于所有 3 个演示表,将提取的数据(包括 sqlite_master 的内容)写入各个阶段的日志中:-

class MainActivity : AppCompatActivity() {

    lateinit var db: TheDatabase
    lateinit var dao: AllDAOs
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        db = TheDatabase.getInstance(this)
        dao = db.getAllDAOs()
        var stage = 0
        logEverything(stage++)

        for (i: Int in 1..3) {
            dao.insert(AutoGenTrueTable(0,"AG_TT_ZERO_${i}"))
            //dao.insert(AutoGenTrueTable(null,"AG_TT_NULL_${i}")) not nullable cannot be used
            dao.insert(AutoGenTrueTable(name = "AG_TT_DEFAULT_${i}"))
            dao.insert(AutoGenTrueTable(id = 100,"AG_TT_100_${i}"))

            dao.insert(AutoGenFalseTable(0,"AG_FT_ZERO_${i}"))
            //dao.insert(AutoGenFalseTable(id = null,name = "AG_FT_NULL_${i}")) not nullable cannot be used
            dao.insert(AutoGenFalseTable(name = "AG_FT_DEFAULT_${i}"))
            dao.insert(AutoGenFalseTable(id = 100, "AG_FT_100_${i}"))

            dao.insert((AutoGenFalseNullableTable(0, "AG_FTNULL_ZERO_${i}") ))
            dao.insert((AutoGenFalseNullableTable(null, "AG_FTNULL_NULL_${i}") ))
            dao.insert((AutoGenFalseNullableTable( name = "AG_FTNULL_DEFAULT_${i}") ))
            dao.insert(AutoGenFalseNullableTable(id = 100, name = "AG_FTNULL_100_${i}"))
        }
        logEverything(stage++)
        
        
        dao.deleteFromAGTTByHighIds(100)
        dao.deleteFromAGFTByHighIds(100)
        dao.deleteFromFalseNullableByHighIds(100)

        logEverything(stage++)
        
        
        for (i: Int in 1..3) {
            dao.insert(AutoGenTrueTable(0,"AG_TT_ZERO_${i}"))
            //dao.insert(AutoGenTrueTable(null,"AG_TT_NULL_${i}")) not nullable cannot be used
            dao.insert(AutoGenTrueTable(name = "AG_TT_DEFAULT_${i}"))
            dao.insert(AutoGenTrueTable(id = 100,"AG_TT_100_${i}"))

            dao.insert(AutoGenFalseTable(0,"AG_FT_ZERO_${i}"))
            //dao.insert(AutoGenFalseTable(id = null,name = "AG_FT_NULL_${i}")) not nullable cannot be used
            dao.insert(AutoGenFalseTable(name = "AG_FT_DEFAULT_${i}"))
            dao.insert(AutoGenFalseTable(id = 100, "AG_FT_100_${i}"))

            dao.insert((AutoGenFalseNullableTable(0, "AG_FTNULL_ZERO_${i}") ))
            dao.insert((AutoGenFalseNullableTable(null, "AG_FTNULL_NULL_${i}") ))
            dao.insert((AutoGenFalseNullableTable( name = "AG_FTNULL_DEFAULT_${i}") ))
            dao.insert(AutoGenFalseNullableTable(id = 100, name = "AG_FTNULL_100_${i}"))
        }
        logEverything(stage++)
    }

    fun logEverything(stage: Int) {
        Log.d("DBINFO_STARTSTAGE_${stage}","Starting logging of stage ${stage}")
        logAllFromAGTT(stage)
        logAllFromAGFT(stage)
        logAllFromAGFTN(stage)
        logAllFromSQLite_Sequence(stage)
    }

    fun logAllFromAGTT(stage: Int) {
        for(a in dao.getAllFromAutoGenTrue()) {
            Log.d("DBINFO_AGTT_STG${stage}","ID is ${a.id} NAME is ${a.name}")
        }
    }
    fun logAllFromAGFT(stage: Int) {
        for(a in dao.getAllFromAutoGenFalse()) {
            Log.d("DBINFO_AGFT_STG${stage}","ID is ${a.id} NAME is ${a.name}")
        }
    }

    fun logAllFromAGFTN(stage: Int) {
        for(a in dao.getAllFromAutoGenFalseNullable()) {
            Log.d("DBINFO_AGFTN_STG${stage}","ID is ${a.id} NAME is ${a.name}")
        }
    }

    fun logAllFromSQLite_Sequence(stage: Int) {
        for(ss in dao.getAllFromSQLiteSequence()) {
            Log.d("DBINFO_SSEQ_STG${stage}","TABLE IS ${ss.name} HIGHEST ID STORED FOR THE TABLE IS ${ss.seq}")
        }
    }
}

第一次运行应用程序安装时,输出为(阶段之间有 2 个空行,3 个表之间有一个空行):-

2023-04-14 12:01:26.073  D/DBINFO_STARTSTAGE_0: Starting logging of stage 0


2023-04-14 12:01:26.244  D/DBINFO_STARTSTAGE_1: Starting logging of stage 1
2023-04-14 12:01:26.246  D/DBINFO_AGTT_STG1: ID is 1 NAME is AG_TT_ZERO_1
2023-04-14 12:01:26.246  D/DBINFO_AGTT_STG1: ID is 2 NAME is AG_TT_DEFAULT_1
2023-04-14 12:01:26.246  D/DBINFO_AGTT_STG1: ID is 100 NAME is AG_TT_100_1
2023-04-14 12:01:26.246  D/DBINFO_AGTT_STG1: ID is 101 NAME is AG_TT_ZERO_2
2023-04-14 12:01:26.246  D/DBINFO_AGTT_STG1: ID is 102 NAME is AG_TT_DEFAULT_2
2023-04-14 12:01:26.247  D/DBINFO_AGTT_STG1: ID is 103 NAME is AG_TT_ZERO_3
2023-04-14 12:01:26.247  D/DBINFO_AGTT_STG1: ID is 104 NAME is AG_TT_DEFAULT_3

2023-04-14 12:01:26.249  D/DBINFO_AGFT_STG1: ID is 0 NAME is AG_FT_ZERO_1
2023-04-14 12:01:26.249  D/DBINFO_AGFT_STG1: ID is 100 NAME is AG_FT_100_1

2023-04-14 12:01:26.250  D/DBINFO_AGFTN_STG1: ID is 0 NAME is AG_FTNULL_ZERO_1
2023-04-14 12:01:26.250  D/DBINFO_AGFTN_STG1: ID is 1 NAME is AG_FTNULL_NULL_1
2023-04-14 12:01:26.250  D/DBINFO_AGFTN_STG1: ID is 2 NAME is AG_FTNULL_DEFAULT_1
2023-04-14 12:01:26.250  D/DBINFO_AGFTN_STG1: ID is 100 NAME is AG_FTNULL_100_1
2023-04-14 12:01:26.251  D/DBINFO_AGFTN_STG1: ID is 101 NAME is AG_FTNULL_NULL_2
2023-04-14 12:01:26.251  D/DBINFO_AGFTN_STG1: ID is 102 NAME is AG_FTNULL_DEFAULT_2
2023-04-14 12:01:26.251  D/DBINFO_AGFTN_STG1: ID is 103 NAME is AG_FTNULL_NULL_3
2023-04-14 12:01:26.251  D/DBINFO_AGFTN_STG1: ID is 104 NAME is AG_FTNULL_DEFAULT_3
2023-04-14 12:01:26.253  D/DBINFO_SSEQ_STG1: TABLE IS AutoGenTrueTable HIGHEST ID STORED FOR THE TABLE IS 104


2023-04-14 12:01:26.258  D/DBINFO_STARTSTAGE_2: Starting logging of stage 2
2023-04-14 12:01:26.261  D/DBINFO_AGTT_STG2: ID is 1 NAME is AG_TT_ZERO_1
2023-04-14 12:01:26.261  D/DBINFO_AGTT_STG2: ID is 2 NAME is AG_TT_DEFAULT_1

2023-04-14 12:01:26.262  D/DBINFO_AGFT_STG2: ID is 0 NAME is AG_FT_ZERO_1

2023-04-14 12:01:26.263  D/DBINFO_AGFTN_STG2: ID is 0 NAME is AG_FTNULL_ZERO_1
2023-04-14 12:01:26.263  D/DBINFO_AGFTN_STG2: ID is 1 NAME is AG_FTNULL_NULL_1
2023-04-14 12:01:26.263  D/DBINFO_AGFTN_STG2: ID is 2 NAME is AG_FTNULL_DEFAULT_1
2023-04-14 12:01:26.264  D/DBINFO_SSEQ_STG2: TABLE IS AutoGenTrueTable HIGHEST ID STORED FOR THE TABLE IS 104


2023-04-14 12:01:26.333  D/DBINFO_STARTSTAGE_3: Starting logging of stage 3

2023-04-14 12:01:26.336  D/DBINFO_AGTT_STG3: ID is 1 NAME is AG_TT_ZERO_1
2023-04-14 12:01:26.336  D/DBINFO_AGTT_STG3: ID is 2 NAME is AG_TT_DEFAULT_1
2023-04-14 12:01:26.337  D/DBINFO_AGTT_STG3: ID is 100 NAME is AG_TT_100_1
2023-04-14 12:01:26.337  D/DBINFO_AGTT_STG3: ID is 105 NAME is AG_TT_ZERO_1
2023-04-14 12:01:26.337  D/DBINFO_AGTT_STG3: ID is 106 NAME is AG_TT_DEFAULT_1
2023-04-14 12:01:26.337  D/DBINFO_AGTT_STG3: ID is 107 NAME is AG_TT_ZERO_2
2023-04-14 12:01:26.337  D/DBINFO_AGTT_STG3: ID is 108 NAME is AG_TT_DEFAULT_2
2023-04-14 12:01:26.337  D/DBINFO_AGTT_STG3: ID is 109 NAME is AG_TT_ZERO_3
2023-04-14 12:01:26.337  D/DBINFO_AGTT_STG3: ID is 110 NAME is AG_TT_DEFAULT_3

2023-04-14 12:01:26.340  D/DBINFO_AGFT_STG3: ID is 0 NAME is AG_FT_ZERO_1
2023-04-14 12:01:26.340  D/DBINFO_AGFT_STG3: ID is 100 NAME is AG_FT_100_1

2023-04-14 12:01:26.342  D/DBINFO_AGFTN_STG3: ID is 0 NAME is AG_FTNULL_ZERO_1
2023-04-14 12:01:26.342  D/DBINFO_AGFTN_STG3: ID is 1 NAME is AG_FTNULL_NULL_1
2023-04-14 12:01:26.342  D/DBINFO_AGFTN_STG3: ID is 2 NAME is AG_FTNULL_DEFAULT_1
2023-04-14 12:01:26.342  D/DBINFO_AGFTN_STG3: ID is 3 NAME is AG_FTNULL_NULL_1
2023-04-14 12:01:26.343  D/DBINFO_AGFTN_STG3: ID is 4 NAME is AG_FTNULL_DEFAULT_1
2023-04-14 12:01:26.343  D/DBINFO_AGFTN_STG3: ID is 100 NAME is AG_FTNULL_100_1
2023-04-14 12:01:26.343  D/DBINFO_AGFTN_STG3: ID is 101 NAME is AG_FTNULL_NULL_2
2023-04-14 12:01:26.343  D/DBINFO_AGFTN_STG3: ID is 102 NAME is AG_FTNULL_DEFAULT_2
2023-04-14 12:01:26.343  D/DBINFO_AGFTN_STG3: ID is 103 NAME is AG_FTNULL_NULL_3
2023-04-14 12:01:26.343  D/DBINFO_AGFTN_STG3: ID is 104 NAME is AG_FTNULL_DEFAULT_3
2023-04-14 12:01:26.346  D/DBINFO_SSEQ_STG3: TABLE IS AutoGenTrueTable HIGHEST ID STORED FOR THE TABLE IS 110

结果解释(一点)

可以清楚地看到,id 不重复(它们不能重复,因为在所有情况下主键都是隐式唯一的),而且 sqlite_sequence 只记录 TT 表中曾经使用过的最高 id(autogenerate=true 的 id)。

不太容易看出的是,当 autogenerate 为 false 时,至少暗示/默认,多次使用任何值(包括 0)将不会生成 id,即 Room 将该值传递给插入。该演示有忽略onConflictStrategy因此,这些尝试的重复操作将被忽略,不会发生任何故障。

因此,FT 和 FTNULL 的 ???_ZERO_nn 的 id 为 0,并且只有 nn 为 1 的单行。与 TT_ZERO 不同,TT_ZERO 中所有 3 行都已插入,生成的 ID 不为 0。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

为什么@PrimaryKey val id: Int? = null 在创建 Room 实体时有效吗? 的相关文章

  • 将片段部分移出屏幕

    我已经被这个问题难住有一段时间了 我试图将包含在 FrameLayout 中的片段向右移动 以便只有片段的左侧 20 可见 我认为我遇到的问题是父级不会让片段移出其边界 或者我不知道如何移动它 我所尝试的一切只是将片段推到右墙上 然后缩放片
  • 未找到 Google 地图 api v2 类

    我正在使用谷歌地图 api v2 一切正常 今天早上我更新了 sdk 现在地图无法工作 尝试了很多事情 例如再次导入 lib 项目 但似乎没有任何效果 请帮忙 这是 logcat 输出 05 16 08 53 34 327 E dalvik
  • SQLite 中的 NOT NULL 列和错误捕获

    我有一个由用户定义的 EditText 填充的数据库 所有编辑文本都不允许有空字段 我知道我可以通过几个简单的 if 语句来检查这一点 if myEditText getText toString equals display error
  • 从非风味应用程序引用的 Android 多风味库 - AAPT:找不到资源

    我有包含字符串资源的库
  • 如何在Android中将文件转换为base64(如.pdf、.text)?

    如何将 SD 卡文档 pdf txt 转换为 Base 64 字符串并将字符串发送到服务器 这个方法对我有用 String encodeFileToBase64Binary encodeFileToBase64Binary yourFile
  • 无法从视图转换为按钮

    我在这里遇到非常令人沮丧的问题 我有这个代码 Button b findViewById android R id button1 我收到了这个错误 类型不匹配 无法将表单视图转换为按钮 但是按钮1is一个按钮 在我的 XML 布局文档中
  • 在 Android 的 Recycler View 中的文本视图背景上生成并设置随机颜色

    I am Trying to Generate Random Colors and set the Random color as background of Text View Just Like in GMail app The Tex
  • 抽屉式导航不显示片段

    我创建了一个新的 Android Studio 项目 我的 MainActivity 是导航抽屉活动 所以 我无法显示碎片 我在互联网上和这里读过很多帖子 解释 我打开导航抽屉 选择菜单 播客 PodcastsFragment 应该显示 但
  • 以编程方式卸载 Android 应用程序

    我希望能够允许我的用户从我的应用程序中卸载应用程序 就像 Google Play 商店允许其用户一样 请下图 主要问题是如何定义一个按钮 按下它我们可以通过提供包名称或其他一些信息来卸载应用程序 就像图像上的卸载按钮一样 try Inten
  • Android图层列表不显示比例可绘制项目?

    使用下面的图层列表 我的比例绘制从未显示 这是为什么 菜单 对话框 标题 xml
  • Android HTTP PUT 请求

    谁能给我一个HTTP PUT请求 Android 的示例代码 假设您想使用 HttpURLConnection 要执行 HTTP PUT 请使用以下命令 URL url new URL http www example com resour
  • Android appwidget 远程视图未更新

    当我从某些活动更新小部件时 列表远程视图不会更新 我的意思是刷新自身 它会出现直到应用程序小部件的更新 日志显示 但不会进入列表视图的适配器以用新数据填充它 public void onUpdate Context context AppW
  • Android:了解 OnDrawFrame、FPS 和 VSync (OpenGL ES 2.0)

    一段时间以来 我在 Android 游戏中遇到了运动精灵间歇性 卡顿 的情况 这是一个非常简单的 2D OpenGL ES 2 0 游戏 这是一个持续存在的问题 我已经多次重新访问过 在我的游戏循环中 我有 2 个 计时器 一个用于记录前一
  • 在 NumberPicker 中显示更多数字

    我有两个问题 第一个问题是删除 NumberPicker 中的分隔线 我在 Android 中扩展 NumberPicker 来解决这个问题 如下所示 import android content Context import androi
  • Android smoothScrollTo 不调用 onScrollStateChanged

    我在用smoothScrollBy 滚动到 a 中的特定位置ListView 我希望在以下情况时得到通知ListView完成滚动以将其与当前集成onScrollStateChanged 当用户用手指滚动时触发的事件 目前我正在使用Timer
  • DOM 中不再存在缓存元素

    就像在类似的问题中一样 我使用appium java 尝试选择元素 在移动应用程序中 我要转到页面 之后有许多元素 android widget ImageView 0 我需要选择 6 个 例如 这样的元素并执行其他步骤 Byt 只能选择一
  • 如何强制刷新 CallLog.Calls.CACHED_NAME 列?

    我的目标是从通话记录中收集所有未知的电话号码 这可以通过以下代码来实现 private static final String CALLOG PROJECTION CallLog Calls ID CallLog Calls CACHED
  • 根据另一个列表的顺序对列表进行排序[重复]

    这个问题在这里已经有答案了 我需要对列表进行排序Person对象 List
  • API 24 AccessibilityService.dispatchGesture() 方法如何工作?

    通过 API 24 我们有了一种向设备发送手势的方法 但是目前还没有可靠的文档或示例 我正在尝试让它工作 但目前手势每次都会点击 onCancelled 回调 这是我调用该方法的代码 TargetApi 24 private void pr
  • 在片段视图之间切换

    在 xml 布局文件中声明片段的标准方法是

随机推荐

  • 如何使用spark-avro包从spark-shell读取avro文件?

    我正在尝试使用spark avro包如所述Apache Avro 数据源指南 https spark apache org docs latest sql data sources avro html 当我提交以下命令时 val df sp
  • Spark中使用reduceByKey时有没有有效的分区方法?

    当我使用reduceByKey or aggregateByKey 我遇到了分区问题 ex reduceBykey map code 特别是 如果输入数据存在偏差 则使用上述方法时分区问题会变得更加严重 因此 作为解决方案 我使用repar
  • Vue - 有什么方法可以让渲染完成吗?

    我知道mounted and created是生命周期挂钩并已经尝试过 我也尝试过使用 nextTick在已安装的情况下 这也无法获得准确的渲染 我目前正在尝试从 dom 节点访问一些数据after初始渲染 这是我的代码的相关部分 moun
  • php 仅从 url 获取文件名,不包含 url 中存在的任何变量值

    我想获取没有任何文件名 GETphp 中 URL 的变量值 我的网址是http learner com learningphp php lid 1348 我只想找回learningphp php从网址 这个怎么做 I used basena
  • Apache 文件缓存

    apache 如何处理某些文件的缓存 是否可以通过给定主机或虚拟主机的标准配置文件明确指出某些文件应该比其他文件更积极地缓存 具体来说 我在各种 XML 文件中保存了大量网站内容 并且我希望能够说该文件将被大量使用 因此会尽可能多地缓存它
  • 如何将 https://example.com/ 重定向到 https://www.example.com/

    我的代码中有这个代码 htaccess文件如下图所示 RewriteEngine On RewriteCond HTTPS off RewriteRule https HTTP HOST REQUEST URI 添加此内容是为了将 http
  • 多进程 vs 多线程 Python 耗时

    我有 2 个简单的函数 在一定范围内循环 可以单独运行而无需任何依赖 我正在尝试使用 Python 多处理模块和多线程模块来运行这两个函数 当我比较输出时 我发现多进程应用程序比多线程模块多花费 1 秒 我读到 由于全局解释器锁 多线程效率
  • 在 Java 中使用 Mockito 模拟枚举

    如何使用 Mockito 模拟枚举以进行测试 给出枚举的示例 public enum TestEnum YES NO 这是使用枚举的方法 public static boolean WorkTheEnum TestEnum theEnum
  • fopen 与 unicode 文件名

    我必须使用一个接受文件名作为字符串的库 const char 内部文件打开方式为fopen 有没有办法让这个库接受 unicode 文件名 我可以用吗宽字符到多字节 http msdn microsoft com en us library
  • 如何处理打字稿中异步函数的返回值? [复制]

    这个问题在这里已经有答案了 caseService 函数处理 HTTP 请求和响应并返回单个对象 我想归还对象 但由于它是一个异步功能 因此它返回空对象 this caseBook 我希望它仅在对象具有价值后才返回该对象 public in
  • Keycloak 8:已添加用户名“admin”的用户

    我无法使用 Ansible 和 Docker Compose 启动 Keycloak 容器 我收到错误 用户名 admin 的用户已添加到 opt jboss keycloak standalone configuration keyclo
  • 如何展平散列,使每个键成为唯一值?

    我想采用带有嵌套哈希和数组的哈希 并将其展平为具有唯一值的单个哈希 我一直试图从不同的角度来解决这个问题 但后来我把它变得比需要的更加复杂 让自己迷失在正在发生的事情中 源哈希示例 Name gt Kim Kones License Num
  • ArangoDB 和用户定义的函数或存储过程

    ArangoDB 文档 Foxx 部分 说 由于 Foxx 直接在 ArangoDB 内部运行 因此您可以将处理请求所需的所有数据库查询和逻辑捆绑在一个位置 除了使用 Foxx 框架 之外 还有其他 更原生 的方法来实现与 ArangoDB
  • 在 ASP.Net 中编写自定义 NTLM 质询/响应

    我知道您可以启用 NTLM验证在 ASP Net 应用程序中使用
  • 通过 React setState 使用子键的计算值

    我希望能够使用计算值sub keys在 React 中更新状态时 我了解如何在简单的设置中使用计算值 如下所示 this setState name value 但我在让键值计算适用于这样的情况时遇到困难 constructor props
  • 在 iPhone 中将 NSTimeInterval 转换为 NSString?

    如何将 NSTimeInterval 转换为字符串 我有 NSTimeInterval today NSDate date timeIntervalSince1970 我必须将 今天 作为 NSString 的输入 NSTimeInterv
  • 使用可配置键进行 Swift 4 JSON 解码

    我是 Swift 新手 我需要用一些东西来解析 JSON可配置按键 与我在这里看到的许多示例相反 密钥是已知的before解码操作开始 它们只依赖于传递给端点的一些参数 Example https some provider com end
  • 每个点都有时间序列的点之间的相关性下降

    我正在研究如何快速实现计算点 X Y Z 中 之间的相关性 并在给定搜索方向的情况下测量相关性何时低于某个阈值 我之前提出过一个相关问题计算点之间的相关性 其中每个点都有一个时间序列 https stackoverflow com ques
  • 在C程序中打印日语字符

    我想使用 C 程序打印日语字符 我找到了一些日语字符的 Unicode 范围 将它们转换为十进制并使用 for 循环来打印它们 setlocale LC ALL ja JP UTF8 for int i 12784 i lt 12799 i
  • 为什么@PrimaryKey val id: Int? = null 在创建 Room 实体时有效吗?

    我正在关注有关如何使用 Jetpack Compose 创建笔记应用程序的教程 这是tutorial https youtu be 8YPXv7xKh2w t 1227 教程中有一点是他创建了这个实体 Entity data class N