前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Android高频面试专题 - 基础篇(一)Activity

Android高频面试专题 - 基础篇(一)Activity

作者头像
Android扫地僧
发布2020-03-19 16:09:01
1.8K0
发布2020-03-19 16:09:01
举报
文章被收录于专栏:Android进阶Android进阶

本篇主要介绍Activity相关面试题,既有基础知识,也有一些比较冷门的,但是面试官会问的比较晦涩的点。

1、Activity生命周期

1.1 正常情况下的生命周期

Activity启动–>onCreate()–>onStart()–>onResume()

点击home键回到桌面–>onPause()–>onStop()

再次回到原Activity时–>onRestart()–>onStart()–>onResume()

退出当前Activity时–>onPause()–>onStop()–>onDestroy()

1.2 异常情况下的生命周期

情况1:资源相关的系统配置发生改变导致Activity被杀死并重新创建。

情况2:资源内存不足导致低优先级的Activity被杀死。

可以从图中看出当Activity发生意外的情况的时候,这里的意外指的就是系统配置发生改变(在未配置android:configChanges的前提下),如横竖屏切换(切横屏时会执行一次,切竖屏时会执行两次。),开启/关闭暗黑模式,Activity会被销毁,onPause,OnStop,onDestory函数均会被调用。并且会调用onSaveInstanceState,我们可以在这里保存当前Activity状态,比如:文本框中用户输入的数据,ListView滚动的位置等。并通过onRestoreInstanceState来恢复之前的状态,调用onSaveInstanceState的时机总会发生在onStop之前,至于会不会调用时机发生在onPause方法之前,那就说不定了,这个没有固定的顺序可言,onRestoreInstanceState的调用时机发生在onStart之后。

如果我们不想自己的Activity在系统配置发生改变时被销毁后再重建,只需在清单文件中对应Activity下的android:configChanges中添加对应场景配置项,如orientation|screenSize|keyboardHidden。

1.3 什么时候Activity单独走onPause()不走onStop()?

很少情况下Activity才走“onPause”,网上一些关于对话框弹出后Activity会走“onPause”的说法,经过笔者验证,在某个Activity内弹出对话框并没有走“onPause”,所以网上大部分这样说法的文章要么是没验证,要么直接转载的。官方文档介绍,如果要调用onPause()Activity必须Leavesforeground,也就是离开前台或者离开栈顶,那么除非start一个设置为android:theme="@android:style/Theme.Dialog"的Activity,当前Activity才会只走onPause()。

1.4 什么时候Activity不执行onDestory()

栈里面的第一个没有销毁的activity会执行ondestroy方法,其他的不会执行。比如说:从mainactivity跳转到activity-A(或者继续从activity-A再跳转到activity-B),这时候,从后台强杀,只会执行mainactivity的onDestroy方法,activity-A(以及activity-B)的onDestroy方法都不会执行;

1.5 进程重要等级和Activity的关系

前台>可见>服务>后台>空

前台:与当前用户正在交互的Activity所在的进程。

可见:Activity可见但是没有在前台所在的进程。

服务:Activity在后台开启了Service服务所在的进程。

后台:Activity完全处于后台所在的进程。

空:没有任何Activity存在的进程,优先级也是最低的。

1.6 onSaveInstanceState()被执行的场景有哪些:

  • 当用户按下HOME键时
  • 长按HOME键,选择运行其他的程序时
  • 锁屏时
  • 从activity A中启动一个新的activity时
  • 屏幕方向切换时

2. Activity启动流程

(1) Activity1调用startActivity,实际会调用Instrumentation类的execStartActivity方法,Instrumentation是系统用来监控Activity运行的一个类,Activity的整个生命周期都有它的影子。(1- 4)

(2) 通过跨进程的binder调用,进入到ActivityManagerService中,其内部会处理Activity栈,通知Activity1 Pause,Activity1 执行Pause 后告知AMS。(5 - 29)

(3) 在ActivityManagerService中的startProcessLocked中调用了Process.start()方法。并通过连接调用Zygote的native方法forkAndSpecialize,执行fork任务。之后再通过跨进程调用进入到Activity2所在的进程中。(30 - 36)

