这是全局异常抓包工具类,在实际工具中应该会用得上,一发生异常闪退就会把日志写入到本地日志文件
具体代码
package com.purui.mobile.utils
import android.content.Context
import android.content.pm.PackageManager
import android.os.Build
import android.os.Environment
import android.os.Looper
import android.os.Process
import android.util.Log
import com.purui.mobile.PuruiApplication
import com.purui.mobile.base.toast
import java.io.File
import java.io.PrintWriter
import java.io.StringWriter
import java.io.Writer
import java.text.DateFormat
import java.text.SimpleDateFormat
import java.util.Locale
class CrashHandlerUtil private constructor() : Thread.UncaughtExceptionHandler {
companion object {
const val TAG = "CrashHandlerUtil"
//CrashHandler实例
val instance by lazy { CrashHandlerUtil() }
}
//系统默认的UncaughtException处理类
private var mDefaultHandler: Thread.UncaughtExceptionHandler? = null
//用来存储设备信息和异常信息
private val infos: MutableMap<String, String> = HashMap()
fun init() {
//获取系统默认的UncaughtException处理器
mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler()
//设置该CrashHandler为程序的默认处理器
Thread.setDefaultUncaughtExceptionHandler(this)
}
/**
* 当UncaughtException发生时会转入该函数来处理
*
* @param thread 线程
* @param ex 异常
*/
override fun uncaughtException(thread: Thread, ex: Throwable) {
if (!handleException(ex) && mDefaultHandler != null) {
//如果用户没有处理则让系统默认的异常处理器来处理
mDefaultHandler?.uncaughtException(thread, ex)
} else {
try {
Thread.sleep(3000)
} catch (e: InterruptedException) {
Log.e("error : ", e.toString())
e.printStackTrace()
}
//退出程序
//退出JVM(java虚拟机),释放所占内存资源,0表示正常退出(非0的都为异常退出)
System.exit(0)
//从操作系统中结束掉当前程序的进程
Process.killProcess(Process.myPid())
}
}
/**
* 自定义错误处理,收集错误信息 发送错误报告等操作均在此完成.
*
* @param throwable 异常
* @return true:如果处理了该异常信息;否则返回false.
*/
private fun handleException(throwable: Throwable?): Boolean {
if (throwable == null) {
return false
}
//使用Toast来显示异常信息
object : Thread() {
override fun run() {
Looper.prepare()
throwable.printStackTrace()
Looper.loop()
}
}.start()
//收集设备参数信息
collectDeviceInfo(PuruiApplication.instance)
//保存日志文件
saveCrashInfo2File(throwable)
return true
}
/**
* 收集设备参数信息
*
* @param ctx 上下文
*/
private fun collectDeviceInfo(ctx: Context?) {
try {
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() + ""
infos["versionName"] = versionName
infos["versionCode"] = versionCode
}
} catch (e: PackageManager.NameNotFoundException) {
Log.e("ttt", "an error occured when collect package info:$e")
}
val fields = Build::class.java.declaredFields
for (field in fields) {
try {
field.isAccessible = true
infos[field.name] = field[null].toString()
// Logger.e(field.getName() + " : " + field.get(null));
} catch (e: Exception) {
Log.e("ttt", "an error occured when collect crash info:$e")
}
}
}
/**
* 保存错误信息到文件中
* @param ex 异常
* @return 返回文件名称, 便于将文件传送到服务器
*/
private fun saveCrashInfo2File(ex: Throwable): String? {
val sb = StringBuffer()
for ((key, value) in infos) {
sb.append("$key=$value\n")
}
val writer: Writer = StringWriter()
val printWriter = PrintWriter(writer)
ex.printStackTrace(printWriter)
var cause = ex.cause
while (cause != null) {
cause.printStackTrace(printWriter)
cause = cause.cause
}
printWriter.close()
val result = writer.toString()
sb.append(result)
val file = File(PuruiApplication.instance.getExternalFilesDir(""), "purui_crash.txt")
file.writeText(sb.toString())
Log.e("ttt", sb.toString())
return null
}
}