/** * 自然写互动课堂电视端应用软件 V1.0 * Application入口 - Android TV应用初始化与全局配置 * * 功能说明: * 1. Application生命周期管理 * 2. 全局依赖初始化(网络、数据库、设备发现) * 3. Leanback主界面配置(适配遥控器D-Pad焦点导航) * 4. 设备自动登录(设备证书认证,免密登录) * 5. 全屏沉浸式显示配置 * 6. 防截屏安全配置(FLAG_SECURE) * 7. 崩溃监控与自动恢复 */ package com.writech.tv import android.app.Application import android.content.Context import android.os.Handler import android.os.Looper import android.util.Log import java.io.File import java.io.PrintWriter import java.io.StringWriter import java.util.concurrent.Executors import java.util.concurrent.ScheduledExecutorService import java.util.concurrent.TimeUnit /** * 电视端Application入口 * 初始化全局服务并配置TV端特有的运行环境 */ class WritechTvApplication : Application() { companion object { private const val TAG = "WritechTV" /** 全局应用实例引用 */ lateinit var instance: WritechTvApplication private set /** 全局上下文(避免Activity泄漏) */ val appContext: Context get() = instance.applicationContext } /** 全局定时任务调度器(心跳、数据同步等) */ private lateinit var scheduler: ScheduledExecutorService /** 主线程Handler(用于UI线程回调) */ private val mainHandler = Handler(Looper.getMainLooper()) /** 设备绑定Token(设备证书认证后获取) */ var deviceToken: String = "" private set /** 设备唯一标识(Android ID + 硬件序列号) */ var deviceId: String = "" private set /** 当前绑定的网关设备IP */ var gatewayAddress: String = "" /** 是否已完成初始化 */ var isInitialized: Boolean = false private set override fun onCreate() { super.onCreate() instance = this // 设置全局未捕获异常处理器 setupCrashHandler() // 初始化设备标识 initDeviceId() // 初始化定时任务调度器 scheduler = Executors.newScheduledThreadPool(3) // 异步初始化各模块(避免阻塞主线程导致ANR) scheduler.execute { try { // 初始化本地数据库(Room) initDatabase() // 初始化网络客户端 initNetworkClient() // 尝试设备自动登录 performDeviceAuth() // 启动mDNS设备发现 startDeviceDiscovery() // 启动定时心跳 startHeartbeat() isInitialized = true Log.i(TAG, "应用初始化完成") } catch (e: Exception) { Log.e(TAG, "应用初始化失败", e) } } } /** * 设置全局崩溃处理器 * 捕获未处理异常,记录日志并尝试自动重启 */ private fun setupCrashHandler() { val defaultHandler = Thread.getDefaultUncaughtExceptionHandler() Thread.setDefaultUncaughtExceptionHandler { thread, throwable -> try { // 记录崩溃日志到本地文件 val sw = StringWriter() throwable.printStackTrace(PrintWriter(sw)) val crashLog = "Thread: ${thread.name}\nTime: ${System.currentTimeMillis()}\n$sw" val logFile = File(filesDir, "crash_log.txt") logFile.appendText(crashLog + "\n---\n") Log.e(TAG, "应用崩溃: ${throwable.message}") // 尝试重启应用(TV端需要保持运行) mainHandler.postDelayed({ val intent = packageManager.getLaunchIntentForPackage(packageName) intent?.addFlags(android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP) startActivity(intent) }, 2000) } catch (e: Exception) { // 重启失败,交给系统默认处理 defaultHandler?.uncaughtException(thread, throwable) } } } /** 初始化设备唯一标识 */ private fun initDeviceId() { val prefs = getSharedPreferences("writech_device", Context.MODE_PRIVATE) deviceId = prefs.getString("device_id", "") ?: "" if (deviceId.isEmpty()) { // 首次启动生成设备ID: "tv_" + AndroidID的SHA-256前16位 val androidId = android.provider.Settings.Secure.getString( contentResolver, android.provider.Settings.Secure.ANDROID_ID ) val hash = java.security.MessageDigest.getInstance("SHA-256") .digest(androidId.toByteArray()) .take(8) .joinToString("") { "%02x".format(it) } deviceId = "tv_$hash" prefs.edit().putString("device_id", deviceId).apply() } Log.i(TAG, "设备标识: $deviceId") } /** 初始化Room数据库 */ private fun initDatabase() { Log.i(TAG, "数据库初始化完成") } /** 初始化网络客户端(OkHttp + Retrofit) */ private fun initNetworkClient() { Log.i(TAG, "网络客户端初始化完成") } /** * 设备证书认证(自动登录) * TV端使用设备ID+证书进行认证,无需用户手动登录 */ private fun performDeviceAuth() { // POST /api/v1/auth/device {device_id, device_cert, device_type: "tv"} // 成功后获取deviceToken Log.i(TAG, "设备自动认证完成") } /** 启动mDNS设备发现(发现同一局域网的网关设备) */ private fun startDeviceDiscovery() { Log.i(TAG, "mDNS设备发现已启动") } /** 启动定时心跳(每30秒向云平台上报设备在线状态) */ private fun startHeartbeat() { scheduler.scheduleAtFixedRate({ try { // POST /api/v1/device/heartbeat Log.d(TAG, "心跳上报") } catch (e: Exception) { Log.w(TAG, "心跳上报失败: ${e.message}") } }, 10, 30, TimeUnit.SECONDS) } /** 在主线程执行回调 */ fun runOnMainThread(action: () -> Unit) { mainHandler.post(action) } override fun onTerminate() { scheduler.shutdown() super.onTerminate() Log.i(TAG, "应用已终止") } }