方案概述
APP在后台时,不提示Crash;
APP刚启动时,因为无法启动Crash处理的Activity,就调用默认的CrashHandler;
其他情况下,弹出不可取消的对话框,用户可选择退出应用或者重启应用(忽略Crash会导致不可知的程序行为,所以没有忽略的选项,只是让用户自主退出,提升用户体验)
效果:
代码:
在Application中注册Crash处理类
class BaseApplication : Application() {
override fun onCreate() {
super.onCreate()
/// other code
//首先配置自定义crash处理,这样不会覆盖神策,友盟等第三方SDK的crash处理
setCustomCrashHandler()
/// other code
}
private fun setCustomCrashHandler() {
//一个进程一个DefaultUncaughtExceptionHandler
val defaultHandler = Thread.getDefaultUncaughtExceptionHandler()
Thread.setDefaultUncaughtExceptionHandler { thread, exception ->
exception?.printStackTrace()
//程序是否在后台
val isBackground = ActivityLifecycleCallbackImp.activityForgroundCount == 0
//APP初始化中,还未启动任何Activity,此时无法启动Crash处理的Activity
if(ActiivtyStackHolder.instance.activityCount() == 0){
defaultHandler?.uncaughtException(thread,exception)
}else if (isBackground) {
Log.d("MyApplication", "isBackground just kill the process without annoying users")
Runtime.getRuntime().exit(1)
} else {
// 跳转到崩溃提示Activity
var intent = intentFor<CrashDialogActivity>()
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)// In case we are called with non-Activity context.
startActivity(intent);
}
}
object ActivityLifecycleCallbackImp : Application.ActivityLifecycleCallbacks {
var activityForgroundCount: Int = 0
override fun onActivityPaused(activity: Activity?) {
}
override fun onActivityResumed(activity: Activity?) {
}
override fun onActivityStarted(activity: Activity?) {
activityForgroundCount++
}
override fun onActivityDestroyed(activity: Activity?) {
}
override fun onActivitySaveInstanceState(activity: Activity?, outState: Bundle?) {
}
override fun onActivityStopped(activity: Activity?) {
activityForgroundCount--
}
override fun onActivityCreated(activity: Activity?, savedInstanceState: Bundle?) {
}
}
fun restartToMainActivity(){
startActivity(intentFor<IndexActivity>().newTask().clearTask())
}
处理Crash的Activity
import android.content.Intent
import android.os.Bundle
import com.afollestad.materialdialogs.GravityEnum
import com.afollestad.materialdialogs.MaterialDialog
import kotlinx.android.synthetic.main.activity_crash_dialog.*
import android.content.Intent.FLAG_ACTIVITY_NEW_TASK
import android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP
/**
* 弹出crash处理对话框的页面
*/
class CrashDialogActivity : BaseActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_crash_dialog)
showCrashDialog()
}
private fun showCrashDialog() {
MaterialDialog.Builder(this)
.cancelable(false)
.contentGravity(GravityEnum.CENTER)
.content("很抱歉,程序出现异常")
.positiveText("重启应用")
.negativeText("退出应用")
.onNegative{dialog, which ->
//退出应用
//某些情况下,比如从最近使用的APP中恢复应用(重新调用了Activity的onCreate方法)后,在当前页面发生crash,可能还是重启的效果,
ActiivtyStackHolder.instance.clearAllActivity()
/* Runtime.getRuntime().exit(1)*/ //调用此方法系统会自动重启APP,所以不调用
}
.onPositive { dialog, which ->
//重启应用
BaseApplication.instance.restartToMainActivity()
Runtime.getRuntime().exit(1)
}
.show()
}
}
Activity管理类
import android.app.Activity
import java.lang.ref.WeakReference
import java.util.*
/**
* Created by Administrator on 2017/9/19.
*/
class ActiivtyStackHolder private constructor() {
private val activityStack = Stack<WeakReference<Activity>>()
companion object {
val instance: ActiivtyStackHolder by lazy {ActiivtyStackHolder() }
}
fun activityCount() : Int{
return activityStack.size
}
// 将当前Activity推入栈中
fun pushActivity(activity: Activity) {
activityStack.add(WeakReference(activity))
}
/**
* 结束指定的 Activity
* @param activity Activity
*/
fun removeActivity(activity: Activity?) {
if (activity != null) {
activityStack.forEach {
if (it.get() == activity){
activityStack.remove(it)
return
}
}
}
}
// 退出栈中所有Activity
fun clearAllActivity() {
while (!activityStack.isEmpty()) {
val activity = activityStack.pop()?.get()
activity?.finish()
}
}
}
感谢以下文章的作者:
Android中实现用户无感知处理后台崩溃
Android全局异常捕获并弹窗提示
网友评论