专栏分享点击跳转=>Unity3D特效百例点击跳转=>案例项目实战源码点击跳转=>游戏脚本-辅助自动化点击跳转=>Android控件全解手册点击跳转=>Scratch编程案例
👉关于作者
众所周知,人生是一个漫长的流程,不断克服困难,不断反思前进的过程。在这个过程中会产生很多对于人生的质疑和思考,于是我决定将自己的思考,经验和故事全部分享出来,以此寻找共鸣!!!
专注于Android/Unity和各种游戏开发技巧,以及各种资源分享(网站、工具、素材、源码、游戏等)
有什么需要欢迎私我,交流群让学习不再孤单。
👉实践过程
在开发过程中,想必你也一定遇到过这样的问题,当我们的应用发生Crash时异常退出,然后又自动启动跳转到未知页面,此时应用在崩溃前保存的全局变量被重置,用户状态丢失,显示数据错乱。更让我们头疼的是,这种崩溃后重启的情况,并不是每次都会遇到,那么究竟是因为什么呢?
经测试,在 Android 的 API 21 ( Android 5.0 ) 以下,Crash 会直接退出应用,但是在 API 21 ( Android 5.0 ) 以上,系统会遵循以下原则进行重启:
包含 Service,如果应用 Crash 的时候,运行着Service,那么系统会重新启动 Service。
不包含 Service,只有一个 Activity,那么系统不会重新启动该 Activity。
不包含 Service,但当前堆栈中存在两个 Activity:Act1 -> Act2,如果 Act2 发生了 Crash ,那么系统会重启 Act1。
不包含 Service,但是当前堆栈中存在三个 Activity:Act1 -> Act2 -> Act3,如果 Act3 崩溃,那么系统会重启 Act2,并且 Act1 依然存在,即可以从重启的 Act2 回到 Act1。
看了上述解释,我们终于知道应用在什么种情况下才会重启。
面对这样的问题,我们提供两种解决思路,一是允许应用自动重启,并在重启时恢复应用在崩溃前的运行状态。二是禁止应用自动重启,而是让用户在应用发生崩溃后自己手动重启应用。
本文主要提供禁止应用自动启动的思路和代码实现。
网上有很多开源的库供大家选择,但个人觉得一个类就可以解决的问题,没必要再引入依赖到项目中。
😜获取应用Crash时的回调
Android提供一个UncaughtExceptionHandler的接口,该接口在应用发生Crash时,会回调接口中的uncaughtException方法。
因此我们可以构建一个类,继承UncaughtExceptionHandler接口,并覆写uncaughtException方法,在覆盖方法中处理Crash问题并退出应用。
class CrashCollectHandler : Thread.UncaughtExceptionHandler {//当UncaughtException发生时会转入该函数来处理override fun uncaughtException(t: Thread?, e: Throwable?) {if (!handleException(e) && mDefaultHandler!=null){//如果用户没有处理则让系统默认的异常处理器来处理mDefaultHandler?.uncaughtException(t,e)}else{try {//给Toast留出时间Thread.sleep(2000)} catch (e: InterruptedException) {e.printStackTrace()}//退出程序android.os.Process.killProcess(android.os.Process.myPid())System.exit(0)}}}
handleException方法主要是为了弹出Toast和收集crash信息
fun handleException(ex: Throwable?):Boolean {if (ex == null){return false}Thread{Looper.prepare()toast("很抱歉,程序出现异常,即将退出")Looper.loop()}.start()//收集设备参数信息collectDeviceInfo(mContext);//保存日志文件saveCrashInfo2File(ex);// 注:收集设备信息和保存日志文件的代码就没必要在这里贴出来了//文中只是提供思路,并不一定必须收集信息和保存日志//因为现在大部分的项目里都集成了第三方的崩溃分析日志,如`Bugly` 或 `啄木鸟等`//如果自己定制化处理的话,反而比较麻烦和消耗精力,毕竟开发资源是有限的return true}
设置程序的默认处理器
fun init(pContext: Context) {this.mContext = pContext// 获取系统默认的UncaughtException处理器mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler()// 设置该CrashHandler为程序的默认处理器Thread.setDefaultUncaughtExceptionHandler(this)}
最后在Application中调用并初始化
class APP:Application{override fun onCreate() {super.onCreate()/**捕获Crash,解决程序崩溃后启动的问题*/CrashCollectHandler.instance.init(this)}}
如果按照上边的代码执行,你会发现应用依然会在崩溃时重启。 (看到这里,心里已经开始骂娘了,写的什么鸟博客,完全解决不了我的问题啊。 )
别急,此刻你的内心和当初的我是一模一样的。
为了让你印象深刻, 请继续往下边看。
😜退出栈内所有的Acitvity
如果应用在发生崩溃时,回退栈内依然存在没有退出的Activity,即使调用了System.exit(0)方法,应用依然会自动重启。因此我们就需要在应用退出之前,先清除栈内所有的Activity。
在Application中定义一个存储Activity的list,并定义三个管理Activity集合的方法。
class App:Application{fun addActivity(activity: Activity) {if (!activityList.contains(activity)) {activityList.add(activity)}}fun removeActivity(activity: Activity) {if (activityList.contains(activity)) {activityList.remove(activity)}}fun removeAllActivity() {activityList.forEach {if (it != null) {it.finish()}}}}
在BaseActivity中执行管理Activity集合的方法。
open class BaseActivity : AppCompatActivity(){override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)App.i.addActivity(this)}override fun onDestroy() {super.onDestroy()App.i.removeActivity(this)}}
在上边我们已经学习过的方法uncaughtException中添加退出所有Activity的方法
class CrashCollectHandler : Thread.UncaughtExceptionHandler {//当UncaughtException发生时会转入该函数来处理override fun uncaughtException(t: Thread?, e: Throwable?) {...//退出程序App.i.removeAllActivity()android.os.Process.killProcess(android.os.Process.myPid())System.exit(0)...}}}
OK,大功告成,跑一下程序试试,果然在应用崩溃后不会发生再次启动应用的情况。
文中使用的是Kotlin语言,如果你使用的编译语言是Java,把上述代码转化为java执行即可。
😜最后
最后再贴一下完整的CrashCollectHandler类:
class CrashCollectHandler : Thread.UncaughtExceptionHandler {var mContext: Context? = nullvar mDefaultHandler:Thread.UncaughtExceptionHandler ?=nullcompanion object {val instance by lazy(mode = LazyThreadSafetyMode.SYNCHRONIZED) {CrashCollectHandler() }}fun init(pContext: Context) {this.mContext = pContext// 获取系统默认的UncaughtException处理器mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler()// 设置该CrashHandler为程序的默认处理器Thread.setDefaultUncaughtExceptionHandler(this)}//当UncaughtException发生时会转入该函数来处理override fun uncaughtException(t: Thread?, e: Throwable?) {if (!handleException(e) && mDefaultHandler!=null){//如果用户没有处理则让系统默认的异常处理器来处理mDefaultHandler?.uncaughtException(t,e)}else{try {//给Toast留出时间Thread.sleep(2000)} catch (e: InterruptedException) {e.printStackTrace()}//退出程序App.i.removeAllActivity()android.os.Process.killProcess(android.os.Process.myPid())System.exit(0)}}fun handleException(ex: Throwable?):Boolean {if (ex == null){return false}Thread{Looper.prepare()toast("很抱歉,程序出现异常,即将退出")Looper.loop()}.start()//收集设备参数信息//collectDeviceInfo(mContext);//保存日志文件//saveCrashInfo2File(ex);// 注:收集设备信息和保存日志文件的代码就没必要在这里贴出来了//文中只是提供思路,并不一定必须收集信息和保存日志//因为现在大部分的项目里都集成了第三方的崩溃分析日志,如`Bugly` 或 `啄木鸟等`//如果自己定制化处理的话,反而比较麻烦和消耗精力,毕竟开发资源是有限的return true}}
👉其他
📢作者:小空和小芝中的小空
📢转载说明-务必注明来源:https://zhima./
📢这位道友请留步☁️,我观你气度不凡,谈吐间隐隐有王者霸气💚,日后定有一番大作为📝!!!旁边有点赞👍收藏🌟今日传你,点了吧,未来你成功☀️,我分文不取,若不成功⚡️,也好回来找我。
温馨提示:点击下方卡片获取更多意想不到的资源。