Once a crash occurs in a distributed program, it can be difficult to investigate the cause. Collecting crash logs is especially important.

Tencent Bugly provides similar services, but I don’t think it’s necessary to introduce other services for such a simple function.

There is a lot of code on the web that does this, writing logs to local files in the event of a crash.

I share a version of Kotlin here:

/** * Created by GreendaMi on 2018/5/3. */
class CrashHandler :Thread.UncaughtExceptionHandler {

    lateinit var mDefaultHandler: Thread.UncaughtExceptionHandler
    lateinit var mContext: Context
    // Save mobile phone information and exception information
    private val mMessage = HashMap<String,String>()

    companion object {
        var sInstance: CrashHandler? = null
        fun getInstance(a): CrashHandler? {
            if (sInstance == null) {
                synchronized(CrashHandler::class.java) {
                    if (sInstance == null) {
                        synchronized(CrashHandler::class.java) {
                            sInstance = CrashHandler()
                        }
                    }
                }
            }
            return sInstance
        }
    }

    override fun uncaughtException(t: Thread? , e:Throwable?). {
        if(! handleException(e)) {// If no manual processing is performed, the system will invoke the default processing exception, and a dialog box is displayed indicating that the system is forced to close
            if(mDefaultHandler ! =null) {
                mDefaultHandler.uncaughtException(t, e)
            }
        } else {
            // The system has logged out
            try {
                Thread.sleep(1000)}catch (e1: InterruptedException) {
                e1.printStackTrace()
            }
            / / restart
            varintent = mContext? .packageManager? .getLaunchIntentForPackage(mContext? .packageName) intent? .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK) mContext? .startActivity(intent) android.os.Process.killProcess(android.os.Process.myPid()) } }/** * Initializes the default exception catch **@param context context
     */
    fun init(context: Context) {
        mContext = context
        // Get the default exception handler
        mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler()
        // Set this class as the default exception handler
        Thread.setDefaultUncaughtExceptionHandler(this)}/** * Whether the exception is artificially caught **@param e Throwable
     * @returnTrue: processed False: not processed */
    private fun handleException(e: Throwable?).: Boolean {
        if (e == null) {// Whether the exception is null
            return false
        }
        object : Thread() {
            // A prompt is displayed in the main thread
            override fun run(a) {
                Looper.prepare()
                Toast.makeText(mContext, "The program has encountered an unknown exception and will restart.", Toast.LENGTH_SHORT).show()
                Looper.loop()
            }
        }.start()
        collectErrorMessages()
        saveErrorMessages(e)
        return false
    }

    private fun collectErrorMessages(a) {
        valpm = mContext? .packageManagertry {
            valpi = pm? .getPackageInfo(mContext? .packageName, PackageManager.GET_ACTIVITIES)if(pi ! =null) {
                val versionName = if (TextUtils.isEmpty(pi.versionName)) "null" else pi.versionName
                val versionCode = "" + pi.versionCode
                mMessage["versionName"] = versionName
                mMessage["versionCode"] = versionCode
            }
            // Get the error message by reflection
            val fields = Build::class.java!!!!! .fields
            if(fields ! =null && fields.isNotEmpty()) {
                for (field in fields!!) {
                    field.isAccessible = true
                    try {
                        mMessage[field.name] = field.get(null).toString()
                    } catch (e: IllegalAccessException) {
                        e.printStackTrace()
                    }

                }
            }
        } catch (e: PackageManager.NameNotFoundException) {
            e.printStackTrace()
        }

    }

    private fun saveErrorMessages(e: Throwable) {
        val sb = StringBuilder()
        for (entry in mMessage) {
            val key = entry.key
            val value = entry.value
            sb.append(key).append("=").append(value).append("\n")}val writer = StringWriter()
        val pw = PrintWriter(writer)
        e.printStackTrace(pw)
        var cause: Throwable? = e.cause
        // loop out Cause
        if(cause ! =null) {
            cause.printStackTrace(pw)
        }
        pw.close()
        val result = writer.toString()
        sb.append(result)
        val time = SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.CHINA).format(Date())
        val fileName = "crash-" + time + "-" + System.currentTimeMillis() + ".log"
        // There is no SD card
        if (Environment.getExternalStorageState() == Environment.MEDIA_MOUNTED) {
            val path = AppConfig.crashPath
            val dir = File(path)
            if(! dir.exists()) dir.mkdirs()var fos: FileOutputStream? = null
            try{ fos = FileOutputStream(path + fileName) fos!! .write(sb.toString().toByteArray()) }catch (e1: Exception) {
                e1.printStackTrace()
            } finally {
                if(fos ! =null) {
                    try{ fos!! .close() }catch (e1: IOException) {
                        e1.printStackTrace()
                    }

                }
            }
        }
    }
}
Copy the code

Initialize it in Application:

 // Exception collectionCrashHandler.getInstance()? .init(this@App)
Copy the code

Warm tip: The written files are synchronous. If you need to upload the error log information to the server, because such network communication is generally asynchronous, you need to upload the error log information to the server

if(! HandleException (e)) {// If no manual processing is done, the system will call the default handling exception, and a dialog box will pop up indicating that the system is forced to closeif(mDefaultHandler ! = null) { mDefaultHandler.uncaughtException(t, e) } }Copy the code

In the

if(mDefaultHandler ! = null) { mDefaultHandler.uncaughtException(t, e) }Copy the code

Part of the move to the end of upload action callback, to prevent the upload action is not finished, the program is accidentally terminated.