Android 篇:2019 初中级 Android 开发社招面试解答(上,作为 Android 开发者
Android 篇
Activity
1、说下 Activity 生命周期 ?
参考解答:在正常情况下,Activity 的常用生命周期就只有如下 7 个
onCreate():表示 Activity 正在被创建,常用来初始化工作,比如调用 setContentView 加载界面布局资源,初始化 Activity 所需数据等;
onRestart():表示 Activity 正在重新启动,一般情况下,当前 Acitivty 从不可见重新变为可见时,OnRestart 就会被调用;
onStart():表示 Activity 正在被启动,此时 Activity 可见但不在前台,还处于后台,无法与用户交互;
onResume():表示 Activity 获得焦点,此时 Activity 可见且在前台并开始活动,这是与 onStart 的区别所在;
onPause():表示 Activity 正在停止,此时可做一些存储数据、停止动画等工作,但是不能太耗时,因为这会影响到新 Activity 的显示,onPause 必须先执行完,新 Activity 的 onResume 才会执行;
onStop():表示 Activity 即将停止,可以做一些稍微重量级的回收工作,比如注销广播接收器、关闭网络连接等,同样不能太耗时;
onDestroy():表示 Activity 即将被销毁,这是 Activity 生命周期中的最后一个回调,常做回收工作、资源释放;
延伸:从整个生命周期来看,onCreate 和 onDestroy 是配对的,分别标识着 Activity 的创建和销毁,并且只可能有一次调用; 从 Activity 是否可见来说,onStart 和 onStop 是配对的,这两个方法可能被调用多次; 从 Activity 是否在前台来说,onResume 和 onPause 是配对的,这两个方法可能被调用多次; 除了这种区别,在实际使用中没有其他明显区别;
2、Activity A 启动另一个 Activity B 会调用哪些方法?如果 B 是透明主题的又或则是个 DialogActivity 呢 ?
参考解答:Activity A 启动另一个 Activity B,回调如下
Activity A 的 onPause() → Activity B 的 onCreate() → onStart() → onResume() → Activity A 的 onStop();
如果 B 是透明主题又或则是个 DialogActivity,则不会回调 A 的 onStop;
3、说下 onSaveInstanceState()方法的作用 ? 何时会被调用?
参考解答:发生条件:异常情况下(系统配置发生改变时导致 Activity 被杀死并重新创建、资源内存不足导致低优先级的 Activity 被杀死)
系统会调用 onSaveInstanceState 来保存当前 Activity 的状态,此方法调用在 onStop 之前,与 onPause 没有既定的时序关系;
当 Activity 被重建后,系统会调用 onRestoreInstanceState,并且把 onSave(简称)方法所保存的 Bundle 对象同时传参给 onRestore(简称)和 onCreate(),因此可以通过这两个方法判断 Activity 是否被重建,调用在 onStart 之后;
推荐文章:
4、说下 Activity 的四种启动模式、应用场景 ?
参考回答:
standard 标准模式:每次启动一个 Activity 都会重新创建一个新的实例,不管这个实例是否已经存在,此模式的 Activity 默认会进入启动它的 Activity 所属的任务栈中;
singleTop 栈顶复用模式:如果新 Activity 已经位于任务栈的栈顶,那么此 Activity 不会被重新创建,同时会回调 onNewIntent 方法,如果新 Activity 实例已经存在但不在栈顶,那么 Activity 依然会被重新创建;
singleTask 栈内复用模式:只要 Activity 在一个任务栈中存在,那么多次启动此 Activity 都不会重新创建实例,并回调 onNewIntent 方法,此模式启动 Activity A,系统首先会寻找是否存在 A 想要的任务栈,如果不存在,就会重新创建一个任务栈,然后把创建好 A 的实例放到栈中;
singleInstance 单实例模式:这是一种加强的 singleTask 模式,具有此种模式的 Activity 只能单独地位于一个任务栈中,且此任务栈中只有唯一一个实例;
推荐文章:
5、了解哪些 Activity 常用的标记位 Flags?
参考回答:
FLAG_ACTIVITY_NEW_TASK : 对应 singleTask 启动模式,其效果和在 XML 中指定该启动模式相同;
FLAG_ACTIVITY_SINGLE_TOP : 对应 singleTop 启动模式,其效果和在 XML 中指定该启动模式相同;
FLAG_ACTIVITY_CLEAR_TOP : 具有此标记位的 Activity,当它启动时,在同一个任务栈中所有位于它上面的 Activity 都要出栈。这个标记位一般会和 singleTask 模式一起出现,在这种情况下,被启动 Activity 的实例如果已经存在,那么系统就会回调 onNewIntent。如果被启动的 Activity 采用 standard 模式启动,那么它以及连同它之上的 Activity 都要出栈,系统会创建新的 Activity 实例并放入栈中;
FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS : 具有这个标记的 Activity 不会出现在历史 Activity 列表中;
推荐文章:
6、说下 Activity 跟 window,view 之间的关系?
参考回答:
Activity 在创建时会调用 attach() 方法初始化一个 PhoneWindow(继承于 Window),每一个 Activity 都包含了唯一一个 PhoneWindow
Activity 通过 setContentView 实际上是调用的 getWindow().setContentView 将 View 设置到 PhoneWindow 上,而 PhoneWindow 内部是通过 WindowManager 的 addView、removeView、updateViewLayout 这三个方法来管理 View,WindowManager 本质是接口,最终由 WindowManagerImpl 实现
延伸
WindowManager 为每个 Window 创建 Surface 对象,然后应用就可以通过这个 Surface 来绘制任何它想要绘制的东西。而对于 WindowManager 来说,这只不过是一块矩形区域而已
Surface 其实就是一个持有像素点矩阵的对象,这个像素点矩阵是组成显示在屏幕的图像的一部分。我们看到显示的每个 Window(包括对话框、全屏的 Activity、状态栏等)都有他自己绘制的 Surface。而最终的显示可能存在 Window 之间遮挡的问题,此时就是通过 SurfaceFlinger 对象渲染最终的显示,使他们以正确的 Z-order 显示出来。一般 Surface 拥有一个或多个缓存(一般 2 个),通过双缓存来刷新,这样就可以一边绘制一边加新缓存。
View 是 Window 里面用于交互的 UI 元素。Window 只 attach 一个 View Tree(组合模式),当 Window 需要重绘(如,当 View 调用 invalidate)时,最终转为 Window 的 Surface,Surface 被锁住(locked)并返回 Canvas 对象,此时 View 拿到 Canvas 对象来绘制自己。当所有 View 绘制完成后,Surface 解锁(unlock),并且 post 到绘制缓存用于绘制,通过 Surface Flinger 来组织各个 Window,显示最终的整个屏幕
推荐文章:
7、横竖屏切换的 Activity 生命周期变化?
参考回答:
不设置 Activity 的 android:configChanges 时,切屏会销毁当前 Activity,然后重新加载调用各个生命周期,切横屏时会执行一次,切竖屏时会执行两次; onPause() →onStop()→onDestory()→onCreate()→onStart()→onResume()
设置 Activity 的 android:configChanges="orientation",经过机型测试
在 Android5.1 即 API 23 级别下,切屏还是会重新调用各个生命周期,切横、竖屏时只会执行一次
在 Android9 即 API 28 级别下,切屏不会重新调用各个生命周期,只会执行 onConfigurationChanged 方法
后经官方查正,原话如下
如果您的应用面向 Android 3.2 即 API 级别 13 或更高级别(按照 minSdkVersion 和 targetSdkVersion 属性所声明的级别),则还应声明 "screenSize" 配置,因为当设备在横向与纵向之间切换时,该配置也会发生变化。即便是在 Android 3.2 或更高版本的设备上运行,此配置变更也不会重新启动 Activity
设置 Activity 的 android:configChanges="orientation|keyboardHidden|screenSize"时,机型测试通过,切屏不会重新调用各个生命周期,只会执行 onConfigurationChanged 方法;
推荐文章:
8、如何启动其他应用的 Activity?
参考回答:
在保证有权限访问的情况下,通过隐式 Intent 进行目标 Activity 的 IntentFilter 匹配,原则是:
一个 intent 只有同时匹配某个 Activity 的 intent-filter 中的 action、category、data 才算完全匹配,才能启动该 Activity;
一个 Activity 可以有多个 intent-filter,一个 intent 只要成功匹配任意一组 intent-filter,就可以启动该 Activity;
推荐文章:
9、Activity 的启动过程?(重点)
参考回答:
?先还是得当前系统中有没有拥有这个 Application 的进程。如果没有,则需要处理 APP 的启动过 程。在经过创建进程、绑定 Application 步骤后,才真正开始启动 Activity 的?法。 startActivity() ? 法最终还是调?的 startActivityForResult()。
在 startActivityForResult() 中,真正去打开 Activity 的实现是在 Instrumentation 的 execStartActivivity() ?法中。
在 execStartActivity() 中采? checkStartActivityResult() 检查在 manifest 中是否已经注册,如果没 有注册则抛出异常。否则把打开 Activity 的任务交给 ActivityThread 的内部类 ApplicationThread, 该类实现了 IApplicationThread 接?。这个类完全搞定了 onCreate()、onStart() 等 Activity 的?命 周期回调?法。
在 ApplicationThread 类中,有?个?法叫 scheduleLaunchActivity(),它可以构造?个 Activity 记 录,然后发送?个消息给事先定义好的 Handler。 这个 Handler 负责根据 LAUNCH_ACTIVITY 的类型来做不同的 Activity 启动?式。其中有?个?要的 ?法 handleLaunchActivity() 。
在 handleLaunchActivity() 中,会把启动 Activity 交给 performLaunchActivity() ?法。 在 performLaunchActivity() ?法中,?先从 Intent 中解析出?标 Activity 的启动参数,然后? ClassLoader 将?标 Activity 的类通过类名加载出来并? newInstance() 来实例化?个对象。 创建完毕后, 开始调? Activity 的 onCreate() ?法,?此,Activity 被成功启动。
推荐文章:
Fragment
1、谈一谈 Fragment 的生命周期?
参考回答:
Fragment 从创建到销毁整个生命周期中涉及到的方法依次为:onAttach()→onCreate()→ onCreateView()→onActivityCreated()→onStart()→onResume()→onPause()→onStop()→onDestroyView()→onDestroy()→onDetach(),其中和 Activity 有不少名称相同作用相似的方法,而不同的方法有:
onAttach():当 Fragment 和 Activity 建立关联时调用;
onCreateView():当 fragment 创建视图调用,在 onCreate 之后;
onActivityCreated():当与 Fragment 相关联的 Activity 完成 onCreate()之后调用;
onDestroyView():在 Fragment 中的布局被移除时调用;
onDetach():当 Fragment 和 Activity 解除关联时调用;
推荐文章:
2、谈谈 Activity 和 Fragment 的区别?
参考回答:
相似点:都可包含布局、可有自己的生命周期
不同点:
Fragment 相比较于 Activity 多出 4 个回调周期,在控制操作上更灵活;
Fragment 可以在 XML 文件中直接进行写入,也可以在 Activity 中动态添加;
Fragment 可以使用 show()/hide()或者 replace()随时对 Fragment 进行切换,并且切换的时候不会出现明显的效果,用户体验会好;Activity 虽然也可以进行切换,但是 Activity 之间切换会有明显的翻页或者其他的效果,在小部分内容的切换上给用户的感觉不是很好;
3、Fragment 中 add 与 replace 的区别(Fragment 重叠)
参考回答:
add 不会重新初始化 fragment,replace 每次都会。所以如果在 fragment 生命周期内获取获取数据,使用 replace 会重复获取;
添加相同的 fragment 时,replace 不会有任何变化,add 会报 IllegalStateException 异常;
replace 先 remove 掉相同 id 的所有 fragment,然后在 add 当前的这个 fragment,而 add 是覆盖前一个 fragment。所以如果使用 add 一般会伴随 hide()和 show(),避免布局重叠;
使用 add,如果应用放在后台,或以其他方式被系统销毁,再打开时,hide()中引用的 fragment 会销毁,所以依然会出现布局重叠 bug,可以使用 replace 或使用 add 时,添加一个 tag 参数;
4、getFragmentManager、getSupportFragmentManager 、getChildFragmentManager 之间的区别?
参考回答:
getFragmentManager()所得到的是所在 fragment 的父容器的管理器, getChildFragmentManager()所得到的是在 fragment 里面子容器的管理器, 如果是 fragment 嵌套 fragment,那么就需要利用 getChildFragmentManager();
因为 Fragment 是 3.0 Android 系统 API 版本才出现的组件,所以 3.0 以上系统可以直接调用 getFragmentManager()来获取 FragmentManager()对象,而 3.0 以下则需要调用 getSupportFragmentManager() 来间接获取;
5、FragmentPagerAdapter 与 FragmentStatePagerAdapter 的区别与使用场景
参考回答:
相同点 :二者都继承 PagerAdapter
不同点 :FragmentPagerAdapter 的每个 Fragment 会持久的保存在 FragmentManager 中,只要用户可以返回到页面中,它都不会被销毁。因此适用于那些数据相对静态的页,Fragment 数量也比较少的那种; **Fra
gmentStatePagerAdapter 只保留当前页面,当页面不可见时,该 Fragment 就会被消除,释放其资源。因此适用于那些数据动态性**较大、占用内存较多,多 Fragment 的情况;
Service
1、谈一谈 Service 的生命周期?
参考回答:Service 的生命周期涉及到六大方法
onCreate():如果 service 没被创建过,调用 startService()后会执行 onCreate()回调;如果 service 已处于运行中,调用 startService()不会执行 onCreate()方法。也就是说,onCreate()只会在第一次创建 service 时候调用,多次执行 startService()不会重复调用 onCreate(),此方法适合完成一些初始化工作;
onStartComand():服务启动时调用,此方法适合完成一些数据加载工作,比如会在此处创建一个线程用于下载数据或播放音乐;
onBind():服务被绑定时调用;
onUnBind():服务被解绑时调用;
onDestroy():服务停止时调用;
推荐文章:
2、Service 的两种启动方式?区别在哪?
参考回答:Service 的两种启动模式
startService():通过这种方式调用 startService,onCreate()只会被调用一次,多次调用 startSercie 会多次执行 onStartCommand()和 onStart()方法。如果外部没有调用 stopService()或 stopSelf()方法,service 会一直运行。
bindService():如果该服务之前还没创建,系统回调顺序为 onCreate()→onBind()。如果调用 bindService()方法前服务已经被绑定,多次调用 bindService()方法不会多次创建服务及绑定。如果调用者希望与正在绑定的服务解除绑定,可以调用 unbindService()方法,回调顺序为 onUnbind()→onDestroy();
推荐文章:
3、如何保证 Service 不被杀死 ?
参考回答:
onStartCommand 方式中,返回 START_STICKY 或则 START_REDELIVER_INTENT
START_STICKY:如果返回 START_STICKY,表示 Service 运行的进程被 Android 系统强制杀掉之后,Android 系统会将该 Service 依然设置为 started 状态(即运行状态),但是不再保存 onStartCommand 方法传入的 intent 对象
START_NOT_STICKY:如果返回 START_NOT_STICKY,表示当 Service 运行的进程被 Android 系统强制杀掉之后,不会重新创建该 Service
START_REDELIVER_INTENT:如果返回 START_REDELIVER_INTENT,其返回情况与 START_STICKY 类似,但不同的是系统会保留最后一次传入 onStartCommand 方法中的 Intent 再次保留下来并再次传入到重新创建后的 Service 的 onStartCommand 方法中
提高 Service 的优先级 在 AndroidManifest.xml 文件中对于 intent-filter 可以通过 android:priority = "1000"这个属性设置最高优先级,1000 是最高值,如果数字越小则优先级越低,同时适用于广播;
在 onDestroy 方法里重启 Service 当 service 走到 onDestroy()时,发送一个自定义广播,当收到广播时,重新启动 service;
提升 Service 进程的优先级 进程优先级由高到低:前台进程 一 可视进程 一 服务进程 一 后台进程 一 空进程 可以使用 startForeground 将 service 放到前台状态,这样低内存时,被杀死的概率会低一些;
系统广播监听 Service 状态
将 APK 安装到/system/app,变身为系统级应用
注意:以上机制都不能百分百保证 Service 不被杀死,除非做到系统白名单,与系统同生共死
4、能否在 Service 开启耗时操作 ? 怎么做 ?
参考回答:
Service 默认并不会运行在子线程中,也不运行在一个独立的进程中,它同样执行在主线程中(UI 线程)。换句话说,不要在 Service 里执行耗时操作,除非手动打开一个子线程,否则有可能出现主线程被阻塞(ANR)的情况;
5、用过哪些系统 Service ?
参考回答:
6、了解 ActivityManagerService 吗?发挥什么作用
参考回答: ActivityManagerService 是 Android 中最核心的服务 , 主要负责系统中四大组件的启动、切换、调度及应用进程的管理和调度等工作,其职责与操作系统中的进程管理和调度模块类似;
推荐文章:
Broadcast Receiver
1、广播有几种形式 ? 都有什么特点 ?
参考回答:
普通广播:开发者自身定义 intent 的广播(最常用),所有的广播接收器几乎会在同一时刻接受到此广播信息,接受的先后顺序随机;
有序广播:发送出去的广播被广播接收者按照先后顺序接收,同一时刻只会有一个广播接收器能够收到这条广播消息,当这个广播接收器中的逻辑执行完毕后,广播才会继续传递,且优先级(priority)高的广播接收器会先收到广播消息。有序广播可以被接收器截断使得后面的接收器无法收到它;
本地广播:仅在自己的应用内发送接收广播,也就是只有自己的应用能收到,数据更加安全,效率更高,但只能采用动态注册的方式;
粘性广播:这种广播会一直滞留,当有匹配该广播的接收器被注册后,该接收器就会收到此条广播;
推荐文章:
2、广播的两种注册方式 ?
参考回答:
3、广播发送和接收的原理了解吗 ?(Binder 机制、AMS)
参考回答:
推荐文章:
ContentProvider
1、ContentProvider 了解多少?
参考回答:
ContentProvider 作为四大组件之一,其主要负责存储和共享数据。与文件存储、SharedPreferences 存储、SQLite 数据库存储这几种数据存储方法不同的是,后者保存下的数据只能被该应用程序使用,而前者可以让不同应用程序之间进行数据共享,它还可以选择只对哪一部分数据进行共享,从而保证程序中的隐私数据不会有泄漏风险。
推荐文章:
2、ContentProvider 的权限管理?
参考回答:
读写分离
权限控制-精确到表级
URL 控制
3、说说 ContentProvider、ContentResolver、ContentObserver 之间的关系?
参考回答:
ContentProvider:管理数据,提供数据的增删改查操作,数据源可以是数据库、文件、XML、网络等,ContentProvider 为这些数据的访问提供了统一的接口,可以用来做进程间数据共享。
ContentResolver:ContentResolver 可以为不同 URI 操作不同的 ContentProvider 中的数据,外部进程可以通过 ContentResolver 与 ContentProvider 进行交互。
ContentObserver:观察 ContentProvider 中的数据变化,并将变化通知给外界。
数据存储
1、描述一下 Android 数据持久存储方式?
参考回答:Android 平台实现数据持久存储的常见几种方式:
SharedPreferences 存储:一种轻型的数据存储方式,本质是基于 XML 文件存储的 key-value 键值对数据,通常用来存储一些简单的配置信息(如应用程序的各种配置信息);
SQLite 数据库存储:一种轻量级嵌入式数据库引擎,它的运算速度非常快,占用资源很少,常用来存储大量复杂的关系数据;
ContentProvider:四大组件之一,用于数据的存储和共享,不仅可以让不同应用程序之间进行数据共享,还可以选择只对哪一部分数据进行共享,可保证程序中的隐私数据不会有泄漏风险;
File 文件存储:写入和读取文件的方法和 Java 中实现 I/O 的程序一样;
网络存储:主要在远程的服务器中存储相关数据,用户操作的相关数据可以同步到服务器上;
2、SharedPreferences 的应用场景?注意事项?
参考回答:
SharedPreferences 是一种轻型的数据存储方式,本质是基于 XML 文件存储的 key-value 键值对数据,通常用来存储一些简单的配置信息,如 int,String,boolean、float 和 long;
注意事项:
勿存储大型复杂数据,这会引起内存 GC、阻塞主线程使页面卡顿产生 ANR
勿在多进程模式下,操作 Sp
不要多次 edit 和 apply,尽量批量修改一次提交
建议 apply,少用 commit
3、SharedPrefrences 的 apply 和 commit 有什么区别?
参考回答:
apply 没有返回值而 commit 返回 boolean 表明修改是否提交成功。
apply 是将修改数据原子提交到内存, 而后异步真正提交到硬件磁盘, 而 commit 是同步的提交到硬件磁盘,因此,在多个并发的提交 commit 的时候,他们会等待正在处理的 commit 保存到磁盘后在操作,从而降低了效率。而 apply 只是原子的提交到内容,后面有调用 apply 的函数的将会直接覆盖前面的内存数据,这样从一定程度上提高了很多效率。
apply 方法不会提示任何失败的提示。 由于在一个进程中,sharedPreference 是单实例,一般不会出现并发冲突,如果对提交的结果不关心的话,建议使用 apply,当然需要确保提交成功且有后续操作的话,还是需要用 commit 的。
4、了解 SQLite 中的事务操作吗?是如何做的
参考回答:
SQLite 在做 CRDU 操作时都默认开启了事务,然后把 SQL 语句翻译成对应的 SQLiteStatement 并调用其相应的 CRUD 方法,此时整个操作还是在 rollback journal 这个临时文件上进行,只有操作顺利完成才会更新 db 数据库,否则会被回滚;
5、使用 SQLite 做批量操作有什么好的方法吗?
参考回答:
使用 SQLiteDatabase 的 beginTransaction 方法开启一个事务,将批量操作 SQL 语句转化为 SQLiteStatement 并进行批量操作,结束后 endTransaction()
6、如何删除 SQLite 中表的个别字段
参考回答:
SQLite 数据库只允许增加字段而不允许修改和删除表字段,只能创建新表保留原有字段,删除原表
7、使用 SQLite 时会有哪些优化操作?
参考回答:
使用事务做批量操作
及时关闭 Cursor,避免内存泄露
耗时操作异步化:数据库的操作属于本地 IO 耗时操作,建议放入异步线程中处理
ContentValues 的容量调整:ContentValues 内部采用 HashMap 来存储 Key-Value 数据,ContentValues 初始容量为 8,扩容时翻倍。因此建议对 ContentValues 填入的内容进行估量,设置合理的初始化容量,减少不必要的内部扩容操作
使用索引加快检索速度:对于查询操作量级较大、业务对查询要求较高的推荐使用索引
评论