最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Android Room使用流程與底層原理詳解

 更新時(shí)間:2025年07月17日 10:05:43   作者:你過來啊你  
Room是Android的SQLite ORM庫,提供編譯時(shí)SQL驗(yàn)證、減少樣板代碼、支持LiveData/Flow/RxJava,適用于結(jié)構(gòu)化數(shù)據(jù)存儲(chǔ),適合用戶數(shù)據(jù)、緩存等場景,但不適合BLOB或簡單鍵值對存儲(chǔ),本文給大家介紹Android Room使用流程,感興趣的朋友一起看看吧

Room 是一個(gè)強(qiáng)大的 SQLite 對象映射庫,旨在提供更健壯、更簡潔、更符合現(xiàn)代開發(fā)模式的數(shù)據(jù)庫訪問方式。

核心價(jià)值: 消除大量樣板代碼,提供編譯時(shí) SQL 驗(yàn)證,強(qiáng)制結(jié)構(gòu)化數(shù)據(jù)訪問,并流暢集成 LiveData、Flow 和 RxJava 以實(shí)現(xiàn)響應(yīng)式 UI。

一、 使用流程 (Step-by-Step Workflow)

Room 的使用遵循一個(gè)清晰的結(jié)構(gòu)化流程:

  1. 添加依賴:

    // build.gradle (Module)
    dependencies {
        def room_version = "2.6.1" // 使用最新穩(wěn)定版本
        implementation "androidx.room:room-runtime:$room_version"
        kapt "androidx.room:room-compiler:$room_version" // Kotlin 使用 kapt
        // 可選:Kotlin 擴(kuò)展和協(xié)程支持
        implementation "androidx.room:room-ktx:$room_version"
        // 可選:RxJava2 支持
        implementation "androidx.room:room-rxjava2:$room_version"
        // 可選:RxJava3 支持
        implementation "androidx.room:room-rxjava3:$room_version"
        // 可選:測試支持
        androidTestImplementation "androidx.room:room-testing:$room_version"
    }
  2. 定義數(shù)據(jù)實(shí)體 (Entity):

    • 使用 @Entity 注解標(biāo)注一個(gè)數(shù)據(jù)類。
    • 每個(gè)實(shí)例代表數(shù)據(jù)庫表中的一行。
    • 使用 @PrimaryKey 定義主鍵(可以是 autoGenerate = true 實(shí)現(xiàn)自增)。
    • 使用 @ColumnInfo(name = "column_name") 自定義列名(可選)。
    • 定義字段(屬性),Room 默認(rèn)使用屬性名作為列名。
    • 可以定義索引 (@Index)、唯一約束 (@Index(unique = true)) 等。
    • 示例 (Kotlin):
      @Entity(tableName = "users",
              indices = [Index(value = ["last_name", "address"], unique = true)])
      data class User(
          @PrimaryKey(autoGenerate = true) val id: Int = 0,
          @ColumnInfo(name = "first_name") val firstName: String,
          @ColumnInfo(name = "last_name") val lastName: String,
          val age: Int,
          val address: String? // 可空類型對應(yīng)數(shù)據(jù)庫可為 NULL
      )
  3. 定義數(shù)據(jù)訪問對象 (DAO - Data Access Object):

    • 使用 @Dao 注解標(biāo)注一個(gè)接口或抽象類。
    • 包含用于訪問數(shù)據(jù)庫的方法(CURD:Create, Update, Read, Delete)。
    • 使用注解聲明 SQL 操作:
      • @Insert:插入一個(gè)或多個(gè)實(shí)體。返回 Long(插入行的 ID)或 Long[]/List<Long>。onConflict 參數(shù)定義沖突策略(如 OnConflictStrategy.REPLACE)。
      • @Update:更新一個(gè)或多個(gè)實(shí)體。返回 Int(受影響的行數(shù))。
      • @Delete:刪除一個(gè)或多個(gè)實(shí)體。返回 Int(受影響的行數(shù))。
      • @Query("SQL_STATEMENT"):執(zhí)行自定義 SQL 查詢。這是最強(qiáng)大的注解。
        • 方法可以返回實(shí)體、List<Entity>、LiveData<Entity>、Flow<Entity>、RxJava 類型 (Single, Observable 等) 或簡單類型 (Int, String 等)。
        • 使用 :paramName 在 SQL 中引用方法參數(shù)。
        • 支持復(fù)雜查詢(JOIN, GROUP BY, 子查詢等)。
        • 編譯時(shí) SQL 驗(yàn)證:Room 會(huì)在編譯時(shí)檢查你的 SQL 語法是否正確,并驗(yàn)證返回類型與查詢結(jié)果的映射關(guān)系。這是 Room 的核心優(yōu)勢之一,能提前捕獲錯(cuò)誤。
    • 示例 (Kotlin):
      @Dao
      interface UserDao {
          @Insert(onConflict = OnConflictStrategy.IGNORE)
          suspend fun insert(user: User): Long // 協(xié)程支持
          @Update
          suspend fun update(user: User): Int
          @Delete
          suspend fun delete(user: User): Int
          @Query("SELECT * FROM users ORDER BY last_name ASC")
          fun getAllUsers(): Flow<List<User>> // 使用 Flow 實(shí)現(xiàn)響應(yīng)式流
          @Query("SELECT * FROM users WHERE id = :userId")
          fun getUserById(userId: Int): LiveData<User> // 使用 LiveData 觀察單個(gè)用戶變化
          @Query("SELECT * FROM users WHERE age > :minAge")
          suspend fun getUsersOlderThan(minAge: Int): List<User> // 普通掛起函數(shù)
          @Query("DELETE FROM users WHERE last_name = :lastName")
          suspend fun deleteUsersByLastName(lastName: String): Int
      }
  4. 定義數(shù)據(jù)庫類 (Database):

    • 創(chuàng)建一個(gè)繼承 RoomDatabase 的抽象類。
    • 使用 @Database 注解標(biāo)注,并指定:
      • entities:包含該數(shù)據(jù)庫中的所有實(shí)體類數(shù)組。
      • version:數(shù)據(jù)庫版本號(整數(shù))。每次修改數(shù)據(jù)庫模式(表結(jié)構(gòu))時(shí)必須增加此版本號。
      • exportSchema:是否導(dǎo)出數(shù)據(jù)庫模式信息到文件(默認(rèn)為 true,建議保留用于版本遷移)。
    • 包含一個(gè)或多個(gè)返回 @Dao 接口/抽象類的抽象方法(無參數(shù))。
    • 通常使用單例模式獲取數(shù)據(jù)庫實(shí)例,以避免同時(shí)打開多個(gè)數(shù)據(jù)庫連接。
    • 示例 (Kotlin):
      @Database(entities = [User::class, Product::class], version = 2, exportSchema = true)
      abstract class AppDatabase : RoomDatabase() {
          abstract fun userDao(): UserDao
          abstract fun productDao(): ProductDao
          companion object {
              @Volatile
              private var INSTANCE: AppDatabase? = null
              fun getInstance(context: Context): AppDatabase {
                  return INSTANCE ?: synchronized(this) {
                      val instance = Room.databaseBuilder(
                          context.applicationContext,
                          AppDatabase::class.java,
                          "my_app_database.db" // 數(shù)據(jù)庫文件名
                      )
                      .addCallback(roomCallback) // 可選:數(shù)據(jù)庫創(chuàng)建/打開回調(diào)
                      .addMigrations(MIGRATION_1_2) // 版本遷移策略 (見下文)
                      // .fallbackToDestructiveMigration() // 危險(xiǎn):破壞性遷移(僅開發(fā)調(diào)試)
                      // .fallbackToDestructiveMigrationOnDowngrade() // 降級時(shí)破壞性遷移
                      .build()
                      INSTANCE = instance
                      instance
                  }
              }
              // 可選:數(shù)據(jù)庫首次創(chuàng)建或打開時(shí)的回調(diào)(用于預(yù)填充數(shù)據(jù)等)
              private val roomCallback = object : RoomDatabase.Callback() {
                  override fun onCreate(db: SupportSQLiteDatabase) {
                      super.onCreate(db)
                      // 在主線程執(zhí)行!小心耗時(shí)操作。通常用協(xié)程在后臺(tái)預(yù)填充。
                  }
                  override fun onOpen(db: SupportSQLiteDatabase) {
                      super.onOpen(db)
                      // 數(shù)據(jù)庫每次打開時(shí)調(diào)用
                  }
              }
              // 定義從版本 1 到版本 2 的遷移策略
              private val MIGRATION_1_2 = object : Migration(1, 2) {
                  override fun migrate(database: SupportSQLiteDatabase) {
                      // 執(zhí)行必要的 SQL 語句來修改數(shù)據(jù)庫模式
                      database.execSQL("ALTER TABLE users ADD COLUMN email TEXT")
                  }
              }
          }
      }
  5. 在應(yīng)用中使用數(shù)據(jù)庫:

    • 通過 AppDatabase.getInstance(context) 獲取數(shù)據(jù)庫實(shí)例。
    • 通過數(shù)據(jù)庫實(shí)例獲取相應(yīng)的 Dao (如 db.userDao())。
    • 使用 Dao 的方法執(zhí)行數(shù)據(jù)庫操作。
    • 關(guān)鍵:
      • 主線程限制: 默認(rèn)情況下,Room 不允許在主線程上執(zhí)行數(shù)據(jù)庫操作(會(huì)拋出 IllegalStateException)。這是為了防止 UI 卡頓。必須在后臺(tái)線程(如使用 Kotlin 協(xié)程、RxJavaLiveData + ViewModel + Repository 模式、ExecutorService)中執(zhí)行耗時(shí)操作。
      • 協(xié)程集成: room-ktx 提供了對 Kotlin 協(xié)程的完美支持,@Dao 方法可以標(biāo)記為 suspend。
      • 響應(yīng)式觀察: 返回 LiveDataFlow 的查詢方法會(huì)在數(shù)據(jù)變化時(shí)自動(dòng)通知觀察者,非常適合驅(qū)動(dòng) UI 更新。Room 會(huì)自動(dòng)在后臺(tái)線程執(zhí)行查詢并管理 LiveData/Flow 的生命周期。
    • 示例 (在 ViewModel 中使用 - Kotlin):
      class UserViewModel(application: Application) : AndroidViewModel(application) {
          private val db = AppDatabase.getInstance(application)
          private val userDao = db.userDao()
          // 使用 Flow 暴露用戶列表,Repository 模式更佳
          val allUsers: Flow<List<User>> = userDao.getAllUsers()
          fun insert(user: User) {
              viewModelScope.launch(Dispatchers.IO) { // 在 IO 線程池執(zhí)行
                  userDao.insert(user)
              }
          }
          fun getUser(userId: Int): LiveData<User> = userDao.getUserById(userId)
      }
  6. 數(shù)據(jù)庫遷移 (Migration - 重要!):

    • 當(dāng)修改了 Entity 類(添加/刪除/重命名字段、添加/刪除表、修改約束等),數(shù)據(jù)庫的模式發(fā)生了變化。
    • 必須增加 @Database 注解中的 version。
    • 必須提供 Migration 策略告訴 Room 如何從舊版本升級到新版本。使用 addMigrations(...) 添加到數(shù)據(jù)庫構(gòu)建器中。
    • Migration 對象重寫 migrate(database: SupportSQLiteDatabase) 方法,在其中執(zhí)行必要的 ALTER TABLE, CREATE TABLE, DROP TABLE 等 SQL 語句。
    • 破壞性遷移: 僅用于開發(fā)或可以接受數(shù)據(jù)丟失的場景。使用 .fallbackToDestructiveMigration().fallbackToDestructiveMigrationOnDowngrade()。生產(chǎn)環(huán)境慎用!

