Android Java异常捕获 线上项目Java部分零bug

为了保证线上项目的稳定性,减少或者消灭项目的崩溃,需要我们提前对项目的异常进行处理。Java异常主要分为主线程异常、子线程异常、未捕获异常。其中也包含activity与fragment的生命周期异常。我们需要对这几个地方的事件进行try catch处理,基本可以捕获到所有Java异常,当然 还有其它三方自己对异常处理可能会有一点影响,友盟的异常捕捉CrashApi就有可能直接crash而不会被捕捉到,所以如果有集成友盟的这里需要自己单独处理一下。

        Bundle args = new Bundle();
        args.putBoolean("enableJavaLog", false);
        args.putBoolean("mDebug", false);
        args.putBoolean("enableNativeLog", true);
        args.putBoolean("mEncryptLog", false);
        args.putBoolean("mZipLog", true);
        args.putBoolean("enableUnexpLog", false);
        CrashApi.createInstanceEx(context, AppConstants.Push.UM_KEY, false, args);

话不多说,上代码。我们创建一个异常捕获的文件CatchService

class CatchService {
    private lateinit var application: Application
    private lateinit var retrofit: Retrofit
    private var mOkHttpClient: OkHttpClient? = null
    private var apiService: ApiService? = null

    //appToken
    private var appToken: String? = null

    //主线程异常id
    private var mainThreadID: Int? = null

    //生命周期异常id`
    private var lifeID: Int? = null

    //子线程异常id
    private var childThreadID: Int? = null

    //其他异常id
    private var otherID: Int? = null
    private var handler: Handler? = null
    private var isLifeChange = false

    companion object {
        @Volatile
        private var instance: CatchService? = null
        fun getInstance() =
            instance ?: synchronized(this) {
                instance ?: CatchService().also { instance = it }
            }

        fun getExceptionStr(e: Throwable): String {
            try {
                val writer: Writer = StringWriter()
                val printWriter = PrintWriter(writer)
                e.printStackTrace(printWriter)
                var cause = e.cause
                while (cause != null) {
                    //异常链
                    cause.printStackTrace();
                    cause = cause.cause;
                }
                printWriter.close()
                return e.toString() + "\n" + writer.toString()
            } catch (t: Throwable) {
                return ""
            }
        }
    }

	//此处id为自主定义 为了在后端做区分  isOpenCrashProtected参数true代表拦截上传 false代表不拦截异常,建议debug环境下设置为false,生产环境为true
    fun init(
        application: Application,
        appToken: String?,
        mainThreadID: Int?,
        lifeID: Int?,
        childThreadID: Int?,
        otherID: Int?,
        isOpenCrashProtected: Boolean
    ) {
        try {
            this.application = application
            this.appToken = appToken
            this.mainThreadID = mainThreadID
            this.lifeID = lifeID
            this.childThreadID = childThreadID
            this.otherID = otherID
            val builder = OkHttpClient.Builder()
            builder.connectTimeout(
                10000,
                TimeUnit.MILLISECONDS
            ).readTimeout(10000, TimeUnit.MILLISECONDS)
                .writeTimeout(10000, TimeUnit.MILLISECONDS)
                .retryOnConnectionFailure(false)
            mOkHttpClient = builder.build()
            retrofit = Retrofit.Builder()
                .baseUrl(BASE_URL)
                .addConverterFactory(GsonConverterFactory.create())
                .client(mOkHttpClient)
                .build()
            apiService = retrofit.create(ApiService::class.java)
            if (isOpenCrashProtected) {
                openCrashProtected()
            }
            handler = Handler()
        } catch (t: Throwable) {

        }
    }

    /**
     * 上传日志
     */
    fun uploadLog(logId: Int, content: String, title: String) {
        try {
        	//此处为自己定义的上传参数,可根据自己的需求与后端协调
            var logRequest = LogRequest(
                appToken, content, title, 1, IntArray(1).apply { set(0, logId) })
            GlobalScope.launch {
                withContext(Dispatchers.IO) {
                    var response = apiService?.uploadLog(logRequest)
                    Log.d("response", response!!.string())
                }
            }
        } catch (t: Throwable) {
            Log.d("response", t.message.toString() + "\n" + t.printStackTrace().toString())
        }
    }

