当前位置: 首页 > news >正文

基于Room+RESTful的双权限Android开机时间监控方案

概述

以下是使用Kotlin实现的商业级Android开机时间记录功能,包含现代Android开发最佳实践。

系统架构

  1. 组件设计
// BootReceiver - 接收开机广播
class BootReceiver : BroadcastReceiver() {override fun onReceive(context: Context, intent: Intent?) {if (intent?.action == Intent.ACTION_BOOT_COMPLETED) {BootTimeLogger.logBootTime(context)}}
}// BootTimeLogger - 核心日志记录类
object BootTimeLogger {private const val TAG = "BootTimeLogger"fun logBootTime(context: Context) {val bootTime = System.currentTimeMillis()// 记录到本地存储saveBootTimeLocally(context, bootTime)// 异步上传到服务器uploadBootTimeToServer(context, bootTime)}private fun saveBootTimeLocally(context: Context, bootTime: Long) {// 实现本地存储}private fun uploadBootTimeToServer(context: Context, bootTime: Long) {// 实现服务器上传}
}

完整实现代码

  1. AndroidManifest.xml 配置
<manifest xmlns:android="http://schemas.android.com/apk/res/android"xmlns:tools="http://schemas.android.com/tools"package="com.example.boottimelogger"><uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" /><uses-permission android:name="android.permission.INTERNET" /><uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /><uses-permission android:name="android.permission.WAKE_LOCK" /><applicationandroid:name=".BootTimeApplication"android:allowBackup="true"android:icon="@mipmap/ic_launcher"android:label="@string/app_name"android:theme="@style/AppTheme"><!-- 开机广播接收器 --><receiverandroid:name=".receiver.BootReceiver"android:enabled="true"android:exported="true"tools:ignore="ExportedReceiver"><intent-filter><action android:name="android.intent.action.BOOT_COMPLETED" /><action android:name="android.intent.action.QUICKBOOT_POWERON" /><action android:name="android.intent.action.LOCKED_BOOT_COMPLETED" /><category android:name="android.intent.category.DEFAULT" /></intent-filter></receiver><!-- 上传服务 --><serviceandroid:name=".service.BootTimeUploadService"android:exported="false" /><!-- WorkManager配置 --><providerandroid:name="androidx.startup.InitializationProvider"android:authorities="${applicationId}.androidx-startup"android:exported="false"tools:node="merge"><meta-dataandroid:name="androidx.work.WorkManagerInitializer"android:value="androidx.startup" /></provider></application>
</manifest>
  1. Application类
class BootTimeApplication : Application() {override fun onCreate() {super.onCreate()initWorkManager()initCrashReporting()}private fun initWorkManager() {// WorkManager配置val config = Configuration.Builder().setMinimumLoggingLevel(if (BuildConfig.DEBUG) Log.DEBUG else Log.ERROR).build()WorkManager.initialize(this, config)}private fun initCrashReporting() {// 初始化错误报告(可选)if (!BuildConfig.DEBUG) {// FirebaseCrashlytics.getInstance().setCrashlyticsCollectionEnabled(true)}}
}
  1. 数据模型
data class BootTimeRecord(val id: Long = 0,val timestamp: Long,val uploaded: Boolean = false,val deviceId: String = "",val osVersion: String = Build.VERSION.RELEASE,val appVersion: String = BuildConfig.VERSION_NAME
)@Entity(tableName = "boot_times")
data class BootTimeEntity(@PrimaryKey(autoGenerate = true) val id: Long = 0,@ColumnInfo(name = "timestamp") val timestamp: Long,@ColumnInfo(name = "uploaded") val uploaded: Boolean = false,@ColumnInfo(name = "device_id") val deviceId: String = "",@ColumnInfo(name = "os_version") val osVersion: String = Build.VERSION.RELEASE,@ColumnInfo(name = "app_version") val appVersion: String = BuildConfig.VERSION_NAME,@ColumnInfo(name = "created_at") val createdAt: Long = System.currentTimeMillis()
)
  1. Room数据库
@Dao
interface BootTimeDao {@Insertsuspend fun insert(record: BootTimeEntity): Long@Query("SELECT * FROM boot_times WHERE uploaded = 0 ORDER BY timestamp ASC")suspend fun getPendingRecords(): List<BootTimeEntity>@Query("UPDATE boot_times SET uploaded = 1 WHERE id = :id")suspend fun markAsUploaded(id: Long)@Query("SELECT COUNT(*) FROM boot_times WHERE uploaded = 0")suspend fun getPendingCount(): Int
}@Database(entities = [BootTimeEntity::class], version = 1, exportSchema = true)
abstract class BootTimeDatabase : RoomDatabase() {abstract fun bootTimeDao(): BootTimeDaocompanion object {@Volatileprivate var INSTANCE: BootTimeDatabase? = nullfun getInstance(context: Context): BootTimeDatabase {return INSTANCE ?: synchronized(this) {val instance = Room.databaseBuilder(context.applicationContext,BootTimeDatabase::class.java,"boot_time_database").apply {if (BuildConfig.DEBUG) {fallbackToDestructiveMigration()}}.build()INSTANCE = instanceinstance}}}
}
  1. 核心管理类
class BootTimeManager private constructor(context: Context) {private val database: BootTimeDatabase = BootTimeDatabase.getInstance(context)private val dao: BootTimeDao = database.bootTimeDao()private val prefs: SharedPreferences = context.getSharedPreferences("boot_time_prefs", Context.MODE_PRIVATE)private val deviceId: String by lazy {prefs.getString("device_id", "") ?: run {val newId = UUID.randomUUID().toString()prefs.edit { putString("device_id", newId) }newId}}companion object {@Volatileprivate var INSTANCE: BootTimeManager? = nullfun getInstance(context: Context): BootTimeManager {return INSTANCE ?: synchronized(this) {val instance = BootTimeManager(context.applicationContext)INSTANCE = instanceinstance}}}suspend fun recordBootTime(timestamp: Long = System.currentTimeMillis()): Long {return withContext(Dispatchers.IO) {val record = BootTimeEntity(timestamp = timestamp,deviceId = deviceId)dao.insert(record)}}suspend fun getPendingRecords(): List<BootTimeEntity> {return withContext(Dispatchers.IO) {dao.getPendingRecords()}}suspend fun markAsUploaded(id: Long) {withContext(Dispatchers.IO) {dao.markAsUploaded(id)}}suspend fun getPendingCount(): Int {return withContext(Dispatchers.IO) {dao.getPendingCount()}}fun getLastBootTime(): Long {return prefs.getLong("last_boot_time", 0)}private fun setLastBootTime(timestamp: Long) {prefs.edit { putLong("last_boot_time", timestamp) }}
}
  1. 广播接收器(Kotlin优化版)
class BootReceiver : BroadcastReceiver() {private val tag = "BootReceiver"override fun onReceive(context: Context, intent: Intent?) {if (intent == null) returnval action = intent.actionlogDebug("Received broadcast: $action")when (action) {Intent.ACTION_BOOT_COMPLETED,"android.intent.action.QUICKBOOT_POWERON","android.intent.action.LOCKED_BOOT_COMPLETED" -> {handleBootCompleted(context)}}}private fun handleBootCompleted(context: Context) {if (!isAppInitialized(context)) {logDebug("App not initialized, scheduling delayed logging")scheduleDelayedLogging(context)return}recordBootTime(context)scheduleUploadWork(context)}private fun isAppInitialized(context: Context): Boolean {// 检查必要的组件是否已初始化return try {BootTimeManager.getInstance(context)true} catch (e: Exception) {false}}private fun recordBootTime(context: Context) {CoroutineScope(Dispatchers.IO).launch {try {val bootTime = System.currentTimeMillis()val recordId = BootTimeManager.getInstance(context).recordBootTime(bootTime)logDebug("Boot time recorded successfully: $recordId")} catch (e: Exception) {logError("Failed to record boot time", e)}}}private fun scheduleDelayedLogging(context: Context) {val workRequest = OneTimeWorkRequestBuilder<DelayedBootWorker>().setInitialDelay(1, TimeUnit.MINUTES).setBackoffCriteria(BackoffPolicy.LINEAR,OneTimeWorkRequest.MIN_BACKOFF_MILLIS,TimeUnit.MILLISECONDS).build()WorkManager.getInstance(context).enqueue(workRequest)}private fun scheduleUploadWork(context: Context) {val constraints = Constraints.Builder().setRequiredNetworkType(NetworkType.CONNECTED).setRequiresBatteryNotLow(true).build()val workRequest = OneTimeWorkRequestBuilder<BootTimeUploadWorker>().setConstraints(constraints).setBackoffCriteria(BackoffPolicy.EXPONENTIAL,30, TimeUnit.SECONDS).build()WorkManager.getInstance(context).enqueue(workRequest)}private fun logDebug(message: String) {if (BuildConfig.DEBUG) {Log.d(tag, message)}}private fun logError(message: String, e: Exception? = null) {Log.e(tag, message, e)// 可集成错误报告系统}
}
  1. WorkManager Worker
class BootTimeUploadWorker(context: Context,params: WorkerParameters
) : CoroutineWorker(context, params) {override suspend fun doWork(): Result {return withContext(Dispatchers.IO) {try {logDebug("Starting boot time upload work")val manager = BootTimeManager.getInstance(applicationContext)val pendingRecords = manager.getPendingRecords()if (pendingRecords.isEmpty()) {logDebug("No pending records to upload")return@withContext Result.success()}logDebug("Found ${pendingRecords.size} records to upload")var successCount = 0pendingRecords.forEach { record ->if (uploadRecord(record)) {manager.markAsUploaded(record.id)successCount++}}logDebug("Upload completed: $successCount/${pendingRecords.size} successful")if (successCount == pendingRecords.size) {Result.success()} else {Result.retry()}} catch (e: Exception) {logError("Upload work failed", e)Result.retry()}}}private suspend fun uploadRecord(record: BootTimeEntity): Boolean {return try {// 实现实际的上传逻辑val apiService = createApiService()val response = apiService.uploadBootTime(record.toDto())response.isSuccessful} catch (e: Exception) {logError("Failed to upload record ${record.id}", e)false}}private fun BootTimeEntity.toDto(): BootTimeDto {return BootTimeDto(timestamp = timestamp,deviceId = deviceId,osVersion = osVersion,appVersion = appVersion)}private fun createApiService(): BootTimeApiService {// 创建Retrofit服务实例TODO("Implement API service creation")}private fun logDebug(message: String) {if (BuildConfig.DEBUG) {Log.d("BootTimeUploadWorker", message)}}private fun logError(message: String, e: Exception) {Log.e("BootTimeUploadWorker", message, e)}
}class DelayedBootWorker(context: Context,params: WorkerParameters
) : CoroutineWorker(context, params) {override suspend fun doWork(): Result {return withContext(Dispatchers.IO) {try {logDebug("Executing delayed boot recording")BootTimeManager.getInstance(applicationContext).recordBootTime()Result.success()} catch (e: Exception) {logError("Delayed boot recording failed", e)Result.retry()}}}
}
  1. API相关类
data class BootTimeDto(val timestamp: Long,val deviceId: String,val osVersion: String,val appVersion: String,val timezone: String = TimeZone.getDefault().id
)interface BootTimeApiService {@POST("boot-times")suspend fun uploadBootTime(@Body dto: BootTimeDto): Response<Unit>@POST("boot-times/batch")suspend fun uploadBootTimes(@Body dtos: List<BootTimeDto>): Response<Unit>
}class ApiClient private constructor() {companion object {fun createBootTimeService(baseUrl: String): BootTimeApiService {val retrofit = Retrofit.Builder().baseUrl(baseUrl).addConverterFactory(MoshiConverterFactory.create()).client(createHttpClient()).build()return retrofit.create(BootTimeApiService::class.java)}private fun createHttpClient(): OkHttpClient {return OkHttpClient.Builder().connectTimeout(30, TimeUnit.SECONDS).readTimeout(30, TimeUnit.SECONDS).writeTimeout(30, TimeUnit.SECONDS).addInterceptor(LoggingInterceptor()).addInterceptor(AuthInterceptor()).build()}}
}class LoggingInterceptor : Interceptor {override fun intercept(chain: Interceptor.Chain): Response {val request = chain.request()// 添加日志逻辑return chain.proceed(request)}
}class AuthInterceptor : Interceptor {override fun intercept(chain: Interceptor.Chain): Response {val request = chain.request().newBuilder().addHeader("Authorization", "Bearer your_token_here").addHeader("Content-Type", "application/json").build()return chain.proceed(request)}
}
  1. 依赖注入(可选,使用Hilt)
@Module
@InstallIn(SingletonComponent::class)
object AppModule {@Provides@Singletonfun provideBootTimeDatabase(@ApplicationContext context: Context): BootTimeDatabase {return BootTimeDatabase.getInstance(context)}@Provides@Singletonfun provideBootTimeDao(database: BootTimeDatabase): BootTimeDao {return database.bootTimeDao()}@Provides@Singletonfun provideBootTimeManager(@ApplicationContext context: Context): BootTimeManager {return BootTimeManager.getInstance(context)}@Provides@Singletonfun provideBootTimeApiService(): BootTimeApiService {return ApiClient.createBootTimeService("https://your-api-url.com/")}
}@HiltAndroidApp
class BootTimeApplication : Application()

Gradle依赖配置

// build.gradle.kts (Module)
dependencies {// Roomimplementation("androidx.room:room-runtime:2.6.0")implementation("androidx.room:room-ktx:2.6.0")ksp("androidx.room:room-compiler:2.6.0")// WorkManagerimplementation("androidx.work:work-runtime-ktx:2.8.1")// Coroutinesimplementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3")// Retrofitimplementation("com.squareup.retrofit2:retrofit:2.9.0")implementation("com.squareup.retrofit2:converter-moshi:2.9.0")implementation("com.squareup.okhttp3:logging-interceptor:4.11.0")// Hilt (可选)implementation("com.google.dagger:hilt-android:2.48.1")ksp("com.google.dagger:hilt-compiler:2.48.1")// Timber (日志)implementation("com.jakewharton.timber:timber:5.0.1")
}

商业级特性

  1. 性能优化: 使用协程异步处理,避免阻塞主线程
  2. 稳定性: WorkManager自动重试,处理网络异常
  3. 数据持久化: Room数据库确保数据不丢失
  4. 电量友好: 批量上传,智能调度
  5. 隐私合规: 设备ID匿名化处理
  6. 错误处理: 完善的异常捕获和重试机制
  7. 可扩展性: 模块化设计,易于扩展功能

这个Kotlin实现采用了现代Android开发的最佳实践,包括协程、Room、WorkManager等组件,确保了代码的简洁性、可维护性和商业级的可靠性。


文章转载自:

http://xXjBjzNn.qncqd.cn
http://4yKAhgaY.qncqd.cn
http://RhFEtaBp.qncqd.cn
http://cvo3npwQ.qncqd.cn
http://kGUkUKEd.qncqd.cn
http://SDPl0pGm.qncqd.cn
http://DPRevq8R.qncqd.cn
http://X6qaRol5.qncqd.cn
http://TmLjRTOl.qncqd.cn
http://XrLhVuoY.qncqd.cn
http://MWzeX1HD.qncqd.cn
http://NnHVUaYi.qncqd.cn
http://tscLJAyg.qncqd.cn
http://9dzOW21v.qncqd.cn
http://UvjBdL2M.qncqd.cn
http://HM6VWfO1.qncqd.cn
http://1R6nvmPy.qncqd.cn
http://G5dsQ4Ky.qncqd.cn
http://9WUUMg0f.qncqd.cn
http://gZvit2I7.qncqd.cn
http://3ywtTzmk.qncqd.cn
http://79QgOJ1H.qncqd.cn
http://QK0RqA2q.qncqd.cn
http://YAGbMUWt.qncqd.cn
http://4P29kbY6.qncqd.cn
http://jhqZqlS5.qncqd.cn
http://BL11HyF6.qncqd.cn
http://oPXE1tlS.qncqd.cn
http://efBePIHu.qncqd.cn
http://CcXQi9WR.qncqd.cn
http://www.dtcms.com/a/375080.html

相关文章:

  • 串口数据收发的设计
  • 基于Nginx实现反向代理、负载均衡与动静分离完整部署指南
  • Excel 表格 - Excel 单元格添加边框
  • 产品无法正确解析复杂表格和流程图,有什么替代方案或优化方法?
  • C++ -- 模板
  • C# ObjectListView实现树状文件夹浏览
  • 高级 RAG 技术原理和前沿进展
  • 42.Shell脚本判断和if语句及相关案例
  • Game Runtime Libraries Package 解决游戏运行的痛点困境
  • 《P3825 [NOI2017] 游戏》
  • 第三课、Cocos Creator 项目创建与目录结构详解
  • C#中的浅拷贝与深拷贝
  • docker 整理几个常用的指令
  • Git上有更新而本地无更新时的解决方案
  • Doc2X为一切AI文档服务的基础设施,将PDF转换为Word、HTML、LaTeX、Markdown等
  • k8s 内置的containerd配置阿里云个人镜像地址及认证
  • 新节点加入k8s集群命令查看
  • 在 PostgreSQL中查看有哪些用户
  • 【从零开始的大模型原理与实践教程】--第一章:NLP基础概念
  • 零侵入式对接美团核销接口的技术合作模式
  • Kafka面试精讲 Day 14:集群扩容与数据迁移
  • 解耦-IOCDI
  • 【秋招笔试】2025.09.07蚂蚁算法岗笔试题
  • 10月17日,博睿数据受邀出席GOPS 全球运维大会 2025 · 上海站!
  • 第三方软件测评机构:MongoDB分片集群写入吞吐量与延迟第三方性能测评
  • 【硬件-笔试面试题-76】硬件/电子工程师,笔试面试题(知识点:H桥驱动电路的设计要点)
  • 【56页PPT】数字孪生智能工厂总体结构技术架构MES+ERP建设方案(附下载方式)
  • type(类型别名)和 interface的区别和最佳实践
  • 【直流电机鲁棒控制】matlab实现H无穷大控制的直流电机鲁棒控制研究
  • 4 C 语言数据结构实战:栈和队列完整实现(结构体 + 函数)+ 最小栈解决方案