Android程序员面试笔试宝典
上QQ阅读APP看书,第一时间看更新

1.1.4 常见面试笔试真题

1)请描述一下Activity生命周期及其各方法。

思路:

遇到此类问生命周期的题第一时间是要先想到Activity的一个完整的生命周期是怎样的,然后再具体到每个阶段的方法又是怎样的。

解答:

一个Activity的完整生命周期是:

onCreate()→onStart()→onResume()→onPause()→onStop()→onDestroy()。

●onCreate():Activity第一次被创建的时候调用,通常在该方法里进行一些初始化操作,例如加载布局、组件和绑定事件等;

●onStart():当Activity创建完后,此时由不可见状态变成可见状态,调用onStart();

●onResume():处于运行状态(Activity位于工作栈栈顶)时调用,用户可进行触碰点击页面上的各种按钮,从而与活动进行交互;

●onPause():当前Activity去启动其他活动时会调用,例如Activity在运行中,用户点击某个按钮触发了一个对话框形式的活动弹出,此时Activity处于暂停状态,触发onPause(),但Activity并不是完全不可见的;

●onStop():Activity处于完全不可见状态就调用,注意它跟onPause()的区别;

●onDestroy():Activity销毁前调用,调用之后Activity就会被销毁。

2)如果一个Activity正在运行,这时从前台切换到后台,然后再回到前台,这一整个过程Activity的生命周期是怎样的?

思路:

这类题就是典型的结合例子来考查面试者对Activity生命周期每个方法的熟悉度了。其实不管例子怎么变,只需把题中的几个关键词给找出来,然后与Activity生命周期各个方法联系起来即可。很明显,该题中几个关键词分别是:“正在运行”“前台切换到后台”和“再回到前台”,相信看到这个几个关键词应该能有一个解题思路了吧。

解答:

该Activity从被创建到正在运行,依次调用了onCreate()、onStart()和onResume();然后Activity从前台切换到后台中,该Activity这时调用onPause(),然后等完全切换到了后台画面后,Activity调用onStop();这时再把Activity切换回前台中去,Activity此时依次调用onRestart()、onStart()和onResume()方法。以上就是该题中Activity的生命周期全过程了。

3)Activity在横竖屏切换的时候生命周期是怎样的?

思路:

像这样的题显然靠理论知识是很难推测出来的,所以面试官也是想考察求职者的实践代码能力是否够强。那么就用代码厘清此题的思路。

这里新建一个活动类BActivity,如下所示:

然后运行,打印它的生命周期方法:

然后切换横屏:

现在再切回竖屏:

现在在配置文件AndroidManifest.xml中为BActivity添加:android:configChanges="orientation",然后运行后,切换到横屏:

此时再切回竖屏:

之后又在AndroidManifest.xml上为BActivity添加:android:configChanges="orientation|screen Size",然后重复上面的步骤,打印的结果如下:

解答:

看到上面的分析与打印出来的结果,可以得出如下结论。

①当不设置configChanges属性时,Activity在切换横竖屏的时候,会执行各个生命周期的方法,横屏执行一轮,竖屏也执行一轮;

②当为Activity设置android:configChanges="orientation"后,再进行横竖屏切换的时候,Activity不再执行生命周期其他方法,横竖屏各执行一次onConfigurationChanged()方法;

③当为Activity设置android:configChanges="orientation|screenSize"后,跟设置"orientation"的效果一样。

另外,configChanges属性的其他值的作用如下:

●orientation:屏幕在纵向和横向间旋转;

●screenSize:屏幕大小改变了;

●keyboard:键盘类型变更,例如手机从12键盘切换到全键盘;

●keyboardHidden:键盘显示或隐藏;

●fontScale:用户变更了首选的字体大小;

●locale:用户选择了不同的语言设定。

4)请描述一下Activity的4种启动模式以及它们的应用场景。

思路:

既然问到启动模式,就首先应该在脑海中把4种模式的原理图给想象出来。

standard模式的原理如图1.3所示。

singleTop模式的原理如图1.4所示。

图1.3 standard模式

图1.4 singleTop模式

singleTask模式的原理如图1.5所示。

图1.5 singleTask模式

singleInstance模式的原理如图1.6所示。

图1.6 singleInstance模式

只要能把它们对应的概念图给想出来,那就能把这4种模式交代清楚了。

解答:

