我个人的处理方法是在现场保护中保存一个变量,在程序再次启动时执行onCreate方法在savedInstanceState中得到这个变量,以此判断是否需要做现场恢复或者重启应用(微信也是重启应用)。手动执行Application类的onCreate方法,问题可以解决,不知道这样做是否标准。
Copy link
如何手动执行Application类的onCreate方法啊?
为何手动执行?什么场景下会有需要这种用法?
为了应用能够重新启动,只启动activity是不够的,因为有些需求是在apppication的oncreate中实现,所以才会手动去调用,这样做或许不对,望大神给出更好的方法
任何的Activity, Service启动前,都会先启动Application的, 不明白为什么会需要手动启动application。
Copy link
我碰到过这个问题,相当讨厌。根源在于Android系统太“自作聪明”了。我的案例是,再回到应用时,系统会自动进行恢复,但是这个恢复既是不全面的,又是背后不让你知道的,所以应用没有正确恢复,而你还不知道,那么运行下去导致崩溃或其他乱象也就很自然了。
我的建议是,1、自己备份认为重要的数据,2、判断系统是否背后帮忙恢复,如果是,删除系统的恢复,自己来恢复。
我测试过手机QQ,当在后台运行时候通过shell命令kill QQ后,QQ是从引导界面重新显示的。而我的APP当通过shell命令kill后是显示的最后的activity,这个activity的onRestoreInstanceState 执行了。我向问我如何让我的APP重新从头执行?(kill后再打开APP,application 会执行onCreate(),只是执行后直接跳到最后显示的activity)
还是逻辑的问题
@zhangfy068 我现在是这么处理的。因为App被回收后重新打开,application 的 onCreate 会执行,但是不回执行android.intent.action.MAIN 的activity(我的App是个引导页)的onCreate而是直接进入到被回收时的activity。依据次现象,我有app会有个变量记录application是否正常启动和非正常启动。如果是非正常启动的我就重新回到android.intent.action.MAIN 的activity,这样就和QQ的现象是一样的。不知道大家有没有好的解决方案!
@lixiangers 那直接进入回收的activity,你是根据异常值,直接finish掉了?
@zhangfy068
Intent i = getBaseContext().getPackageManager()
.getLaunchIntentForPackage(getBaseContext().getPackageName());
i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(i);
finish();
Application应该不会回收吧,研究下 把结果告诉我 谢谢
会回收的,然后直接重建Application 并直接启动栈顶的activity。
会绕过很多activity,所以如果在其他activity对Application中的值进行了赋值,就有可能导致问题。
2015-07-26 21:58 GMT+08:00 jovezhougang notifications@github.com:
Application应该不会回收吧,研究下 把结果告诉我 谢谢
—
Reply to this email directly or view it on GitHub
#123 (comment)
.
这种情况很容易模拟,你弄个XXX手机助手/管家 , 运行程序之后切回桌面,疯狂清理,再进去就可以了,appliation会重新onCreate的,底层的C++库也会重新加载,但是会直接恢复到崩溃之前的activity,而不是正常流程到MainActivity ,我目前也没什么好的办法。。。只能增加一个变量来判断是不是正常进入这个activity,然后不正常的话直接killprocess然后重启。
目前有没有比较好的方案,我也遇到了问题。
我觉得,如果你回到Activity程序Crash了,肯定是你的代码有问题,要不就是在Application存储了数据,被回收之后恢复现场出现了错误,要不就是你自己做了内容备份,但是备份的方式不对,造成不能正常恢复现场。如果你没有前面这两种情况,再次回来重启Activity的时候,会重新加载数据,而不应该是崩溃。所以崩溃一定是你的代码有问题
crush肯定是代码问题, Activity在resume以后崩溃一般是依赖的对象消失形成的nullpointer居多吧, 先debug找到具体是崩在哪里,
试试看加一点先决条件判断
ZhaoKaiQiang notifications@github.com于2015年12月10日周四 上午6:57写道:
我觉得,如果你回到Activity程序Crash了, 肯定是你的代码有问题, 要不就是在Application存储了数据, 被回收之后恢复现场出现了错误,要不就是你自己做了内容备份, 但是备份的方式不对,造成不能正常恢复现场。 如果你没有前面这两种情况, 再次回来重启Activity的时候,会重新加载数据, 而不应该是崩溃。所以崩溃一定是你的代码有问题
—
Reply to this email directly or view it on GitHub .
@maxyou “恢复不全面”体现在什么地方。。能举个例子嘛。savedInstanceState中难道还有部分值不能成功保存。。?今天刚遇到这个问题。。
Copy link
我刚好在别的地方谈及了这个问题,拷贝过来:
如果你:
1、先启动A
2、A为B做一些初始化等等
3、启动B
4、隐藏APP
那么系统需要内存时会杀掉app,但又会保留app的某些映像用于恢复。
接下来你用任何方式返回或启动app,那么系统会根据之前保留的app映像帮助你恢复app。
但是。。。系统会直接恢复B,而不是先启动A再恢复B,所以会得到一个让你很迷惑的B。
以上是常见问题之一。
之前遇到过类似问题,ViewPager嵌套ViewPager容易出现此问题。现象是最里层的Fragment中gatActiviy()和获取到的kill掉又重新create的Activity的id不一致,取activiy中的数据都为null(就像@maxyou 描述的一样,这个fragment很迷惑),然后crash了
目前解决方法是在Activity的onCreate方法中的super.onCreate()前上如下代码
if (savedInstanceState != null){
String FRAGMENTS_TAG = "android:support:fragments";
savedInstanceState.remove(FRAGMENTS_TAG);
}
具体原理,看代码应该能明白了
👍
4
🎉
2
现在项目里面有个问题就是这样的:IM的app,需要在启动时进行登录操作,成功后创建一个整个项目都要用到的全局管理器对象 。退到后台后,系统在内存紧张的时候,把app杀掉,然后又恢复,恢复后的进程肯定是发生了变化的,此时全局管理器对象也不存在了,然后在用到这个对象的时候就会有问题。
我的想法就是不要让系统把App 重启,不知道怎么做。
@lixiangers 你好,有个问题想请教你一下。看到你上面的回复
我测试过手机QQ,当在后台运行时候通过shell命令kill QQ后,QQ是从引导界面重新显示的。而我的APP当通过shell命令kill后是显示的最后的activity,这个activity的onRestoreInstanceState 执行了。我向问我如何让我的APP重新从头执行?(kill后再打开APP,application 会执行onCreate(),只是执行后直接跳到最后显示的activity)
我在测试的时候,直接kill掉进程,再次打开app,是从头执行的,可是我并没有进行特殊处理。请问怎么才能实现kill后再打开APP,application 执行onCreate(),执行后直接跳到最后显示的activity???
@nangonghuang 你好,有个问题想请教你一下,看到你上面的回复
这种情况很容易模拟,你弄个XXX手机助手/管家 , 运行程序之后切回桌面,疯狂清理,再进去就可以了,appliation会重新onCreate的,底层的C++库也会重新加载,但是会直接恢复到崩溃之前的activity,而不是正常流程到MainActivity ,我目前也没什么好的办法。。。只能增加一个变量来判断是不是正常进入这个activity,然后不正常的话直接killprocess然后重启。
我kill掉APP的进程后,再次打开app,是从MainActivity 开始执行,并没有恢复最后打开的activity。怎么才能恢复最后打开的activity
Copy link
@lixiangers “依据次现象,我有app会有个变量记录application是否正常启动和非正常启动。如果是非正常启动的我就重新回到android.intent.action.MAIN 的activity,这样就和QQ的现象是一样的。” == 请问这个变量记录您是怎么实现的?
@lixiangers 能说一下你那个标记application是否正常启动的标志位是怎么弄的么?谢谢
@weinierfei 也遇到这个问题。 测试异常 使用 kill -pid 关掉程序 。
在初始化数据的地方加个变量,初始化正常后 更改这个变量。然后每次app到前台后,判断这个变量是不是更改后的值,如果 不是,就关闭所有Activity,重新进入 初始化数据地方。
例如: 我们有数据初始化需要在 splash 界面初始化,全局加了个常量 is_launch_normal = false , 进入 splash 界面后,把这个变量 改为 true 。 在 app 进入前台后判断 这个变量如果 为 false, 就重新进入 splash 界面。
@onloadcc 感谢回复哈;这个前段时间用记录进程id的方法解决了;
1.Application里,将int pid = android.os.Process.myPid()存入SharedPreferences;
2.在BaseActivity的onCreate的super前
//如果进程被杀死,终止系统恢复机制,重新启动
if (savedInstanceState != null) {
int oldPid = SavePreferences.getInt(SPreferencesConst.APP_PID);
if (oldPid != 0) {
Intent i = getBaseContext().getPackageManager().getLaunchIntentForPackage(getBaseContext()
.getPackageName());
i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
startActivity(i);
}
}
3.正常退出的时候,应该将存在SharedPreferences的APP_PID至为默认值
👍
8
Copy link
@weinierfei 请问下第三步的
“正常退出的时候,应该将存在SharedPreferences的APP_PID至为默认值”
那么什么时候恢复默认值呢
@weinierfei 请问下可以直接判断savedInstanceState是否为空,如果不为空就重启可以吗?还是说有什么问题
@ZhaoKaiQiang 你说的没错crash肯定是代码有问题。关键是,很多初始化操作都是在自定义Application的onCreate中,比如:图片加载框架ImageLoader的初始化。假设现在在相册的Activity,点击Home,过段时间app被回收,在进入app恢复到相册Activity,这时就会崩溃:E/uncaughtException: java.lang.IllegalStateException: ImageLoader must be init with configuration before using
But,几乎每个Activity都会有这种情况,因为很多框架需要在Application初始化。 可否有解决办法?
👍
1
我有个比较粗鲁的解决方案:在自定义的Application中加入:
Thread.setDefaultUncaughtExceptionHandler( new Thread.UncaughtExceptionHandler() {
public void uncaughtException(Thread thread, Throwable ex) {
LogUtil.e("uncaughtException",LogUtil.exception2String(ex));
restartApp();
}
});
从后台启动app(也包括其他情况),不管因为什么原因的崩溃,咱们直接重启app
👍
1
以下是我 2015 年的做法,自己觉得沿用到现在也很实用:
概念:
内存重启:表现是 App 从后台进入前台的过程中,因为内存数据之前已经被回收,系统会从最后一个显示的 Activity 开始一步步恢复 Activity 和 Activity 状态。
Activity 状态 - 初始状态 :初始状态为 Activity 加载了一个 layout 布局,还没有获取任何的 id 、操作 view 、开始异步加载数据。
Activity 状态 - 数据渲染状态:初始状态以后开始加载数据和渲染改变一些界面的状态,会以一个方法作为入口,正常启动会直接 onCreate 初始状态后进入 数据渲染状态,但我的做法是内存重启 的时候会在 Activity 的 onActivityResult 才进入数据渲染状态。那么具体怎么做到呢?
讲解
假定:恢复的 Activity 为 RestoreActivity,引导页 Activity 为 AppStartActivity
在所有 Activity 的基类中有判断,若 onCreate 发现是内存重启的情况下,RestoreActivity
的 startActivityForResult 启动一个引导界面AppStartActivity,同时结束 onCreate 不进入 数据渲染状态,直到启动页面结束返回,这个恢复的 Activity 被系统调用了 onActivityResult 通知我们结果,然后根据接收到返回的对应 result code 进入 数据渲染状态 。
第二点很重要的是,在这个引导界面进行一些 App 的全局数据单例的恢复(例如:已登录的用户单例信息),当然根据需求可以适当预访问或预加载一些比较急需的 App 全局数据,加速返回后的恢复页 Activity 的界面展现,因为引导页的时间一般都会有最低3秒或有个最低秒数阈值,所以理论上可以合理预加载一点数据利用一下,然后到达一定时间就要返回,引导界面的关键就是恢复关键数据和预加载数据
为什么会这么设计呢?有心人注意去测试看看 微信 的启动,真正的内存重启的时候,会让你看到 小人 + 地球 的引导 Activity,再返回真正的对应恢复 Activity,用户体验上也是内存重启就会出现一个相对较友好的引导界面做了一个长期的用户认知培养,而不是一个 正在加载或容易 crash 的界面。
拓展
可以不用依赖于一个 Activity 级别去实现这个 引导界面,通过 Fragment 和 属性变量来实现也可以,若发现是 内存重启 不进入 数据渲染状态,加载引导 Fragment,做对应的恢复关键数据和预加载数据,改变属性变量调用对应的入口方法,继续进入 数据渲染状态
👍
3
我比较常遇见的情况就是“can not perform xxx after state loss”,这种问题是在onResume之前对Fragment transition 进行了commit,因为在进入后台一段时间后热启动,系统会自动为我们恢复Fragment,如果你在onResume之前有作FragmentTransition的commit操作的,系统也会自动调用FragmentTransition的commit操作,而commit操作会清掉savedInstanceState中的数据,此时就会报这错误。一般两种做法,一种是:
if (savedInstanceState == null){
getFragmentManager().beginTransition().add(xxxx,xxxx).commit()
}
另一种是:(不推荐)
getFragmentManager().beginTransition().add(xxxx,xxxx).commitAllowStateLoss()