(4) ApplicationThread是一个binder对象,其运行在binder线程池中,内部包含一个H类,该类继承于类Handler。主线程发起bind Application,AMS 会做一些配置工作,然后让主线程 bind ApplicationThread,ApplicationThread将启动Activity2的信息通过H对象发送给主线程。发送的消息是EXECUTE_TRANSACTION,消息体是一个 ClientTransaction,即 LaunchActivityItem。主线程拿到Activity2的信息后,调用Instrumentation类的newActivity方法,其内通过ClassLoader创建Activity2实例。(37 - 40)

(5) 通知Activity2去performCreate,走生命周期流程。(41 - 最后)

https://www.jianshu.com/p/cf6efb2d1832

3. Activity的启动模式

standard模式

standard模式是Activity默认的启动模式。每启动一个Activity就在栈顶创建一个新的实例。闹钟通常采用此种模式。

singleTop模式

singleTop顾名思义,栈顶单例。如果有时候satndard模式并不合理,比如当前Activity已处于栈顶,再次启动此Activity会重新创建实例,不会直接复用。

将某个Activity的启动模式设置为singleTop,启动此Activity时,会先检查栈顶是否是此Activity的实例,如果是,则直接复用,如果不是,才创建实例。浏览器的书签通常采用此种模式。

singleTask模式

singleTask,顾名思义,任务栈中只有一个实例。

启动某个Activity时,会先检查任务栈中是否有该Activity的实例,有就直接复用(把前面所有的Activity出栈),没有才创建并入栈。浏览器的主界面通常采用此模式。

singleInstance模式

会启动一个新的任务栈来管理当前程序中singleInstance模式启动的Activity,在Android系统中,该Activity只有一个实例。

这种模式主要是为了,在不同程序间共享同一个Activity实例。

启动Activity时,若Android系统中不存在该Activity的实例,则创建并入栈;若已存在,不管此实例位于哪个程序的哪个任务栈中,系统都会把该任务栈转移到前台,显示该实例。来电界面通常使用此模式。

我们假设目前有2个任务栈,前台任务栈的情况为AB,而后台任务栈的情况为CD,这里假设CD的启动模式均为singleTask。现在请求启动D,那么整个后台任务栈都会被切换到前台,这个时候整个后退列表变成了ABCD。当用户按back键的时候,列表中的Activity会一一出栈,如下图1所示:

4.scheme跳转协议

先来个完整的URL Schema协议格式:

SchemaName://hostName:8080/pathName?query

通过上面的路径包括 Schema、Host、port、path、query。

•SchemaName代表该Schema 协议名称

•hostName代表Schema作用于哪个地址域

•pathName代表Schema指定的页面

•query代表传递的参数

•8080代表该路径的端口号 ,可以省略

代码语言:javascript
复制
<activity
  android:name=".ui.activity.XXXX"
  android:configChanges="orientation|keyboard|mcc|mnc|locale|keyboardHidden|uiMode|fontScale">
  <intent-filter>
      <category android:name="android.intent.category.DEFAULT" />
      <action android:name="android.intent.action.VIEW" />
      <data
          android:host="com.ldx.demo"
          android:scheme="example"
          android:path="/ActivityDemo1"/>
  </intent-filter>
</activity>

最终形成的Uri :example://com.ldx.demo/ActivityDemo1

启动方式:uri就是上面生成的字符串,调用之后就会启动对应的Activity

代码语言:javascript
复制
try{
    Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(uri));
    context.startActivity(intent);
} catch (Exception e) {
    e.printStackTrace();
}

Activity中通过getIntent().getData();和getIntent().getSchema();获取数据。

通常用于以下几种场景:

  • 服务器下发跳转路径,客户端根据服务器下发跳转路径跳转相应的页面;
  • H5页面点击锚点,根据锚点具体跳转路径App端跳转具体的页面;
  • App端收到服务器端下发的PUSH通知栏消息,根据消息的点击跳转路径跳转相关页面。

5. 如何将一个 Activity 设置成窗口的样式?

只需要给我们的 Activity 配置如下属性即可。

android:theme="@android:style/Theme.Dialog"

6. 如何用代码安装一个apk文件

代码语言:javascript
复制
Intent intent = new Intent();
intent.setAction(android.content.Intent.ACTION_VIEW);
intent.setDataAndType(Uri.fromFile(file),
                "application/vnd.android.package-archive");
startActivity(intent)

7. App的入口

既然Android是基于Java语言的,而Java有main方法,那么Android有吗?Android的主入口在哪里?

答案:有Main方法,main方法在ActivityThread类中的第 6041行 main(String[] args)

8. 下拉状态栏时Activity的生命周期

下拉状态栏对生命周期没有任何影响,弹出AlertDialog、Toast时都没有影响,重新理解下onPause(),应该修正为“被Activity遮挡”

9. 谈谈隐式启动和显示启动Activity的方式

a. 显示启动方式:

直接指定Activity:

代码语言:javascript
复制
Intent intent = new Intent(A.this,B.class);

指定包名、类名

代码语言:javascript
复制
Intent intent = new Intent();
intent.setClassName("com.android","com.android.B.class");

指定ComponentName

代码语言:javascript
复制
Intent intent = new Intent();
intent.setComponent(new Component("com.android","com.android.B.class"));

b. 隐式启动方式:

只要知道被启动Activity的Action和Category即可,不用知道对应的类名或者是包名,常见的启动浏览器,启动相机等。注意异常处理。

代码语言:javascript
复制
private void startSecondActivityByAction() {
    XLog.i(TAG, "startSecondActivityByAction()");
    Intent intent = new Intent();
    intent.setAction("android.intent.action.SECONDACTIVITY_START");
    intent.addCategory(Intent.CATEGORY_DEFAULT);
    try {
        startActivity(intent);
    } catch (Exception e) {
        XLog.i(TAG, "start activity error!");
    }
}

10.如何给Activity设置进入和退出的动画?

  1. 在AndroidMainfest.xml中为Activity指定theme,theme中设置android:windowAnimationStyle为指定style, style中设置以下属性 <style name="AnimationActivity" parent="@android:style/Animation.Activity"> <item name="android:activityOpenEnterAnimation">@anim/slideinleft</item> <item name="android:activityOpenExitAnimation">@anim/slideoutleft</item> <item name="android:activityCloseEnterAnimation">@anim/slideinright</item> <item name="android:activityCloseExitAnimation">@anim/slideoutright</item> </style>
  2. 通过调用overridePendingTransition(A, B) 可以实时修改Activity的切换动画。但需注意的是:该函数必须在调用startActivity()或者finish()后立即调用,且只有效一次。 其中A是新Activity进入时的动画,B是旧Activity退出时的动画
  3. API21以后可以使用转场动画

11.如何统计Activity的工作时间

定义一个基类Activity,每一个Activity都继承自这个基类,并在这个基类onStart()和onStop()方法中进行上报,继而就可以统计到每个页面的PV、页面留存时间,同时还可以在基类中做一些优化设置

12.a->b->c界面,其中b是SingleInstance的,那么c界面点back返回a界面,为什么

singleInstance模式是存在于另一个任务栈中的。也就是说ActivityA和ActivityC是处于同一个任务栈中的,ActivityB则是存在另个栈中。所以当关闭了ActivityC的时候,它自然就会去找当前任务栈存在的activityA。当前的activity都关闭了之后,才会去找另一个任务栈中的activity。也就是说当在ActivityC中finish之后,会回到ActivityA的界面,在ActivityA里finish之后会回到ActivityB界面。

13. Activity常用的标记位Flags

FLAG_ACTIVITY_NEW_TASK

此标记位作用是为Activity指定“singleTask”启动模式,其效果和在XML中指定相同android:launchMode="singleTask"

FLAG_ACTIVITY_SINGLE_TOP

此标记位作用是为Activity指定“singleTop”启动模式,其效果和在XML中指定相同android:launchMode="singleTop"

FLAG_ACTIVITY_CLEAR_TOP

具有此标记位的Activity,当它启动时,在同一个任务栈中位于它上面的Activity都要出栈。此标记位一般会和singleTask启动模式一起出现,此情况下,若被启动的Activity实例存在,则系统会调用它的onNewIntent。

FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS

具有此标记位的Activity不会出现在历史Activity的列表中,当某些情况我们不希望用户通过历史列表回到我们的Activity时这个标记比较有用。他等同于在XML中指定Activity的excludeFromRecents属性。 android:excludeFromRecents="true"

14.你知道onNewIntent吗?

如果IntentActivity处于任务栈的顶端,也就是说之前打开过的Activity,现在处于onPause、onStop 状态的话,其他应用再发送Intent的话,执行顺序为:onNewIntent,onRestart,onStart,onResume。

15.Activity进程优先级

  • 优先级最高: 与用户正在进行交互的Activity,即前台Activity。
  • 优先级中等:可见但非前台的Activity,比如:一个弹出对话框的Activity,可见但是非前台运行。
  • 优先级最低:完全存在于后台的Activity,比如:执行了onStop。

16. 有什么方法可以启动一个没有在AndroidManifest.xml中注册过的Activity

通过Hook AMS,插件化技术原理,用一个已经注册过的Activity去欺骗AMS和PMS的检查,然后真正创建Ativity和启动的时机替换成真的Activity。

17. 在Activity中可以多次调用setContentView方法吗?说说不同时机第二次调用setContentView会发生什么?

setContentView方法所指定的View,只有在onCreate方法返回后才会显示在界面上。因此,如果调用了两次setContentView方法,只有最后一次才是有效的。

18.Activity用Intent传递数据和Bundle传递数据的区别?为什么不用HashMap呢

  • Bundle内部是由ArrayMap实现的,ArrayMap的内部实现是两个数组,一个int数组是存储对象数据对应下标,一个对象数组保存key和value,内部使用二分法对key进行排序,所以在添加、删除、查找数据的时候,都会使用二分法查找,只适合于小数据量操作,如果在数据量比较大的情况下,那么它的性能将退化。而HashMap内部则是数组+链表结构,所以在数据量较少的时候,HashMap的Entry Array比ArrayMap占用更多的内存。因为使用Bundle的场景大多数为小数据量,我没见过在两个Activity之间传递10个以上数据的场景,所以相比之下,在这种情况下使用ArrayMap保存数据,在操作速度和内存占用上都具有优势,因此使用Bundle来传递数据,可以保证更快的速度和更少的内存占用。
  • 另外一个原因,则是在Android中如果使用Intent来携带数据的话,需要数据是基本类型或者是可序列化类型,HashMap使用Serializable进行序列化,而Bundle则是使用Parcelable进行序列化。而在Android平台中,更推荐使用Parcelable实现序列化,虽然写法复杂,但是开销更小,所以为了更加快速的进行数据的序列化和反序列化,系统封装了Bundle类,方便我们进行数据的传输

19.Activity使用Intent传递数据是否有限制 & 如果传递一个复杂的对象,例如一个复杂的控件对象应该怎么做?

intent传递数据有限制,实质上是由Binder内核传递,并不是为了传输大量数据而设计,而是为了进程间频繁通信所设计,内核限制是4M,在APP中限制了不到1M(比1M略小的值),真机中可能还有其他任务在占用,最好在512k以内。否则会报错TransactionTooLargeException。

改进方案:

一. 限制传递数据量

二. 改变数据传输方式(参见Activity之间传递数据的方式)

1. 静态static

2. 单例

3. Application

4. 持久化

20. 如何在Application中获取当前Activity实例

在Application类,通过实现Application.ActivityLifecycleCallbacks接口调用registerActivityLifecycleCallbacks,在onActivityResumed(Activity activity) 方法中记录当前显示的Activity,注意内存泄漏。


本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2020-02-24,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Android扫地僧 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 7. App的入口
    • FLAG_ACTIVITY_NEW_TASK
      • FLAG_ACTIVITY_SINGLE_TOP
        • FLAG_ACTIVITY_CLEAR_TOP
          • FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
          领券
          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档