standard:标准模式,Activity默认就是standard模式。它也是常说的在正常情况下的模式,每次启动一个活动时,就会创建一个实例,然后被启动的活动入栈,并处于栈顶的位置。

singleTop:该模式下启动Activity,系统会先检查任务栈栈顶是否有该Activity实例,如果有则直接使用它,如果没有则创建新的实例并且入栈到栈顶。一般会将具有推送信息展示的Activity指定为singleTop,因为往往实际中可能会有很多条推送发送与展示,不可能每发一条推送,其Activity就创建一个新例。

singleTask:如果是在同一个应用中(taskAffinity值一样)启动Activity时,系统先检测任务栈中是否存在该Activity,如果存在则直接使用该活动实例,让它置于栈顶,而它之上的其他活动纷纷出栈。如果不存在该实例,则创建该活动新的实例,置于栈顶。如果不是在同一应用中(taskAffinity值不一样),而是其他应用中来启动该模式下的Activity时,那么系统会创建一个新的任务栈,然后创建该活动新的一个实例,将它置于该新建任务栈栈顶。一般实际开发中会将具有程序入口等启动页面的Activity指定为singleTask,这样就可避免在启动页退出的时候因存在多个实例而重复点击几次才能退出的问题。而指定taskAffinity是在AndroidManifest.xml文件里的activity标签里指定:

不指定android:taskAffinity属性,那么它的值默认就是包名。

singleInstance:该模式下启动Activity时,系统会先创建一个新的任务栈来专门存储与管理该Activity。singleInstance跟singleTask类似,唯一的区别就是在singleInstance模式下的Activity具有全局唯一性,就是一个任务栈只有该Activity,任何应用如果启动该Activity使用的都是这个任务栈中的这个实例。该模式的应用场景多用于与其他应用进行交互的情况,例如:闹铃或者紧急呼叫等。

5)onNewIntent()与4种启动模式有着什么样的关系?

思路:

standard:在默认模式下,每次启动Activity,创建新的实例,但不会调用onNewIntent()。

singleTop:在该模式下,启动的Activity只要在栈顶,就复用该实例,然后就会触发onNewIntent(),如果任务栈不存在该实例,就会创建新实例,可此时并不会调用onNewIntent()。

singleTask:在该模式下,启动的Activity只要任务栈存在该实例,就复用它,此时触发onNewIntent(),如果任务栈不存在该实例,就会创建新实例,可此时并不会调用onNewIntent()。

singleInstance:在该模式下,启动的Activity只要任务栈存在该实例(该模式下的活动是单独占用一个任务栈),就复用它,此时onNewIntent()触发。

上述情况也可以总结为当Activity被restart或者Activity位于栈顶时被再次start这两种情况就会调用onNewIntent()。所以,无论是哪一种模式,只有Activity是在同一个实例的情况下,intent发生了变化,就会调用onNewIntent(),onNewIntent()的作用是让开发者在里面对旧的intent进行保存,对新的intent进行相应处理。

解答:

可以用代码来分析,BActivity设置为singleTask模式,然后点击按钮启动MainActivity,然后跳转到MainActivity页面,再点击按钮启动BActivity,这两个Activity的代码跟1.1.2小节中的代码类似,只不过再多增加一个方法:

然后看一看打印它们的生命周期是怎样的,先运行BActivity的代码,结果如下:

然后跳转到MainActivity后:

可以看到此时BActivity最后调用的是onStop()方法。接着继续点击按钮启动BActivity后:

当再次启动BActivity时BActivity就会执行onNewIntent()方法。另外需要注意的是如果调用了onNewIntent()方法,那就需要在onNewIntent()方法中调用setIntent(intent)给Activity设置Intent对象,这样做是为了避免在调用getIntent()获取Intent时,得到的是旧的Intent值。

其他模式也可以这样去分析,这里就不做讲解了,可自行去实践探索。

6)假如活动A启动活动B,而活动B启动活动C,现在需要在活动C的界面点击back键退回到活动A,应该要怎么做?

思路:

看完这题能想到的思路是跟启动模式有关的,所以这时得想起4种启动模式的概念图。然后进行分析,哪种启动模式才是能实现该题需求的。

解答:

