本篇文章主要是记录Activity各种疑难杂症,在平时使用过程中遇到的坑点,以及Activity使用难点,欢迎各位拍砖。
1.setResult和finish的顺序关系
当ActivtyA通过startActivityForResult启动ActivityB的时候,从ActivityB页面返回并设置setResult的时候,会回调ActivityA的onActivityResult方法,并可以通过setResult传值,有时候会遇到setResult传值不起作用的情况:
image.png
上面这张图我是在SecordActivity的onPause方法里调用的setResult:
@Override protected void onPause() { super.onPause(); Intent intent = new Intent(); intent.putExtra("name","来自secord的data"); setResult(101,intent); Log.e("wangkeke","secord---onPause"); }
可以发现setResult传递name值失败,那么setResult()应该在什么时候调用呢?看下setResult和finish的源码:
public final void setResult(int resultCode, Intent data) { synchronized (this) { mResultCode = resultCode; mResultData = data; } } private void finish(int finishTask) { if (mParent == null) { int resultCode; Intent resultData; synchronized (this) { resultCode = mResultCode; resultData = mResultData; } if (false) Log.v(TAG, "Finishing self: token=" + mToken); try { if (resultData != null) { resultData.prepareToLeaveProcess(this); } if (ActivityManager.getService() .finishActivity(mToken, resultCode, resultData, finishTask)) { mFinished = true; } } catch (RemoteException e) { // Empty } } else { mParent.finishFromChild(this); } }
Activity返回result是在finish的时候,也就是说调用setResult()方法必须在finish()之前。所以在onPause、onStop、onDestroy方法中调用setResult()可能会返回失败。
如果你需要在点击back键的时候setResult,可以用以下写法:
@Override public void onBackPressed() { Intent intent = new Intent(); intent.putExtra("name","来自secord的data"); setResult(101,intent); //下面这行代码会去调用finish方法 super.onBackPressed(); }
2.onNewIntent注意事项以及SingleTask的坑
触发时机:activity任务栈中已存在要启动的Activity实例,则不会再创建新实例,而是执行Activity的onNewIntent(intent)方法。
@Override protected void onNewIntent(Intent intent) { super.onNewIntent(intent); //更新intent setIntent(intent); }
如果没有调用setIntent(intent),则getIntent()获取的数据可能不是你所期望的。但是使用onNewIntent的参数intent,是可以获得正确的数据的。
注意:对于Activity的启动模式这里不多介绍,但是当要启动的Activity的启动模式设置为SingleTask时,并且启动Activity使用的是startActivityforResult方法,那么此时SingleTask会失效。
具体原因分析点这里!
3.旋转手机屏幕时Activity的生命周期和onConfigurationChanged()的调用时机
我在Android8.0上Activity未配置configChanges时的测试结果如下:
image.png
配置configChanges为android:configChanges="orientation|screenSize"时,结果如下:
image.png
此时Activity不会重新创建,而只会调用onConfigurationChanged()方法。在targetSdkVersion的值小于或等于12时,只需要配置orientation就可以得到同样的结果。
所以旋转手机屏幕时,activity生命周期的变化和configChanges属性有关,配置相关属性就不会重新走onCreate流程,不配置的话就会先走销毁流程,再走onCreate流程。
注:configChanges属性参数如下表:
官网截图.png
4.onSaveInstanceState()和onRestoreInstanceState()
onSaveInstanceState() 和 onRestoreInstanceState() 并不是生命周期方法,它们不同于 onCreate()、onPause()等生命周期方法,并不一定会被触发。
当应用遇到意外情况(如:内存不足、按Home键等)由系统销毁一个Activity时,onSaveInstanceState() 会被调用。但当用户主动去销毁一个Activity时,例如在应用中按返回键,onSaveInstanceState() 就不会被调用。
onSaveInstanceState()的调用遵循一个重要原则,即当系统存在 “未经你许可” 销毁了我们的 activity 的可能时,则 onSaveInstanceState() 会被系统调用,且调用将发生在 onPause() 之前。
onRestoreInstanceState() 被调用的前提是,activityA “确实” 被系统销毁了,且 activity A 被重新创建。当 activityA 未被重新创建时,该方法不会被调用,onRestoreInstanceState() 在 onStart() 和 onResume() 之间调用。
注意:onRestoreInstanceState()一旦被调用,其参数bundle一定是有值的,而onCreate则不一定。
5. TaskAffinity属性
我们知道,每个APP默认只有一个任务栈,以应用的包名来命名,Activity的TaskAffinity属性可以新建任务栈。
如果单独设置TaskAffinity属性的话是没有任何效果的,只有Activity的launchMode设置成singleTask的时候才会生效的,在AndroidManifest的Activity中可以配置TaskAffinity这个属性。
注意:
1.TaskAffinity的值应该是形如xxx.xxx.xxx的形式,如果没有包含 . 的话是安装不了的;
2.如果不指定TaskAffinity的话,默认的任务栈名称就是应用包名。
注意:采用startActivityForResult的方式启动Activity时,TaskAffinity属性无效!
作者:寒小枫
链接:https://www.jianshu.com/p/7fc6560c5d99