package com.hzpd.jwztc.service
import android.content.Context
import android.os.Environment
import android.util.Log
import chengying.com.core.util.TimeUtils
import chengying.com.core.util.TimeUtils.format1
import chengying.com.core.util.getAppVersion
import com.google.gson.Gson
import com.hzpd.jwztc.service.api.ErrorMsgService
import com.hzpd.jwztc.service.beans.request.AddWarnInfoReq
import com.hzpd.jwztc.service.beans.request.ErrorInfoReq
import com.hzpd.jwztc.service.beans.request.WarnInfo
import com.hzpd.jwztc.service.control.NetUtils
import java.io.*
import java.io.File.separator
import java.text.SimpleDateFormat
import java.util.concurrent.CountDownLatch
/**
* Created by yangfan
* nrainyseason@163.com
* 异常全局捕获
*/
class CrashHandler private constructor() : Exception(), Thread.UncaughtExceptionHandler {
private var context: Context? = null
private val tag = javaClass.simpleName
private var mDefaultCrashHandler: Thread.UncaughtExceptionHandler? = null
private var errorSrc: ErrorMsgService? = null
private var countDownLunch: CountDownLatch? = null
companion object {
private var instance: CrashHandler? = null
get() {
if (field == null) {
field = CrashHandler()
}
return field
}
@Synchronized
fun get(): CrashHandler {
return instance!!
}
}
fun init(context: Context) {
this.context = context
errorSrc = ErrorMsgService(context!!)
mDefaultCrashHandler = Thread.getDefaultUncaughtExceptionHandler()
Thread.setDefaultUncaughtExceptionHandler(this)
}
override fun uncaughtException(thread: Thread, ex: Throwable) {
Log.e(tag, "uncaughtException")
//如果异常没有被处理
if (!handleException(ex) && mDefaultCrashHandler != null) {
//进行异常捕获
mDefaultCrashHandler?.uncaughtException(thread, ex);
}
}
//上传服务器
fun uploadToServer(ex: Throwable) {
var crashReport = getCrashReport(ex)
Log.e(tag, "crashReport" + crashReport)
if (crashReport.isNullOrEmpty()) return
var warnInfo = WarnInfo().apply {
this.detail = AddWarnInfoReq().apply {
this.clientOs = "Android: ${android.os.Build.VERSION.RELEASE}"
this.clientAppVer = getAppVersion(context!!)
this.clientType="${android.os.Build.BRAND} \r ${android.os.Build.MODEL}"
this.warnTrack = crashReport
this.netWork="${getNetState(NetUtils.getNetworkState(context!!))}"
}
this.warnTitle=getWarnTitle(ex)
this.createTime = TimeUtils.getDateToString3(System.currentTimeMillis(), format1)
this.warnType="6"
this.warnFrom= "Android"
this.warnLevel="5"
}
errorSrc?.saveErrorInfo(ErrorInfoReq().apply { this.warnInfo = Gson().toJson(warnInfo) }) {
it?.let {
if (it.code == 200) {
Log.e(tag, "upload success")
}
countDownLunch!!.countDown()
}
}
}
/**
* 获取异常title
*/
fun getWarnTitle(ex: Throwable):String{
var array=ex.javaClass.name.split(".")
if(array.size>0)return array[array.size-1]
return "UnKnowException"
}
fun getNetState(state:Int):String{
when(state){
NetUtils.NETWORK_2G->return "2G"
NetUtils.NETWORK_3G->return "3G"
NetUtils.NETWORK_4G->return "4G"
NetUtils.NETWORK_WIFI->return "wifi"
else->return "无网络"
}
}
private fun handleException(ex: Throwable): Boolean {
if (ex == null) {
return false
}
countDownLunch= CountDownLatch(1)
var crashReport = getCrashReport(ex)
Log.e(tag, "handleException: ${crashReport}")
// saveInfoToSD(crashReport)
uploadToServer(ex)
countDownLunch!!.await()
return true
}
/**
* @Description 获取APP崩溃异常报告
*
* @param Context
* context(上下文), Throwable ex(异常信息)
* @return String
*/
private fun getCrashReport(ex: Throwable): String {
var stringBuilder = StringBuilder()
stringBuilder.append("Android: ${android.os.Build.VERSION.RELEASE}(BRAND: ${android.os.Build.BRAND} MODEL: ${android.os.Build.MODEL})\n")//版本
.append("Exception: ${ex.message}\n")//异常信息
var elements = ex.stackTrace
elements.forEach {
stringBuilder.append("${it}\n")
}
return stringBuilder.toString()
}
private fun saveInfoToSD(str: String): String? {
var fileName: String? = null
if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
val dir = File("${context?.getFilesDir()}${separator} crash ${separator}")
if (dir.exists()) {
deleteDir(dir)
}
if (!dir.exists()) {
dir.mkdir()
}
try {
fileName = dir.toString() + File.separator + getAssignTime("yyyy_MM_dd_HH_mm") + ".txt"
val fos = FileOutputStream(fileName)
fos.write(str.toByteArray())
fos.flush()
fos.close()
} catch (e: Exception) {
e.printStackTrace()
}
}
return fileName
}
private fun deleteDir(dir: File): Boolean {
if (dir.isDirectory) {
val children = dir.listFiles()
// 递归删除目录中的子目录下
for (child in children!!) {
child.delete()
}
}
// 目录此时为空,可以删除
return true
}
private fun cacheCrashFile(fileName: String) {
val sp = context?.getSharedPreferences("crash", Context.MODE_PRIVATE)
sp?.edit()?.putString("CRASH_FILE_NAME", fileName)?.commit()
}
fun getCrashFile(): String {
return context?.getSharedPreferences("crash", Context.MODE_PRIVATE)?.getString("CRASH_FILE_NAME", "")
?: ""
}
private fun getAssignTime(dateFormatStr: String): String {
val dataFormat = SimpleDateFormat(dateFormatStr)
val currentTime = System.currentTimeMillis()
return dataFormat.format(currentTime)
}
fun getCrashReport(): String {
var fileName = getCrashFile()
if (fileName.isNullOrEmpty()) return ""
var crashFile = File(fileName)
var fis: FileInputStream? = null
try {
fis = FileInputStream(crashFile)
val buffer = ByteArray(1024)
val sb = StringBuilder()
while (fis!!.read(buffer) !== -1) {
sb.append(String(buffer))
}
return sb.toString()
} catch (e: FileNotFoundException) {
e.printStackTrace()
} catch (e: IOException) {
e.printStackTrace()
} finally {
if (fis != null) {
fis!!.close()
}
}
return ""
}
}
在application中调用
//全局异常捕获
CrashHandler.get().init(applicationContext)

本文介绍了一个用于Android应用的全局异常捕获机制,通过自定义CrashHandler类实现未捕获异常的处理,包括收集设备信息、异常堆栈信息,并将其上传至服务器。此外,还介绍了如何在应用启动时初始化此异常处理机制。
3035

被折叠的 条评论
为什么被折叠?