可以想象一个任务栈,A启动B,那就是在该栈中B在A上,然后B启动C,那此时栈中从高到低就是C、B、A,然后题目说需要C点击back键后退到的页面是A,那这样显然不对,因为现在的这种方式(其实是singleTask),C点击回退键,退到的页面是B,所以再排除默认模式与singleTop,剩下的只有singleInstance单例模式了。因为上文已经说到了,singleInstance模式下的Activity单独占用一个任务栈,具有唯一性,整个系统就是一个实例,当其他程序要启动该活动时,也是复用的是这个实例,所以活动B就是设置了singleInstance,单独占用了一个任务栈,而A和C则是同一个任务栈的,而此时活动C点击回退键后,C出栈,而在其后的A也就处于栈顶,符合题目的情况了。

如果还是觉得有点抽象,可以看着singleInstance模式图来自行理解。

7)Activity启动模式的标记位有哪些?

思路:

标记位的作用就是指定活动的启动模式,它跟之前在配置文件AndroidManifest.xml中用属性android:launchMode来指定的方式不一样,标记位是通过代码在Intent中设置来指定Activity的启动模式的:intent.addFlags(Intent.标记位)。

解答:

经常用到的标记位有以下:

●FLAG_ACTIVITY_NEW_TASK:相当于为Activity指定“singleTask”启动模式;

●FLAG_ACTIVITY_SINGLE_TOP:相当于为Activity指定“singleTop”启动模式;

●FLAG_ACTIVITY_CLEAR_TOP:此模式下的Activity启动时,在同一个任务栈中的所有位于它之上的Activity都要出栈。该标记位一般和singleTask一起出现,此时Activity启动,如果实例存在,则直接使用该实例,并触发onNewIntent();

●FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS:该Activity不会在最近启动的Activity的历史列表中保存。

8)如何启动其他应用的Activity?

思路:

该题也是考查Activity的启动模式。

解答:

启动的是其他应用的Activity,所以就该使用singleInstance模式了,因为singleInstance模式下的Activity是单独占用一个任务栈,具有全局唯一性,这样其他应用也是复用该实例;当然,也可以使用singleTask模式,这样就要在配置文件AndroidManifest.xml中指定该Activity的taskAffinity值,让启动方活动与被启动方活动处在不同的任务栈,这样就能达到同样的效果。

9)Activity的状态是怎样保存与恢复的?

思路:

回答该题时应该从3个方面去思考,Activity的状态是什么?哪些情况是需要去保存与恢复状态?怎样去保存与恢复?

解答:

①Activity的状态是什么?其实就是数据,当需要对一些临时的数据进行操作,这时因为一些突发原因,没来得及保存,就被清除了,这时就需要对这些状态数据进行保存与恢复;

②哪些突发状况需要去保存与恢复?设备配置在运行时发生变化,例如屏幕方向、键盘可用性等;

③怎样去保存与恢复?在Activity生命周期中,Android会在销毁Activity之前调用onSaveInstanceState(),以便保存有关应用状态的数据。然后,可以在onCreate()或onRestoreInstance State()期间恢复Activity状态。

一般在onPaused()之后onStop()之前使用onSaveInstanceState()保存数据:

然后在onStart()之后onResume()之前使用onRestoreInstanceState()对数据进行恢复:

当然也可以直接在onCreate()里恢复:

10)说一说Activity与Fragment两者生命周期的区别。

思路:

只要把它们的生命周期都描述清楚,然后再说出Fragment那几个Activity没有的方法即可。

解答:

Fragment与Activity之间的生命周期:

●Fragment:onAttach();

●Fragment:onCreate();

●Fragment:onCreateView();

●Activity:onCreate();

●Fragment:onActivityCreated();

●Activity:onStart();

●Fragment:onStart();

●Activity:onResume();

●Fragment:onResume();

●Fragment:onPause();

●Activity:onPause();

●Fragment:onStop();

●Activity:onStop();

●Fragment:onDestroyView();

●Fragment:onDestroy();

●Fragment:onDetach();

●Activity:onDestroy()。

相对于Activity来说,Fragment多了几个方法:

●onAttach():当Fragment与Activity建立关联时触发;

●onCreateView():当Fragment创建视图(布局)时触发;

●onActivityCreated():当与Fragment相关联的Activity已经创建完时触发;

●onDestroyView():当Fragment的视图被移除时触发;

●onDetach():当Fragment和相关联的Activity解除关联时触发。

11)如何实现Fragment的滑动?

思路:

实现Fragment的滑动有很多组合搭配方案,这么多方案,其实原理都是ViewPager +Fragment或使用第三方封装好的滑动组件。

解答:

为ViewPager定义一个适配器,这个适配器需要继承PagerAdapter,传一个List数据给该适配器,该List数据就是存储Fragment,想滑动多少个Fragment都能放进去,最后实现ViewPager的onPageScrolled()、onPageSelected()、onPageScrollStateChanged()方法,就能实现Fragment滑动了,实际上App启动轮播页也可以用该方式实现。

12)Fragment之间传递数据的方式有哪些?

思路:

可以采用最传统的方法就是接口回调,也可以使用现有的框架如EventBus。

解答:

①可以在FragmentA中定义一个接口以及对应的set方法,然后在接口里面定义一个方法dataChange(),参数data是我们要传递的数据:

然后采用回调方式进行数据传递:

此时就能在Activity中实现FragmentA接口和里面的方法了,最后在FragmentB中定义供Activity调用的方法,参数的类型跟FragmentA中参数data的类型一样,也为String类型,这样就能达到FragmentA与FragmentB数据传递的效果了:

②用EventBus传值,在FragmentA中调用EventBus.getDefault().post()传递数据,然后在FragmentB中调用onEvent()接收与处理传递过来的数据。

13)Activity与Fragment之间如何通信?

思路:

该题跟第12题类似,只不过这题是切换到了Activty与Fragment的交互,可以理解为它们两者怎么获取到对方的实例,这就是关键所在。

解答:

Fragment可以调用getActivity()方法来获得Activity的实例,获得实例后就能调用Activity的各种方法及其数据,当然,这里的Fragment和Activity是互相关联的。

在第12题中,其实已经用Activity与FragmentA进行交互了,就是利用接口回调的方式进行的:在Fragment中定义一个接口以及对应的set方法,接口里面定义一个方法,参数是我们要交互的数据,然后我们采用回调方式进行数据传递,最后在Activity中实现Fragment接口和里面的方法。其中,我们Activity要获得Fragment的实例,所以调用FragmentManager的findFragmentById()或者findFragmentByTag()来获取。

14)什么情况下会考虑使用Fragment?

思路:

通常为什么会用一样东西,是因为这个东西有它的优点,所以要结合Fragment的优点与实际开发需求来思考。

解答:

现在除了手机,还有各种各样的平板电脑也是用Android系统的,由于平板电脑的屏幕要比手机的大很多,所以可能会出现这样的现象:一些应用的UI在手机上看起来美观,可在平板上显示,就会出现画面被拉大的不协调感,影响了UI美观。所以我们开发平板电脑的应用时,可以用Fragment,因为Fragment灵活且复用性强,便于管理各个布局的组合,解耦性强。当要开发一些视图层结构比较复杂的布局,也可以考虑用Fragment。

15)Activity之间的通信方式有哪些?

思路:

把常用的方法(Intent、Bundle、工具类等)说清楚,结合代码来分析。

解答:

●Intent,当使用Intent进行跳转启动活动时,可以先把要传递的数据放置在Intent中,然后当成功启动了另一个活动后,再从Intent里取出刚传递过来的数据。

假设ActivityA启动ActivityB,在ActivityA中:

然后在ActivityB中获取数据:

●Bundle,也可以通过创建Bundle的bundle.putXXX()把数据存进Bundle里去,然后再通过intent.putExtras(bundle)将bundle存到intent中去,这样等启动了ActivityB后就逐一往intent、bundle里取数据值。

●还可以使用Java中类的静态成员来进行交互,即“类名.属性名”。所以可以在ActivityA中定义一个静态成员,倘若ActivityB想访问该静态成员,直接在AcitvityB中通过“ActivityA.属性名”格式来获取就可以了:

●通过文件存储、SharedPreference、数据库、内容提供者等外部工具来进行交互。

16)onStart()和onResume()有什么区别?onPause()和onStop()有什么区别?

思路:

分析它们被调用的时候活动所处于的状态是怎样的。

解答:

onStart()是在活动从不可见变成可见时触发的,但此时活动还不能与用户交互,而onResume()则是代表活动可以跟用户进行交互,也就是用户此时可以通过触碰可见页面上的各种按钮,此时活动属于运行状态;onPause()是活动因为一些原因(例如对话框形式的活动弹出)导致当前活动处于一种“半透明”显示的暂停状态,此时触发onPause(),而onStop()则是代表活动,此时变为完全不可见。