    /**
     * 上传主线程日志
     */
    fun uploadMainThreadLog(title: String, content: String) {
        try {
            mainThreadID?.let { uploadLog(it, content, title) }
        } catch (t: Throwable) {

        }
    }

    /**
     * 上传子线程日志
     */
    fun uploadChildThreadLog(title: String, content: String) {
        try {
            childThreadID?.let { uploadLog(it, content, title) }
        } catch (t: Throwable) {

        }

    }

    /**
     * 上传生命周期日志
     */
    fun uploadLifeLog(title: String, content: String) {
        try {
            lifeID?.let { uploadLog(it, content, title) }
        } catch (t: Throwable) {

        }

    }

    /**
     * 上传其他日志
     */
    fun uploadOtherLog(title: String, content: String) {
        try {
            otherID?.let { uploadLog(it, content, title) }
        } catch (t: Throwable) {

        }

    }

    /**
     * 开启日志拦截
     */
    private fun openCrashProtected() {
        Thread.setDefaultUncaughtExceptionHandler(object : Thread.UncaughtExceptionHandler {
            override fun uncaughtException(t: Thread, e: Throwable) {
                CatchService.getInstance().uploadChildThreadLog(
                    "子线程异常",
                    getExceptionStr(e)
                )
            }

        })
        Handler(Looper.getMainLooper()).post {
            while (true) {
                try {
                    Looper.loop()
                } catch (e: Throwable) {
                    CatchService.getInstance().uploadMainThreadLog(
                        "主线程异常",
                        getExceptionStr(e)
                    )
                    isLifeChange = false
                    handler?.postDelayed(Runnable {
                        ServiceManager.getInstance().currentActivity?.let {
                            if (!it.hasWindowFocus()) {
                                it.finish()
                            }
                        }
                    }, 3000)
                }
            }
        }
    }
}

上面的catch文件,只需要在application里init就可以捕获项目中的主线程与子线程异常了,至于生命周期异常,在activity的onCreate、onStart、onResume、onStop、onRestart、onDestroy中try catch,并将catch中的异常上传到服务器。

/**
 * 捕捉生命周期异常的
 */
abstract class LifeCatchActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        try {
            create(savedInstanceState)
        } catch (t: Throwable) {
            CatchService.getInstance()
                .uploadLifeLog("生命周期异常", CatchService.getExceptionStr(t))
        }
    }

    override fun onResume() {
        super.onResume()
        try {
            resume()
        } catch (t: Throwable) {
            CatchService.getInstance()
                .uploadLifeLog("生命周期异常", CatchService.getExceptionStr(t))
        }
    }

    override fun onDestroy() {
        super.onDestroy()
        try {
            destroy()
        } catch (t: Throwable) {
            CatchService.getInstance()
                .uploadLifeLog("生命周期异常", CatchService.getExceptionStr(t))
        }
    }


    override fun onStart() {
        super.onStart()
        try {
            start()
        } catch (t: Throwable) {
            CatchService.getInstance()
                .uploadLifeLog("生命周期异常", CatchService.getExceptionStr(t))
        }
    }

    override fun onStop() {
        super.onStop()
        try {
            stop()
        } catch (t: Throwable) {
            CatchService.getInstance()
                .uploadLifeLog("生命周期异常", CatchService.getExceptionStr(t))
        }
    }

    override fun onRestart() {
        super.onRestart()
        try {
            restart()
        } catch (t: Throwable) {
            CatchService.getInstance()
                .uploadLifeLog("生命周期异常", CatchService.getExceptionStr(t))
        }
    }

    abstract fun create(savedInstanceState: Bundle??)

    open fun resume() {

    }

    open fun destroy() {

    }

    open fun start() {

    }

    open fun stop() {

    }

    open fun restart() {

    }

}

fragment是同样的处理方法,这里就不多做赘述了,将BaseActivity继承LifeCatchActivity,就可以实现对生命周期异常的捕获。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值