屏蔽ptt
This commit is contained in:
parent
2eaba957d0
commit
0e5883d0de
|
|
@ -13,21 +13,20 @@ class MyApplication : Application() {
|
|||
}
|
||||
|
||||
override fun onCreate() {
|
||||
// 第一步:先初始化日志,哪怕后面的代码崩了,这里的初始化也完成了
|
||||
LogManager.init(this)
|
||||
|
||||
super.onCreate()
|
||||
instance = this
|
||||
|
||||
// Initialize Timber and File Logging
|
||||
LogManager.init(this)
|
||||
|
||||
try {
|
||||
// Initialize PTT Module
|
||||
PTTMyApplication.init(this)
|
||||
Timber.tag("MyApplication").i("PTT Module init sequence finished")
|
||||
} catch (e: Throwable) {
|
||||
// Catch UnsatisfiedLinkError or other PTT init failures
|
||||
Timber.tag("MyApplication").e(e, "PTT Module failed to start (Expected on non-ARM simulators)")
|
||||
}
|
||||
|
||||
Timber.tag("MyApplication").i("Application onCreate - Logging system ready")
|
||||
Timber.tag("MyApplication").i("=== Application Starting ===")
|
||||
|
||||
// try {
|
||||
// // 第二步:异步或保护性初始化 PTT
|
||||
// PTTMyApplication.init(this)
|
||||
// Timber.tag("MyApplication").i("PTT Module init sequence finished")
|
||||
// } catch (e: Throwable) {
|
||||
// Timber.tag("MyApplication").e(e, "PTT Module CRASH during init")
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,28 +6,65 @@ import okhttp3.*
|
|||
import okhttp3.MediaType.Companion.toMediaTypeOrNull
|
||||
import okhttp3.RequestBody.Companion.asRequestBody
|
||||
import timber.log.Timber
|
||||
import java.io.File
|
||||
import java.io.FileInputStream
|
||||
import java.io.FileOutputStream
|
||||
import java.io.FileWriter
|
||||
import java.io.IOException
|
||||
import java.io.*
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.*
|
||||
import java.util.concurrent.Executors
|
||||
import java.util.zip.ZipEntry
|
||||
import java.util.zip.ZipOutputStream
|
||||
import kotlin.system.exitProcess
|
||||
|
||||
object LogManager {
|
||||
private val executor = Executors.newSingleThreadExecutor()
|
||||
private const val MAX_DAYS = 15
|
||||
private val client = OkHttpClient()
|
||||
private var logDir: File? = null
|
||||
|
||||
fun init(context: Context) {
|
||||
Timber.plant(Timber.DebugTree())
|
||||
val logDir = File(context.getExternalFilesDir(null), "logs")
|
||||
if (!logDir.exists()) logDir.mkdirs()
|
||||
cleanOldLogs(logDir)
|
||||
Timber.plant(FileLoggingTree(logDir))
|
||||
|
||||
try {
|
||||
logDir = File(context.getExternalFilesDir(null), "logs")
|
||||
if (logDir?.exists() == false) logDir?.mkdirs()
|
||||
|
||||
// 立即清理旧日志
|
||||
logDir?.let { cleanOldLogs(it) }
|
||||
|
||||
// 挂载文件日志树
|
||||
logDir?.let { Timber.plant(FileLoggingTree(it)) }
|
||||
|
||||
// 【核心增强】接管全局异常,确保崩溃信息能写进文件
|
||||
setupCrashHandler()
|
||||
|
||||
Timber.tag("LogManager").i("Logging system initialized and CrashHandler attached")
|
||||
} catch (e: Exception) {
|
||||
android.util.Log.e("LogManager", "Init failed", e)
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupCrashHandler() {
|
||||
val defaultHandler = Thread.getDefaultUncaughtExceptionHandler()
|
||||
Thread.setDefaultUncaughtExceptionHandler { thread, throwable ->
|
||||
// 强制同步写入崩溃日志
|
||||
val timestamp = SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS", Locale.getDefault()).format(Date())
|
||||
val crashInfo = "\n\n=== FATAL CRASH ===\n" +
|
||||
"Time: $timestamp\n" +
|
||||
"Thread: ${thread.name}\n" +
|
||||
"Stacktrace:\n${throwable.stackTraceToString()}\n" +
|
||||
"====================\n\n"
|
||||
|
||||
logDir?.let { dir ->
|
||||
val logFile = File(dir, SimpleDateFormat("yyyy-MM-dd", Locale.getDefault()).format(Date()) + ".log")
|
||||
try {
|
||||
FileOutputStream(logFile, true).use { it.write(crashInfo.toByteArray()) }
|
||||
} catch (e: Exception) {
|
||||
android.util.Log.e("LogManager", "Failed to write crash log", e)
|
||||
}
|
||||
}
|
||||
|
||||
// 交还给系统处理(弹出“应用已停止”对话框)
|
||||
defaultHandler?.uncaughtException(thread, throwable) ?: exitProcess(1)
|
||||
}
|
||||
}
|
||||
|
||||
private fun cleanOldLogs(logDir: File) {
|
||||
|
|
@ -46,12 +83,12 @@ object LogManager {
|
|||
}
|
||||
|
||||
fun zipLogs(context: Context): String? {
|
||||
val logDir = File(context.getExternalFilesDir(null), "logs")
|
||||
if (!logDir.exists() || logDir.listFiles().isNullOrEmpty()) return null
|
||||
val dir = logDir ?: return null
|
||||
if (!dir.exists() || dir.listFiles().isNullOrEmpty()) return null
|
||||
val zipFile = File(context.getExternalFilesDir(null), "logs_${System.currentTimeMillis()}.zip")
|
||||
return try {
|
||||
ZipOutputStream(FileOutputStream(zipFile)).use { zos ->
|
||||
logDir.listFiles()?.forEach { file ->
|
||||
dir.listFiles()?.forEach { file ->
|
||||
if (file.isFile && file.name.endsWith(".log")) {
|
||||
zos.putNextEntry(ZipEntry(file.name))
|
||||
FileInputStream(file).use { it.copyTo(zos) }
|
||||
|
|
@ -66,9 +103,6 @@ object LogManager {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 上传日志到服务器
|
||||
*/
|
||||
fun uploadLogs(context: Context, callback: (Boolean, String) -> Unit) {
|
||||
executor.execute {
|
||||
val zipPath = zipLogs(context)
|
||||
|
|
@ -80,8 +114,6 @@ object LogManager {
|
|||
val file = File(zipPath)
|
||||
val serverUrl = AppConfig.getServerUrl(context)
|
||||
val uploadUrl = "$serverUrl/api/attachment/upload"
|
||||
|
||||
// 使用固定的 Token (根据用户提供)
|
||||
val bearerToken = "eyJraWQiOiJzLXYxIiwidHlwIjoiSldUIiwiYWxnIjoiSFMyNTYifQ.eyJzdWIiOiJhZG1pbiIsInR5cCI6ImFjY2VzcyIsImV4cCI6MTc3NjQ5MDczNSwianRpIjoiMjYzNjljYzQtYzJiMy00YTg0LWFiYTMtZDU4NmNiZDRmMmM0IiwiZmdwIjoiY2FhNzYzOTI4YWRhYWZiYjgzMjE3NjExYmVjYTc3ZjMiLCJraWQiOiJzLXYxIn0.AyuzNAe-R_GOBC65STH5qpHkmmUngX8rMBPI4CdIIhk"
|
||||
|
||||
val requestBody = MultipartBody.Builder()
|
||||
|
|
@ -99,7 +131,6 @@ object LogManager {
|
|||
client.newCall(request).execute().use { response ->
|
||||
if (response.isSuccessful) {
|
||||
callback(true, "上传成功")
|
||||
// 上传成功后删除临时 zip 文件
|
||||
file.delete()
|
||||
} else {
|
||||
callback(false, "服务器返回错误: ${response.code}")
|
||||
|
|
|
|||
Loading…
Reference in New Issue