2019年的冬天有点冷,一份基础面试题送给还在奋斗在Android领域的同学.
Android基础问题
Activity&View系列
简述Android的布局分类?
早期Android官方提供以下五种布局:
- LinearLayout
- RelativeLayout
- FrameLayout
- GridLayout
- TableLayout
以上传统的布局,以LinearLayout和RelativeLayout最为常用,但是或多或少有些缺陷,比如RelativeLayout需要两次measure它的子View才能之道确切的大小;而LinearLayout布局的子View在使用layout_weight属性后,同样也需要两次measure过程.
为此Android官方推出了两种新的布局:
- ConstraintLayout: 约束布局,类似RelativeLayout,但更加灵活,再复杂的界面也可以只有2层
- FlexboxLayout: 弹性布局,类似前端FlexBox,一种更高级的LinearLayout
如何分析布局问题?
判断布局是否有问题,可以借助Hierarchy Viewer工具,简称HV,该工作可以分析每个布局的measure,layout,draw三个过程的耗时.
需要注意的是在Root设备或者Dev版的ROM上可以直接使用,而在没有Root的手机需要在PC端添加环境变量
ANDROID_HVPROTO=ddm
谈谈Activity的生命周期
Activity实例是由系统自动创建,并在不同的状态期间回调相应的方法.一个最简单的完整的Activity生命周期会按照如下顺序回调:onCreate -> onStart -> onResume -> onPause -> onStop -> onDestroy
.
- 启动Activity:
onCreate() -> onStart() -> onResume()
,Activity进入运行状态 - Activity退居后台: 当前Activity转到新的Activity界面或按Home键回到主屏:
onPause() -> onStop()
- Activity返回前台:
onRestart() -> onStart() -> onResume()
,再次回到运行状态
介绍不同场景下Activity生命周期的变化过程
假设A Activity位于栈顶,此时用户操作,从A Activity跳转到B Activity.那么对AB来说.具体会回调哪些生命周期中的方法呢?回调方法的具体回调顺序又是怎么样的呢?
- 当用户点击A中按钮来到B时,假设B全部遮挡住了A,将依次执行A:
onPause -> B:onCreate -> B:onStart -> B:onResume -> A:onStop
. - 此时如果点击Back键,将依次执行
B:onPause -> A:onRestart -> A:onStart -> A:onResume -> B:onStop -> B:onDestroy
至此,Activity栈中只有A.在Android中,有两个按键在影响Activity生命周期这块需要格外区分下,即Back键和Home键.
- 此时如果按下Back键,系统返回到桌面,并依次执行
A:onPause -> A:onStop -> A:onDestroy
- 此时如果按下Home键(非长按),系统返回到桌面,并依次执行
A:onPause -> A:onStop
.由此可见Back键和Home键主要区别在于是否会执行onDestroy.
开启开发者选项中"不保留活动"对Activity生命周期有什么影响?
开启此设置项后,当A到B时,假设B全部遮挡住了A,将依次执行:
A:onPause -> B:onCreate -> B:onStart -> B:onResume -> A:onStop -> A:onDestroy
不难看出,A在系统原本的生命周期回调中增加了onDestroy,此即“用户离开后即销毁每个活动”的含义.需要注意的是,只要没有人为的调用A的finish()
方法,虽然A执行了onDestroy,但Activity栈中依然保留有A,此时B处于栈顶.
那么在B中按Back键回到A时,将依次执行:
B:onPause -> A:onCreate -> A:onStart -> A:onResume -> B:onStop -> B:onDestroy
可以看出A从onCreate开始执行.
Activity的finish()
和onDestory()
联系?
-
对于
finish()
方法:当调用此方法的时候系统只是将最上面的Activity移出了栈,并没有及时的调用onDestory()
方法,其占用的资源也没有被及时释放.因为移出了栈,所以当你点击手机上面的“back”按键的时候,也不会再找到这个Activity -
对于
onDestory()
方法:系统销毁了这个Activity的实例在内存中占据的空间.在Activity的生命周期中,onDestory()方法是他生命的最后一步,资源空间等就被回收了.当重新进入此Activity的时候,必须重新创建,执行onCreate()方法
简单来说就是:finish()
方法用于结束一个Activity的生命周期,而onDestory()
方法则是Activity的一个生命周期方法,其作用是在一个Activity对象被销毁之前,Android系统会调用该方法,用于释放此Activity之前所占用的资源.
如何判断Activity是否已经被销毁?简写代码
if (activity == null || activity.isDestroyed() || activity.isFinishing()) {
return;
}
需要注意isFinishing()
只有在主动调用finish()
方法主动结束一个Activity时才会返回true.
Activity销毁但Task如果没有销毁掉,当Activity重启时这个AsyncTask该如何解决?
当一个屏幕发生旋转时,当前Activity会被销毁和重建.在当Activity重启时,AsyncTask持有该Activity将失效,因此onPostExecute()不会起作用.若AsynTask正在执行,此时会报 view not attached to window manager 异常.
通常对于生命周期问题,一般的解决思路是将其进行同步,对于AsyncTask而言可以在Activity的onDestory()
方法中调用Asyntask.cancel()
方法.
现在有两个应用A和B,先打开A应用,假设依次打开其A-Page1,A-Page2,A-Page3三个Activity,此时回到桌面,打开B应用,假设由于B应用非常大,促使系统杀死了A应用,此时回到桌面,点击A应用图标,请问出现什么情况?
对于由于系统原因导致进程被杀死的情况,在重新打开该应用的时候,系统会恢复进程被杀死之前栈顶的元素,此时我们看到的是A-Page3,按back键后A-Page2会被重建,继续按back键后A-Page1会被重建.
应用被杀死之前:
后台被杀死的情况:
应用恢复后的情况:
继续点back键的情况:
内存不足时,系统会杀死后台的Activity,此时如何保存当前Activity的某些状态?如何恢复?
有A,B两个Activity,当从A进入B之后一段时间,可能系统会把A回收,这时候按back,执行的不是A的onRestart而是onCreate方法即A被重新创建一次,这A中的临时数据和状态可能就丢失了.在Activity中,提供了onSaveInstanceState()/onRestoreInstanceState()
回调方法用于临时保存/恢复数据,其使用实例代码如下:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if( savedInstanceState != null ){
savedInstanceState.getString("anAnt");
}
}
@Override
protected void onSaveInstanceState(Bundle outState) {
// 将需要的数据保存到outState中
super.onSaveInstanceState(outState);
}
@Override
public void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
// 从savedInstanceState中取出数据
}
}
onSaveInstanceState()
的工作原理是什么?
当某个Activity变得“容易”被系统销毁时,该Activity的onSaveInstanceState()
就会被调用,除非该activity是被用户主动销毁的,例如当用户按BACK键的时候.
那Activity什么时候处在"容易"被销毁时呢?在以下几种情况中,Activity处在易被销毁的状态:
- 按下HOME键后,该Activity进入后台,由于系统不知道我们按下HOME后要可能要运行多少其他的程序,也就不知道该Activity是否会被销毁,此时系统会调用
onSaveInstanceState()
- 长按HOME键,选择运行其他应用程序,原理同上
- 按下电源按键(关闭屏幕显示)
- 从Activity A中启动一个新的Activity时,由于系统可能会在某些时机回收掉A,因此系统会调用
onSaveInstanceState()
- 屏幕方向切换时,会导致Activity实例重建,因此系统需要调用
onSaveInstanceState()
以便在重建后的Activity显示之前的Activity状态.
简单来说,只要系统未经你许可而销毁了你的Activity是,系统一定会调用onSaveInstanceState()
.对于该方法,需要注意一下几点:
- 只有你为布局中的每个View设置了id,且实现了其
onSaveInstanceState()
时,系统才会对这个UI的任何改变自动存储及恢复. - 重写该方法时,最好需要显示调用
super.onSaveInstanceState()
,如果需要额外保存自己的UI数据,那么在进行额外操作. - 不要滥用
onSaveInstanceState()
,需要谨记其目的是为了保存当前界面的瞬时状态.
onRestoreInstanceState()的工作原理是什么?
onRestoreInstanceState(
)被调用的前提是,Activity A“确实”被系统销毁了,而不是处在可能被销毁的状态下.比如当正在显示Activity A的时候,按下HOME键回到主界面,紧接着又返回到Activity A,这种情况下Activity A一般不会因为内存的原因被系统销毁,因此Activity A的onRestoreInstanceState()
方法不会被执行.
onRestoreInstanceState()
在onStart()
后进行回调,此外onRestoreInstanceState()
中的bundle参数也会传递到onCreate(Bundle savedInstanceState)
方法中.
那在什么时候进行数据恢复呢?或者说恢复在这两个方法中进行恢复操作有什么不同?当在onCreate()
中恢复数据时,需要对savedInstanceState进行判空操作,而在