software copyright
This commit is contained in:
@@ -0,0 +1,204 @@
|
||||
/**
|
||||
* 自然写互动课堂电视端应用软件 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, "应用已终止")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user