二、 應(yīng)用場景 (Use Cases)

Room 適用于需要結(jié)構(gòu)化、關(guān)系型、本地持久化存儲(chǔ)的場景:

  1. 用戶數(shù)據(jù)管理: 用戶配置、偏好設(shè)置、用戶資料信息。
  2. 應(yīng)用核心數(shù)據(jù)緩存: 從網(wǎng)絡(luò) API 獲取的數(shù)據(jù)(如新聞文章、產(chǎn)品目錄、社交媒體帖子)本地緩存,實(shí)現(xiàn)離線訪問和快速加載。
  3. 復(fù)雜數(shù)據(jù)查詢: 需要執(zhí)行 JOIN、聚合函數(shù)、排序、過濾等復(fù)雜 SQL 操作的場景。
  4. 歷史記錄/日志: 搜索歷史、瀏覽歷史、操作日志、聊天記錄。
  5. 表單/草稿保存: 用戶在填寫復(fù)雜表單過程中臨時(shí)保存的數(shù)據(jù)。
  6. 需要強(qiáng)類型和編譯時(shí)安全的數(shù)據(jù)庫訪問: 避免 SQL 字符串拼寫錯(cuò)誤和運(yùn)行時(shí)崩潰。
  7. 需要響應(yīng)式數(shù)據(jù)觀察: 當(dāng)數(shù)據(jù)庫數(shù)據(jù)變化時(shí)需要自動(dòng)更新 UI 的場景(通過 LiveData/Flow)。
  8. 需要事務(wù)支持的操作: 保證一組數(shù)據(jù)庫操作要么全部成功,要么全部失?。ㄈ玢y行轉(zhuǎn)賬)。
  9. 替代直接使用 SQLiteOpenHelperContentProvider: 提供更現(xiàn)代、更簡潔、更安全的抽象層。

