基本思路
统一应用所有崩溃异常入口,当应用崩溃的时候记录相关崩溃日志到本地并在合适时间上传至服务器;
相关实现
package com.crystal.view.exception
import android.content.Context
import android.content.pm.PackageManager
import android.os.Build
import android.os.Environment
import android.util.Log
import java.io.File
import java.io.FileOutputStream
import java.io.PrintWriter
import java.io.StringWriter
/**
* 自定义全局异常捕获类
* on 2022/11/17
*/
class ExceptionCrashHandler private constructor() : Thread.UncaughtExceptionHandler {
private lateinit var context: Context
private lateinit var defaultExceptionCrashHandler: Thread.UncaughtExceptionHandler
/**
* 双重DCL单例
*/
companion object {
val TAG = ExceptionCrashHandler::class.java.simpleName
val instance: ExceptionCrashHandler by lazy(mode = LazyThreadSafetyMode.SYNCHRONIZED) {
ExceptionCrashHandler()
}
}
/**
* 初始化
*/
fun init(context: Context) {
this.context = context
//设置全局的异常类为本类
Thread.currentThread().uncaughtExceptionHandler = this
//获取系统默认的异常处理
defaultExceptionCrashHandler =
Thread.getDefaultUncaughtExceptionHandler() as Thread.UncaughtExceptionHandler;
}
override fun uncaughtException(t: Thread, e: Throwable) {
Log.e(TAG, "此处有异常!!!")
//这里我们可以将崩溃信息保存到本地文件,找机会上传至服务器,如再次打开APP时遍历查找crash文件上传
saveExceptionInfoToLocal(e)
//让系统默认处理,不加会默认不抛异常
defaultExceptionCrashHandler.uncaughtException(t, e)
}
/**
* 记录错误信息到本地
*/
private fun saveExceptionInfoToLocal(e: Throwable) {
//获取相关信息
val appInfo = getAppInfo(e)
if (Environment.getExternalStorageState() == Environment.MEDIA_MOUNTED) {
val crashFile = File(context.filesDir.path + File.separator + "crash" + File.separator)
val fileName = crashFile.path + System.currentTimeMillis().toString() + ".txt"
val fos = FileOutputStream(fileName)
fos.write(appInfo.toByteArray())
fos.flush()
fos.close()
}
}
/**
* 获取手机相关信息(可以拼接想要记录的异常信息)
*/
private fun getAppInfo(e: Throwable): String {
val packageManager = context.packageManager
val packageInfo =
packageManager.getPackageInfo(context.packageName, PackageManager.GET_ACTIVITIES)
val stringBuffer = StringBuffer()
stringBuffer.append("VersionName:" + packageInfo.versionName)
stringBuffer.append("\n")
stringBuffer.append("VersionCode:" + packageInfo.versionCode)
stringBuffer.append("\n")
//反射获取Build类中的相关属性信息
val buildClazz = Build::class.java
val deviceField = buildClazz.getDeclaredField("DEVICE")
val device = deviceField.get(null)
stringBuffer.append("device:$device")
stringBuffer.append("\n")
val productField = buildClazz.getDeclaredField("PRODUCT")
val product = productField.get(null)
stringBuffer.append("product:$product")
stringBuffer.append("\n")
//记录异常信息
val stringWriter = StringWriter()
val printWriter = PrintWriter(stringWriter)
e.printStackTrace(printWriter)
printWriter.close()
stringBuffer.append(stringWriter.toString())
return stringBuffer.toString()
}
}
验证
class BaseApplication : Application() {
override fun onCreate() {
super.onCreate()
ExceptionCrashHandler.instance.init(this)
}
}
class MainActivity : AppCompatActivity() {
@BindView(R.id.tv)
private var tv: TextView? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
InjectUtils.bind(this)
}
@OnClick(R.id.tv, R.id.tv)
private fun onClick(view: View) {
//故意引入崩溃
Toast.makeText(this, "Send Message for you" + 2 / 0, Toast.LENGTH_LONG).show()
}
}
本地记录崩溃信息
结语
如果以上文章对您有一点点帮助,希望您不要吝啬的点个赞加个关注,您每一次小小的举动都是我坚持写作的不懈动力!ღ( ´・ᴗ・` )