时隔两年,我对于activity的生命周期非但未变得清晰,反而越来越疑惑。除了普通生命周期方法:onStart(),onRestart(),onCreate(),onResume(),onPause(),onStop(),onDestroy()这些,其实activity启动时,还有一些隐藏的,系统一定会调用的方法。
事实上,一些优秀的开源项目会重写这些方法,完成一些必要的操作,而我在看到这些方法,除了牛掰,大概看的懂之外,再无其他的感受了。
以下方法,也是系统一定会调用的方法:
onApplyThemeResource(Theme theme, int resid, boolean first)
onContentChanged()
onPostCreate()
onPostResume()
onAttachedToWindow()
onWindowFocusChanged(boolean hasFocus)
onDetachedFromWindow()
以下几个问题是一定要搞清楚的,为了我的生命周期大业:
这些方法和生命周期夹杂在一起,哪个先,哪个后?当对话框弹出来,会调用哪些?当应用进入后台,会调用哪些?
哪些获得的window是不为空的?
哪些进行数据处理,会对界面显示造成影响?(黑白屏,无焦点)
哪些可以对view进行初始化?
哪些可以获得控件的尺寸?
我想,这非常重要,此后还会特别关注view的生命周期。此次主要对以上问题进行测试,并进行结果记录。
启动执行顺序:(注意onPostCreate()未被调用)
onApplyThemeResource()
onContentChanged()
onCreate()
onStart()
onResume()
onPostResume()
onAttachedToWindow()
onWindowFocusChanged()
dialog弹出,关闭:
都只调用onWindowFocusChanged,额,onPause呢?难道7.0不用了?换个手机再试试。好吧,现在很多手机在dialog启动的时候不会onPause了,就调用了onWindowFocusChanged,看来课本什么的也不一定靠谱。或许有些型号依旧满足失去焦点会调用onPause,但我试的小米,红米都不会。
退出activity:
onPause()
onWindowFocusChanged()
onStop()
onDestroy()
onDetachedFromWindow()
注意,退出时onDetachedFromWindow()是在onDestroy()之后调用的。
转屏:
onPause()
onStop()
onDestroy()
onDetachedFromWindow()
onApplyThemeResource()
onContentChanged()
onCreate()
onStart()
onResume()
onPostResume()
onAttachedToWindow()
onWindowFocusChanged()
网上看到其他人测试会调用到onPostCreate(),但是我的真的没有,看来这个方法并不完全靠谱,决定弃疗。
好吧,getWindow()都不为空,那么onAttachToWindow()的意义何在?试试getDecorView,终于有用了,测试结果:
onApplyThemeResource()DecorView@9f4f736[]
onContentChanged()DecorView@9f4f736[]
onCreate()DecorView@9f4f736[]
onStart()DecorView@9f4f736[]
onResume()DecorView@9f4f736[]
onPostResume()DecorView@9f4f736[]
onAttachedToWindow()DecorView@9f4f736[LifecycleActivity]
onWindowFocusChanged()DecorView@9f4f736[LifecycleActivity]
onPause()DecorView@9f4f736[LifecycleActivity]
onWindowFocusChanged()DecorView@9f4f736[LifecycleActivity]
onStop()DecorView@9f4f736[LifecycleActivity]
onDestroy()DecorView@9f4f736[LifecycleActivity]
onDetachedFromWindow()DecorView@9f4f736[LifecycleActivity]
发现了么?方括号里面的是DectorView绑定的activity,可以看到,这个DctorView确实挺特殊,从onAttatchToWindow以后就一直和activity处于绑定状态,包括onDestroy
当需要获取状态栏,标题栏高度的时候,可以用它,我记得应用要截屏的时候,也有用到,这时候注意放在onAttachedToWindow了。
状态栏,标题栏
Rect frame = new Rect();
getWindow().getDecorView().getWindowVisibleDisplayFrame(frame);
int statusBarHeight = frame.top;
Rect frame = new Rect();
getWindow().getDecorView().getWindowVisibleDisplayFrame(frame);
int statusBarHeight = frame.top
屏幕截图:
public static Bitmap captureScreen(Activity activity) {
activity.getWindow().getDecorView().setDrawingCacheEnabled(true);
Bitmap bmp=activity.getWindow().getDecorView().getDrawingCache();
return bmp;
}
我这里指的略耗时,并不是像网络请求这种的,而是循环次数较多,数据略庞大,这里用:
for(int i = 0; i < 1000000; i++){
Log.e(TAG,"onPause()"+getWindow().getDecorView());
}
代替。
效果:
onStart()-黑屏卡死
onRestart()-卡死无黑屏
onResume()-黑屏卡死
onCreate()-黑屏卡死
onStop()-无影响
onPause()-黑屏卡死
onDestroy()-无影响
onApplyThemeResource()-黑屏卡死
onContentChanged()-黑屏卡死
onPostResume()-黑屏卡死
onAttachedToWindow()-黑屏卡死
onWindowFocusChanged()-无影响
onDetachedFromWindow()-无影响
可见启动过程中,唯一对于略多数据处理没有影响的就是onWindowFocusChanged(),关闭过程中,onStop(),onDestroy(),onDetachedFromWindow()无影响。让我略惊讶的是,onCreate方法中,进行这种处理也会卡死,如果这个数据降到100,对于卡死的方法效果会不同吗?
通过测试发现,其实那些卡死的在少量数据的情况下,或许会略卡顿,但是不会太明显,onCreate是一样的,卡顿效果略改善。所以对于少量数据处理,还是放在onCreate方法中更妥当一些。如果数据量略大,可考虑放在onAttachToWindow(),但是还有个问题,就是失去焦点时也会调用这个方法,需要添加一些条件判断。如果数据量庞大,还是放在异步中处理更妥善一些。
onApplyThemeResource()null
onContentChanged()android.support.v7.widget.AppCompatButton{227e6023 VFED..C. ……I. 0,0-0,0 #7f0c0060 app:id/btn_lifecycle_dialog}
onCreate()android.support.v7.widget.AppCompatButton{227e6023 VFED..C. ……I. 0,0-0,0 #7f0c0060 app:id/btn_lifecycle_dialog}
onStart()android.support.v7.widget.AppCompatButton{227e6023 VFED..C. ……I. 0,0-0,0 #7f0c0060 app:id/btn_lifecycle_dialog}
onResume()android.support.v7.widget.AppCompatButton{227e6023 VFED..C. ……I. 0,0-0,0 #7f0c0060 app:id/btn_lifecycle_dialog}
onPostResume()android.support.v7.widget.AppCompatButton{227e6023 VFED..C. ……I. 0,0-0,0 #7f0c0060 app:id/btn_lifecycle_dialog}
onAttachedToWindow()android.support.v7.widget.AppCompatButton{227e6023 VFED..C. ……I. 0,0-0,0 #7f0c0060 app:id/btn_lifecycle_dialog}
onWindowFocusChanged()android.support.v7.widget.AppCompatButton{227e6023 VFED..C. ……I. 0,0-600,240 #7f0c0060 app:id/btn_lifecycle_dialog}
onPause()android.support.v7.widget.AppCompatButton{227e6023 VFED..C. …….. 0,0-600,240 #7f0c0060 app:id/btn_lifecycle_dialog}
onWindowFocusChanged()android.support.v7.widget.AppCompatButton{227e6023 VFED..C. …….. 0,0-600,240 #7f0c0060 app:id/btn_lifecycle_dialog}
onStop()android.support.v7.widget.AppCompatButton{227e6023 VFED..C. …….. 0,0-600,240 #7f0c0060 app:id/btn_lifecycle_dialog}
onDestroy()android.support.v7.widget.AppCompatButton{227e6023 VFED..C. …….. 0,0-600,240 #7f0c0060 app:id/btn_lifecycle_dialog}
onDetachedFromWindow()android.support.v7.widget.AppCompatButton{227e6023 VFED..C. ……ID 0,0-600,240 #7f0c0060 app:id/btn_lifecycle_dialog}
可以看到,除了onApplyThemeResource都可以获取到view,不过应该不是每个都能对view进行操作吧,试试setText(),结果是,真的都可以!
当然onApplyThemeResource因为获得view为null不可以,就不试了,我这里简单粗暴,直接getWidth()获取控件宽度。
onContentChanged()0
nCreate()0
onStart()0
onResume()0
onPostResume()0
onAttachedToWindow()0
onWindowFocusChanged()600
onPause()600
onWindowFocusChanged()600
onStop()600
onDestroy()600
onDetachedFromWindow()600
可以看到,onWindowFocusChanged是创建过程中唯一可以获得正确的尺寸的。
所实话,这次测试受益很大,尤其是dialog弹出那里,原来有些手机除了销毁已经不怎么用onPause了,这个方法既然在有的手机内不能作为失去焦点的依据,那么以后就要把onWindowFocusChanged(false)当做onPause用了。
说几点关键的:
onAttachedToWindow()-decorView开始不为空,可用于获取状态栏高度等,以后重写后我要把它变成onAttachedDecorView(),便于理解。
onWindowFocusChanged-true可以用来获取尺寸,以及略多的数据初始化,false可以当onPause用
onStart/onResume/onRestart这些还是不要随便用,可以用onWindowFocusChanged(true)代替,这个方法可以处理稍大些的数据,用户体验应该较好。重写时会将true的时候分到onStartDataDimen(),false时分到onPauseDataDimen(),容易记忆。
因为每次打开界面(包括打开dialog)onWindowFocusChanged,home键都会被调用,还可以用于作用于数据保存和恢复,放在intent里面就可以了:
@Override
public void onWindowFocusChanged(boolean hasFocus) {//可以
super.onWindowFocusChanged(hasFocus);
Log.e(TAG,"onWindowFocusChanged()"+getIntent()+num);
if(hasFocus) {
num = getIntent().getIntExtra("num", 0);
boolean isFirst = getIntent().getBooleanExtra("isFirst",true);
if(isFirst){
num = 5;
getIntent().putExtra("isFirst",false);
}
}else
getIntent().putExtra("num",num);
}
这部分可以放到onCreate():
boolean isFirst = getIntent().getBooleanExtra("isFirst",true);
if(isFirst){
num = 5;
getIntent().putExtra("isFirst",false);
}
比onCreate+onSaveInstance靠谱多了,哈哈,这个方法好万能啊,还不会卡顿。
ps:以前都是用post获取的宽高,自定义组件多了白屏就延时加载,呵呵,测过之后被自己蠢哭了T T.