不適合的場景:

  • 存儲(chǔ)大型二進(jìn)制文件(BLOB):應(yīng)存儲(chǔ)文件路徑到數(shù)據(jù)庫,文件本身存到文件系統(tǒng)。
  • 簡單的鍵值對存儲(chǔ):優(yōu)先考慮 SharedPreferencesDataStore
  • 非結(jié)構(gòu)化或文檔型數(shù)據(jù):考慮 Firestore (云) 或本地 NoSQL 方案(雖然 Room 也能存 JSON,但查詢不高效)。
  • 高度復(fù)雜的關(guān)系型數(shù)據(jù)庫設(shè)計(jì):雖然 Room 支持,但超復(fù)雜設(shè)計(jì)可能更適合專門的 SQLite 包裝或 ORM。

三、 實(shí)現(xiàn)原理 (Implementation Principles)

Room 的核心是一個(gè)編譯時(shí)注解處理器,它在編譯階段生成實(shí)現(xiàn)代碼,運(yùn)行時(shí)庫則提供執(zhí)行環(huán)境。其設(shè)計(jì)哲學(xué)是**“抽象而不隱藏”**,開發(fā)者依然寫 SQL,但獲得了更好的安全性和便利性。

  1. 編譯時(shí)處理 (Annotation Processing):

    • room-compiler (KAPT/KSP) 掃描代碼中的 @Entity, @Dao, @Database, @Query 等注解。
    • 生成實(shí)現(xiàn)類:
      • 為每個(gè) @Entity 生成對應(yīng)的 *_Table 類(包含表名、列名、創(chuàng)建表 SQL 等元信息)。
      • 為每個(gè) @Dao 接口/抽象類生成具體的實(shí)現(xiàn)類 (如 UserDao_Impl)。這個(gè)實(shí)現(xiàn)類包含:
        • @Insert, @Update, @Delete 注解方法的實(shí)現(xiàn):使用 EntityInsertionAdapter, EntityUpdateAdapter, EntityDeletionAdapter 等內(nèi)部類處理綁定參數(shù)和執(zhí)行 SQL。
        • @Query 的核心: 對于每個(gè) @Query 方法:
          • SQL 驗(yàn)證: 編譯器解析 SQL 語句,檢查語法錯(cuò)誤,驗(yàn)證表名、列名是否存在(基于 @Entity 定義)。
          • 返回類型映射驗(yàn)證: 嚴(yán)格檢查查詢返回的列數(shù)、類型是否與方法的返回類型(或其包含的實(shí)體類型)匹配。
          • 生成查詢實(shí)現(xiàn): 生成一個(gè) *_Query 類(如 getUserById_Query)。這個(gè)類:
            • 包含編譯好的 SQL 語句字符串。
            • 包含將方法參數(shù) (:paramName) 綁定到 SQLite 語句 (bind 方法) 的邏輯。
            • 包含將 Cursor(SQLite 查詢結(jié)果游標(biāo))行數(shù)據(jù)轉(zhuǎn)換為 Java/Kotlin 對象 (Entity 或簡單類型) 的邏輯 (convert/map 方法)。
      • @Database 類生成實(shí)現(xiàn)類 (如 AppDatabase_Impl)。這個(gè)類:
        • 繼承自你的抽象 AppDatabase。
        • 實(shí)現(xiàn)其抽象方法(如 userDao()),返回生成的 UserDao_Impl 實(shí)例。
        • 包含數(shù)據(jù)庫創(chuàng)建 (createAllTables) 和遷移相關(guān)的邏輯。
        • 持有 SupportSQLiteOpenHelper 實(shí)例(由 Room.databaseBuilder 配置),這是實(shí)際打開和管理 SQLite 數(shù)據(jù)庫的核心類。
  2. 運(yùn)行時(shí)庫 (room-runtime):

    • 提供 RoomDatabase, Room 等核心類和構(gòu)建器 (databaseBuilder, inMemoryDatabaseBuilder)。
    • 管理數(shù)據(jù)庫連接:通過生成的 *_Impl 類間接使用 SupportSQLiteOpenHelper(內(nèi)部封裝了 SQLiteOpenHelper 或直接使用 SQLite API)來打開、關(guān)閉和操作實(shí)際的 SQLite 數(shù)據(jù)庫文件。
    • SQLite 抽象 (SupportSQLite*): Room 定義了一套 SupportSQLiteDatabase, SupportSQLiteStatement 等接口。這些接口由 room-runtime 提供的 FrameworkSQLite* 實(shí)現(xiàn)類具體實(shí)現(xiàn)(最終調(diào)用 Android Framework 的 SQLiteDatabase, SQLiteStatement)。這提供了抽象層,方便測試(可以用內(nèi)存實(shí)現(xiàn)替換)。
    • 事務(wù)管理: 提供簡單的事務(wù) API (runInTransaction),確保操作的原子性。
    • LiveData/Flow 集成: 對于返回 LiveDataFlow 的查詢方法,Room 在內(nèi)部使用 InvalidationTracker 機(jī)制。它注冊一個(gè)觀察者監(jiān)聽底層 SupportSQLiteDatabase 的變化通知(通過 SQLite 的 sqlite3_update_hook 或更現(xiàn)代的 SQLiteDatabase.OnCommitListener 等)。當(dāng)檢測到相關(guān)表發(fā)生修改(Insert/Update/Delete)時(shí),它會(huì)自動(dòng)觸發(fā) LiveData 更新或發(fā)射新的 Flow 值(在后臺(tái)線程重新執(zhí)行查詢并傳遞新結(jié)果)。
    • 類型轉(zhuǎn)換器 (TypeConverter): 如果 @Entity 包含 Room 不直接支持的類型(如 Date, UUID, 自定義枚舉),你可以定義 @TypeConverter 類,Room 會(huì)在讀寫數(shù)據(jù)庫時(shí)自動(dòng)調(diào)用這些轉(zhuǎn)換器進(jìn)行類型映射。
    • 依賴注入 (可選): 其單例模式設(shè)計(jì)天然適合依賴注入框架(如 Dagger/Hilt)。

