屏蔽ptt
This commit is contained in:
parent
2eaba957d0
commit
0e5883d0de
|
|
@ -13,21 +13,20 @@ class MyApplication : Application() {
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCreate() {
|
override fun onCreate() {
|
||||||
|
// 第一步:先初始化日志,哪怕后面的代码崩了,这里的初始化也完成了
|
||||||
|
LogManager.init(this)
|
||||||
|
|
||||||
super.onCreate()
|
super.onCreate()
|
||||||
instance = this
|
instance = this
|
||||||
|
|
||||||
// Initialize Timber and File Logging
|
Timber.tag("MyApplication").i("=== Application Starting ===")
|
||||||
LogManager.init(this)
|
|
||||||
|
|
||||||
try {
|
// try {
|
||||||
// Initialize PTT Module
|
// // 第二步:异步或保护性初始化 PTT
|
||||||
PTTMyApplication.init(this)
|
// PTTMyApplication.init(this)
|
||||||
Timber.tag("MyApplication").i("PTT Module init sequence finished")
|
// Timber.tag("MyApplication").i("PTT Module init sequence finished")
|
||||||
} catch (e: Throwable) {
|
// } catch (e: Throwable) {
|
||||||
// Catch UnsatisfiedLinkError or other PTT init failures
|
// Timber.tag("MyApplication").e(e, "PTT Module CRASH during init")
|
||||||
Timber.tag("MyApplication").e(e, "PTT Module failed to start (Expected on non-ARM simulators)")
|
// }
|
||||||
}
|
|
||||||
|
|
||||||
Timber.tag("MyApplication").i("Application onCreate - Logging system ready")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,28 +6,65 @@ import okhttp3.*
|
||||||
import okhttp3.MediaType.Companion.toMediaTypeOrNull
|
import okhttp3.MediaType.Companion.toMediaTypeOrNull
|
||||||
import okhttp3.RequestBody.Companion.asRequestBody
|
import okhttp3.RequestBody.Companion.asRequestBody
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import java.io.File
|
import java.io.*
|
||||||
import java.io.FileInputStream
|
|
||||||
import java.io.FileOutputStream
|
|
||||||
import java.io.FileWriter
|
|
||||||
import java.io.IOException
|
|
||||||
import java.text.SimpleDateFormat
|
import java.text.SimpleDateFormat
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import java.util.concurrent.Executors
|
import java.util.concurrent.Executors
|
||||||
import java.util.zip.ZipEntry
|
import java.util.zip.ZipEntry
|
||||||
import java.util.zip.ZipOutputStream
|
import java.util.zip.ZipOutputStream
|
||||||
|
import kotlin.system.exitProcess
|
||||||
|
|
||||||
object LogManager {
|
object LogManager {
|
||||||
private val executor = Executors.newSingleThreadExecutor()
|
private val executor = Executors.newSingleThreadExecutor()
|
||||||
private const val MAX_DAYS = 15
|
private const val MAX_DAYS = 15
|
||||||
private val client = OkHttpClient()
|
private val client = OkHttpClient()
|
||||||
|
private var logDir: File? = null
|
||||||
|
|
||||||
fun init(context: Context) {
|
fun init(context: Context) {
|
||||||
Timber.plant(Timber.DebugTree())
|
Timber.plant(Timber.DebugTree())
|
||||||
val logDir = File(context.getExternalFilesDir(null), "logs")
|
|
||||||
if (!logDir.exists()) logDir.mkdirs()
|
try {
|
||||||
cleanOldLogs(logDir)
|
logDir = File(context.getExternalFilesDir(null), "logs")
|
||||||
Timber.plant(FileLoggingTree(logDir))
|
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) {
|
private fun cleanOldLogs(logDir: File) {
|
||||||
|
|
@ -46,12 +83,12 @@ object LogManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
fun zipLogs(context: Context): String? {
|
fun zipLogs(context: Context): String? {
|
||||||
val logDir = File(context.getExternalFilesDir(null), "logs")
|
val dir = logDir ?: return null
|
||||||
if (!logDir.exists() || logDir.listFiles().isNullOrEmpty()) return null
|
if (!dir.exists() || dir.listFiles().isNullOrEmpty()) return null
|
||||||
val zipFile = File(context.getExternalFilesDir(null), "logs_${System.currentTimeMillis()}.zip")
|
val zipFile = File(context.getExternalFilesDir(null), "logs_${System.currentTimeMillis()}.zip")
|
||||||
return try {
|
return try {
|
||||||
ZipOutputStream(FileOutputStream(zipFile)).use { zos ->
|
ZipOutputStream(FileOutputStream(zipFile)).use { zos ->
|
||||||
logDir.listFiles()?.forEach { file ->
|
dir.listFiles()?.forEach { file ->
|
||||||
if (file.isFile && file.name.endsWith(".log")) {
|
if (file.isFile && file.name.endsWith(".log")) {
|
||||||
zos.putNextEntry(ZipEntry(file.name))
|
zos.putNextEntry(ZipEntry(file.name))
|
||||||
FileInputStream(file).use { it.copyTo(zos) }
|
FileInputStream(file).use { it.copyTo(zos) }
|
||||||
|
|
@ -66,9 +103,6 @@ object LogManager {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 上传日志到服务器
|
|
||||||
*/
|
|
||||||
fun uploadLogs(context: Context, callback: (Boolean, String) -> Unit) {
|
fun uploadLogs(context: Context, callback: (Boolean, String) -> Unit) {
|
||||||
executor.execute {
|
executor.execute {
|
||||||
val zipPath = zipLogs(context)
|
val zipPath = zipLogs(context)
|
||||||
|
|
@ -80,8 +114,6 @@ object LogManager {
|
||||||
val file = File(zipPath)
|
val file = File(zipPath)
|
||||||
val serverUrl = AppConfig.getServerUrl(context)
|
val serverUrl = AppConfig.getServerUrl(context)
|
||||||
val uploadUrl = "$serverUrl/api/attachment/upload"
|
val uploadUrl = "$serverUrl/api/attachment/upload"
|
||||||
|
|
||||||
// 使用固定的 Token (根据用户提供)
|
|
||||||
val bearerToken = "eyJraWQiOiJzLXYxIiwidHlwIjoiSldUIiwiYWxnIjoiSFMyNTYifQ.eyJzdWIiOiJhZG1pbiIsInR5cCI6ImFjY2VzcyIsImV4cCI6MTc3NjQ5MDczNSwianRpIjoiMjYzNjljYzQtYzJiMy00YTg0LWFiYTMtZDU4NmNiZDRmMmM0IiwiZmdwIjoiY2FhNzYzOTI4YWRhYWZiYjgzMjE3NjExYmVjYTc3ZjMiLCJraWQiOiJzLXYxIn0.AyuzNAe-R_GOBC65STH5qpHkmmUngX8rMBPI4CdIIhk"
|
val bearerToken = "eyJraWQiOiJzLXYxIiwidHlwIjoiSldUIiwiYWxnIjoiSFMyNTYifQ.eyJzdWIiOiJhZG1pbiIsInR5cCI6ImFjY2VzcyIsImV4cCI6MTc3NjQ5MDczNSwianRpIjoiMjYzNjljYzQtYzJiMy00YTg0LWFiYTMtZDU4NmNiZDRmMmM0IiwiZmdwIjoiY2FhNzYzOTI4YWRhYWZiYjgzMjE3NjExYmVjYTc3ZjMiLCJraWQiOiJzLXYxIn0.AyuzNAe-R_GOBC65STH5qpHkmmUngX8rMBPI4CdIIhk"
|
||||||
|
|
||||||
val requestBody = MultipartBody.Builder()
|
val requestBody = MultipartBody.Builder()
|
||||||
|
|
@ -99,7 +131,6 @@ object LogManager {
|
||||||
client.newCall(request).execute().use { response ->
|
client.newCall(request).execute().use { response ->
|
||||||
if (response.isSuccessful) {
|
if (response.isSuccessful) {
|
||||||
callback(true, "上传成功")
|
callback(true, "上传成功")
|
||||||
// 上传成功后删除临时 zip 文件
|
|
||||||
file.delete()
|
file.delete()
|
||||||
} else {
|
} else {
|
||||||
callback(false, "服务器返回错误: ${response.code}")
|
callback(false, "服务器返回错误: ${response.code}")
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue