diff --git a/app/src/main/java/io/legado/app/App.kt b/app/src/main/java/io/legado/app/App.kt index 7994ac212..c72c5133c 100644 --- a/app/src/main/java/io/legado/app/App.kt +++ b/app/src/main/java/io/legado/app/App.kt @@ -15,6 +15,7 @@ import io.legado.app.constant.AppConst.channelIdReadAloud import io.legado.app.constant.AppConst.channelIdWeb import io.legado.app.data.AppDatabase import io.legado.app.help.ActivityHelp +import io.legado.app.help.CrashHandler import io.legado.app.lib.theme.ThemeStore import io.legado.app.utils.getCompatColor import io.legado.app.utils.getPrefInt @@ -39,7 +40,7 @@ class App : Application() { override fun onCreate() { super.onCreate() INSTANCE = this - + CrashHandler().init(this) db = AppDatabase.createDatabase(INSTANCE) packageManager.getPackageInfo(packageName, 0)?.let { versionCode = it.versionCode diff --git a/app/src/main/java/io/legado/app/help/CrashHandler.kt b/app/src/main/java/io/legado/app/help/CrashHandler.kt new file mode 100644 index 000000000..59623a96d --- /dev/null +++ b/app/src/main/java/io/legado/app/help/CrashHandler.kt @@ -0,0 +1,150 @@ +package io.legado.app.help + +import android.annotation.SuppressLint +import android.content.Context +import android.content.pm.PackageManager +import android.os.Build +import android.os.Handler +import android.os.Looper +import android.util.Log +import android.widget.Toast +import java.io.File +import java.io.FileOutputStream +import java.io.PrintWriter +import java.io.StringWriter +import java.text.SimpleDateFormat +import java.util.* + +/** + * 异常管理类 + */ +class CrashHandler : Thread.UncaughtExceptionHandler { + private val tag = this.javaClass.simpleName + /** + * 系统默认UncaughtExceptionHandler + */ + private var mDefaultHandler: Thread.UncaughtExceptionHandler? = null + + /** + * context + */ + private var mContext: Context? = null + + /** + * 存储异常和参数信息 + */ + private val paramsMap = HashMap() + + /** + * 格式化时间 + */ + @SuppressLint("SimpleDateFormat") + private val format = SimpleDateFormat("yyyy-MM-dd-HH-mm-ss") + + fun init(context: Context) { + mContext = context + mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler() + //设置该CrashHandler为系统默认的 + Thread.setDefaultUncaughtExceptionHandler(this) + } + + /** + * uncaughtException 回调函数 + */ + override fun uncaughtException(thread: Thread, ex: Throwable) { + handleException(ex) + mDefaultHandler?.uncaughtException(thread, ex) + } + + /** + * 处理该异常 + */ + private fun handleException(ex: Throwable?) { + if (ex == null) return + //收集设备参数信息 + collectDeviceInfo(mContext!!) + //添加自定义信息 + addCustomInfo() + kotlin.runCatching { + //使用Toast来显示异常信息 + Handler(Looper.getMainLooper()).post { + Toast.makeText( + mContext, + ex.message, + Toast.LENGTH_LONG + ).show() + } + } + //保存日志文件 + saveCrashInfo2File(ex) + } + + /** + * 收集设备参数信息 + */ + private fun collectDeviceInfo(ctx: Context) { + //获取versionName,versionCode + kotlin.runCatching { + val pm = ctx.packageManager + val pi = pm.getPackageInfo(ctx.packageName, PackageManager.GET_ACTIVITIES) + if (pi != null) { + val versionName = if (pi.versionName == null) "null" else pi.versionName + val versionCode = pi.versionCode.toString() + "" + paramsMap["versionName"] = versionName + paramsMap["versionCode"] = versionCode + } + } + + //获取所有系统信息 + val fields = Build::class.java.declaredFields + kotlin.runCatching { + for (field in fields) { + field.isAccessible = true + paramsMap[field.name] = field.get(null).toString() + } + } + } + + /** + * 添加自定义参数 + */ + private fun addCustomInfo() { + Log.i(tag, "addCustomInfo: 程序出错了...") + } + + /** + * 保存错误信息到文件中 + */ + private fun saveCrashInfo2File(ex: Throwable) { + kotlin.runCatching { + val sb = StringBuilder() + for ((key, value) in paramsMap) { + sb.append(key).append("=").append(value).append("\n") + } + + val writer = StringWriter() + val printWriter = PrintWriter(writer) + ex.printStackTrace(printWriter) + var cause: Throwable? = ex.cause + while (cause != null) { + cause.printStackTrace(printWriter) + cause = cause.cause + } + printWriter.close() + val result = writer.toString() + sb.append(result) + val timestamp = System.currentTimeMillis() + val time = format.format(Date()) + val fileName = "crash-$time-$timestamp.log" + val path = mContext?.externalCacheDir?.toString() + "/crash/" + val dir = File(path) + if (!dir.exists()) { + dir.mkdirs() + } + val fos = FileOutputStream(path + fileName) + fos.write(sb.toString().toByteArray()) + fos.close() + } + } + +}