核心優(yōu)勢原理總結(jié):

  • 編譯時(shí)安全: 通過在編譯時(shí)解析和驗(yàn)證 SQL 及映射關(guān)系,將潛在的運(yùn)行時(shí)錯(cuò)誤(如 SQL 語法錯(cuò)誤、表/列不存在、返回類型不匹配)提前到編譯期暴露,極大提高可靠性。
  • 減少樣板代碼: 注解處理器自動(dòng)生成大量重復(fù)的、易錯(cuò)的數(shù)據(jù)庫操作代碼(如 CRUD 的 SQL 拼寫、參數(shù)綁定、游標(biāo)解析)。
  • 強(qiáng)制結(jié)構(gòu)化和抽象: 通過 EntityDao 清晰地定義了數(shù)據(jù)模型和訪問接口,符合良好的架構(gòu)原則(如 Clean Architecture)。
  • 現(xiàn)代化集成: 原生支持協(xié)程(suspend)、響應(yīng)式流(LiveData, Flow)、RxJava,簡化異步編程和 UI 更新。
  • 明確的線程模型: 默認(rèn)禁止主線程操作,引導(dǎo)開發(fā)者正確處理后臺(tái)任務(wù)。
  • 可測試性: 良好的抽象層(Dao 接口)使得單元測試業(yè)務(wù)邏輯時(shí)更容易 mock 數(shù)據(jù)庫層。room-testing 提供測試輔助工具。

總結(jié): Room 通過編譯時(shí)代碼生成和運(yùn)行時(shí)抽象封裝,將原始 SQLite API 的強(qiáng)大功能與現(xiàn)代化開發(fā)所需的類型安全、簡潔性、響應(yīng)式支持和架構(gòu)友好性完美結(jié)合,成為 Android 本地結(jié)構(gòu)化數(shù)據(jù)存儲(chǔ)的首選標(biāo)準(zhǔn)解決方案。理解其流程、場景和原理,能幫助開發(fā)者更高效、更可靠地構(gòu)建數(shù)據(jù)層。

到此這篇關(guān)于Android Room使用方法與底層原理詳解的文章就介紹到這了,更多相關(guān)Android Room使用內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Android WebP 圖片壓縮與傳輸

    Android WebP 圖片壓縮與傳輸

    本文主要講解Android WebP 圖片壓縮與傳輸,這里對WebP圖片格式以及如何實(shí)現(xiàn)壓縮和傳輸,做了詳細(xì)講解,有需要的小伙伴可以參考下
    2016-08-08
  • Android HttpURLConnection.getResponseCode()錯(cuò)誤解決方法

    Android HttpURLConnection.getResponseCode()錯(cuò)誤解決方法

    在使用HttpURLConnection.getResponseCode()的時(shí)候直接報(bào)錯(cuò)是IOException錯(cuò)誤,一直想不明白,同一個(gè)程序我調(diào)用了兩次,結(jié)果有一個(gè)鏈接一直O(jiān)K,另一個(gè)卻一直報(bào)這個(gè)錯(cuò)誤
    2013-06-06
  • 一個(gè)吸頂Item的簡單實(shí)現(xiàn)方法分享

    一個(gè)吸頂Item的簡單實(shí)現(xiàn)方法分享

    這篇文章主要給大家介紹了一個(gè)吸頂Item的簡單實(shí)現(xiàn)方法,文中通過示例代碼介紹的非常詳細(xì),對各位Android開發(fā)者們具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-09-09
  • android BitmapFactory.Options使用方法詳解

    android BitmapFactory.Options使用方法詳解

    這篇文章主要為大家詳細(xì)介紹了android BitmapFactory.Options使用方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-01-01
  • Android編程實(shí)現(xiàn)動(dòng)畫自動(dòng)播放功能

    Android編程實(shí)現(xiàn)動(dòng)畫自動(dòng)播放功能

    這篇文章主要介紹了Android編程實(shí)現(xiàn)動(dòng)畫自動(dòng)播放功能,結(jié)合實(shí)例形式分析了Android動(dòng)畫自動(dòng)播放功能的實(shí)現(xiàn)方法與相關(guān)注意事項(xiàng),需要的朋友可以參考下
    2017-07-07
  • 安卓14前端服務(wù)foregroundService權(quán)限問題解決辦法

    安卓14前端服務(wù)foregroundService權(quán)限問題解決辦法

    Android服務(wù)(Service)是應(yīng)用組件,它可以在后臺(tái)執(zhí)行長時(shí)間運(yùn)行的操作,而不提供用戶界面,這篇文章主要介紹了安卓14前端服務(wù)foregroundService權(quán)限問題解決的相關(guān)資料,文中通過代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2025-09-09
  • 怎樣才能導(dǎo)入別人的android項(xiàng)目不再報(bào)錯(cuò)

    怎樣才能導(dǎo)入別人的android項(xiàng)目不再報(bào)錯(cuò)

    每次看到好的項(xiàng)目都想拿過來看看源碼,可是導(dǎo)入以后各種報(bào)錯(cuò)怎么辦?源碼有問題嗎?有這種可能,但更多的可能性是你沒有正確導(dǎo)入這個(gè)項(xiàng)目
    2021-08-08
  • Android單元測試之對Activity的測試示例

    Android單元測試之對Activity的測試示例

    本篇文章主要介紹了Android單元測試之對Activity的測試示例,具有一定的參考價(jià)值,有興趣的可以了解一下
    2017-08-08
  • Android編程實(shí)現(xiàn)夜間模式的方法小結(jié)

    Android編程實(shí)現(xiàn)夜間模式的方法小結(jié)

    這篇文章主要介紹了Android編程實(shí)現(xiàn)夜間模式的方法,涉及Android亮度調(diào)節(jié)、自定義Theme、遮罩模式等相關(guān)使用技巧,需要的朋友可以參考下
    2018-01-01
  • Android 實(shí)現(xiàn)長按彈出PopupMenu 菜單欄

    Android 實(shí)現(xiàn)長按彈出PopupMenu 菜單欄

    這篇文章主要介紹了Android 實(shí)現(xiàn)長按彈出PopupMenu 菜單欄,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2019-12-12

最新評論

达日县| 越西县| 玛多县| 威海市| 泾川县| 东阳市| 特克斯县| 磐石市| 古交市| 高雄市| 三亚市| 光山县| 丽水市| 依安县| 长武县| 壤塘县| 扬中市| 沾化县| 城步| 蒙城县| 辽阳市| 乌什县| 永顺县| 孟连| 中超| 奉贤区| 天门市| 依兰县| 墨脱县| 平山县| 水富县| 日照市| 福安市| 千阳县| 霸州市| 万盛区| 蓬安县| 西乡县| 阿城市| 平阴县| 湖北省|