-
关闭B Activity,分别执行B onPause()、A onRestart()、A onStart()、A onResume()、B onStop()、B onDestroy()方法。
-
横竖屏切换A Activity,清单文件中不设置android:configChanges属性时,先销毁onPause()、onStop()、onDestroy()再重新创建onCreate()、onStart()、onResume()方法,设置orientation|screenSize(一定要同时出现)属性值时,不走生命周期方法,只会执行onConfigurationChanged()方法。
-
Activity之间的切换可以看出onPause()、onStop()这两个方法比较特殊,切换的时候onPause()方法不要加入太多耗时操作否则会影响体验。
4、Activity切换横屏时生命周期
-
onSaveInstanceState()
-
onPause()
-
onStop()
-
onDestroy()
-
onCreate()
-
onStart()
-
onRestoreInstanceState()
-
onResume()
5、进程的优先级
oom_adj
是linux内核分配给每个系统进程的一个值,代表进程的优先级,进程回收机制就是根据这个优先级来决定是否进行回收。进程的oom_adj
越大,表示此进程优先级越低,越容易被杀回收;越小,表示进程优先级越高,越不容易被杀回收。普通app进程的oom_adj
>=0,系统进程的oom_adj
才可能小于0。进程优先级从小到大如下
-
空进程
-
后台进程
-
服务进程
-
可见进程
-
前台进程
6、Activity任务栈
- 先进后出
7、Activity启动模式
-
standard
-
singletop
-
singletask
-
singleinstance
8、scheme跳转协议
android中的scheme是一种页面内跳转协议,通过定义自己的scheme协议,可以跳转到app中的各个页面
-
服务器可以定制化告诉app跳转哪个页面
-
App可以通过跳转到另一个App页面
-
可以通过H5页面跳转页面
9、Context、Activity、Application之间有什么区别
Activity和Application都是Context的子类。Context从字面上理解就是上下文的意思,在实际应用中它也确实是起到了管理上下文环境中各个参数和变量的总用,方便我们可以简单的访问到各种资源。虽然Activity和Application都是Context的子类,但是他们维护的生命周期不一样。前者维护一个Acitivity的生命周期,所以其对应的Context也只能访问该activity内的各种资源。后者则是维护一个Application的生命周期
10、Activity启动过程
-
在安装应用的时候,系统会启动PackaManagerService管理服务,这个管理服务会对AndroidManifest进行解析,从而得到应用程序中的相关信息,比如service,activity,Broadcast等等,然后获得相关组件的信息
-
当用户点击应用图标时,就会调用
startActivitySately()
,而这个方法内部则是调用startActivty()
,startActivity()
最终还是会调用startActivityForResult()
。由于startActivityForResult()
是有返回结果的,系统直接返回-1,表示不需要返回结果 -
startActivityForResult()
通过Instrumentation类中的execStartActivity()
来启动activity,Instrumentation这个类主要作用是监控程序和系统之间的交互。在这个execStartActivity()
中会获取ActivityManagerService的代理对象,通过这个代理对象进行启动activity -
在ActivityManagerService的代理对象中,通过Binder通信,调用到
ApplicationThread.scheduleLaunchActivity()
进行启动activity,在这个方法中创建一个ActivityClientRecord对象,用来记录启动Activity组件的信息,然后通过handler将ActivityClientRecord发送出去 -
在handler收到消息后,调用
ActivityThread.handleLaunchActivity()
启动Activity
11、简述Activity,View,Window三者关系
-
Activity本质是上下文,View本质是视图,Window本质是窗口
-
Activity构造的时候会初始化一个Window,其具体实现是PhoneWindow
-
PhoneWindow中有一个ViewRoot(View或ViewGroup),是最初始的根视图
-
ViewRoot通过addView()将View添加到根视图上,实际上是将View交给了PhoneWindow处理
-
View的事件监听是由WindowManagerService来接受消息,并且回调Activity函数
Fragment面试题
1、Fragment为什么被称为第五大组件
Fragment比Activity更节省内存,其切换模式也更加舒适,使用频率不低于四大组件,且有自己的生命周期,而且必须依附于Activity
2、Activity创建Fragment的方式
-
静态创建
-
动态创建
3、FragmentPageAdapter和FragmentPageStateAdapter的区别
-
FragmentPageAdapter在每次切换页面的的时候,是将Fragment进行分离,适合页面较少的Fragment使用以保存一些内存,对系统内存不会多大影响
-
FragmentPageStateAdapter在每次切换页面的时候,是将Fragment进行回收,适合页面较多的Fragment使用,这样就不会消耗更多的内存
4、Fragment生命周期
Fragment的生命周期方法主要有onAttach()、onCreate()、onCreateView()、onActivityCreated()、onstart()、onResume()、onPause()、onStop()、onDestroyView()、onDestroy()、onDetach()等11个方法。
-
切换到该Fragment,分别执行onAttach()、onCreate()、onCreateView()、onActivityCreated()、onstart()、onResume()方法。
-
锁屏,分别执行onPause()、onStop()方法。
-
亮屏,分别执行onstart()、onResume()方法。
-
覆盖,切换到其他Fragment,分别执行onPause()、onStop()、onDestroyView()方法。
-
从其他Fragment回到之前Fragment,分别执行onCreateView()、onActivityCreated()、onstart()、onResume()方法。
5、Fragment的通信
-
Fragment调用Activity中的方法:getActivity
-
Activity调用Fragment中的方法:接口回调
-
Fragment调用Fragment中的方法:FragmentManager.findFragmentById
6、Fragment的replace、add、remove方法
-
replace:替代Fragment的栈顶页面
-
add:添加Fragment到栈顶页面
-
remove:移除Fragment栈顶页面
Service面试题
1、Service是什么
Service是四大组件之一,它可以在后台执行长时间运行操作而没有用户界面的应用组件
2、Service和Thread的区别
-
Service是安卓中系统的组件,它运行在独立进程的主线程中,不可以执行耗时操作。Thread是程序执行的最小单元,分配CPU的基本单位,可以开启子线程执行耗时操作
-
Service在不同Activity中可以获取自身实例,可以方便的对Service进行操作。Thread在不同的Activity中难以获取自身实例,如果Activity被销毁,Thread实例就很难再获取得到
3、Service启动方式
-
startService
-
bindService
4、Service生命周期
-
手动调用startService()启动服务,自动调用内部方法:onCreate()、onStartCommand(),如果一个Service被startService()多次启动,那么onCreate()也只会调用一次。
-
手动调用stopService()关闭服务,自动调用内部方法:onDestory(),如果一个Service被启动且被绑定,如果在没有解绑的前提下使用stopService()关闭服务是无法停止服务的。
-
手动调用bindService()后,自动调用内部方法:onCreate()、onBind()。
-
手动调用unbindService()后,自动调用内部方法:onUnbind()、onDestory()。
-
startService()和stopService()只能开启和关闭Service,无法操作Service,调用者退出后Service仍然存在;bindService()和unbindService()可以操作Service,调用者退出后,Service随着调用者销毁。
Broadcast Receiver面试题
1、Broadcast Receiver是什么
Broadcast是四大组件之一,是一种广泛运用在应用程序之间传输信息的机制,通过发送Intent来传送我们的数据
2、Broadcast Receiver的使用场景
-
同一App具有多个进程的不同组件之间的消息通信
-
不同App之间的组件之间的消息通信
3、Broadcast Receiver的种类
-
普通广播
-
有序广播
-
本地广播
-
Sticky广播
4、Broadcast Receiver的实现
-
静态注册:注册后一直运行,尽管Activity、进程、App被杀死还是可以接收到广播
-
动态注册:跟随Activity的生命周期
5、Broadcast Receiver实现机制
-
自定义广播类继承BroadcastReceiver,复写onReceiver()
-
通过Binder机制向AMS进行注册广播
-
广播发送者通过Binder机制向AMS发送广播
-
AMS查找符合相应条件的广播发送到BroadcastReceiver相应的循环队列中
-
消息队列执行拿到广播,回调BroadcastReceiver的onReceiver()
6、LocalBroadcastManager特点
-
本地广播只能在自身App内传播,不必担心泄漏隐私数据
-
本地广播不允许其他App对你的App发送该广播,不必担心安全漏洞被利用
-
本地广播比全局广播更高效
-
以上三点都是源于其内部是用Handler实现的
WebView面试题
1、WebView安全漏洞
- API16之前存在远程代码执行安全漏洞,该漏洞源于程序没有正确限制使用WebView.addJavascriptInterface方法,远程攻击者可通过使用Java反射机制利用该漏洞执行任意Java对象的方法
2、WebView销毁步骤
- WebView在其他容器上时(如:LinearLayout),当销毁Activity时,需要在onDestroy()中先移除容器上的WebView,然后再将WebView.destroy(),这样就不会导致内存泄漏
3、WebView的jsbridge
- 客户端和服务端之间可以通过Javascript来互相调用各自的方法
4、WebViewClient的onPageFinished
- WebViewClient的onPageFinished在每次完成页面的时候调用,但是遇到未加载完成的页面跳转其他页面时,就会一直调用,使用WebChromeClient.onProgressChanged可以替代
5、WebView后台耗电
- 在WebView加载页面的时候,会自动开启线程去加载,如果不很好的关闭这些线程,就会导致电量消耗加大,可以采用暴力的方法,直接在onDestroy方法中System.exit(0)结束当前正在运行中的java虚拟机
6、WebView硬件加速
Android3.0引入硬件加速,默认会开启,WebView在硬件加速的情况下滑动更加平滑,性能更加好,但是会出现白块或者页面闪烁的副作用,建议WebView暂时关闭硬件加速
7、WebView内存泄漏
由于WebView是依附于Activity的,Activity的生命周期和WebView启动的线程的生命周期是不一致的,这会导致WebView一直持有对这个Activity的引用而无法释放,解决方案如下
-
独立进程,简单暴力,不过可能涉及到进程间通信(推荐)
-
动态添加WebView,对传入WebView中使用的Context使用弱引用
Binder面试题
1、Linux内核的基本知识
-
进程隔离/虚拟地址空间:进程间是不可以共享数据的,相当于被隔离,每个进程被分配到不同的虚拟地址中
-
系统调用:Linux内核对应用有访问权限,用户只能在应用层通过系统调用,调用内核的某些程序
-
binder驱动:它负责各个用户的进程,通过binder通信内核来进行交互的模块
2、为什么使用Binder
-
性能上,相比传统的Socket更加高效
-
安全性高,支持协议双方互相校验
3、Binder通信原理
-
Service端通过Binder驱动在ServiceManager的查找表中注册Object对象的add方法
-
Client端通过Binder驱动在ServiceManager的查找表中找到Object对象的add方法,并返回proxy对象的add方法,add方法是个空实现,proxy对象也不是真正的Object对象,是通过Binder驱动封装好的代理类的add方法
-
当Client端调用add方法时,Client端会调用proxy对象的add方法,通过Binder驱动去请求ServiceManager来找到Service端真正对象,然后调用Service端的add方法
4、AIDL
-
客户端通过aidl文件的Stub.asInterface()方法,拿到Proxy代理类
-
通过调用Proxy代理类的方法,将参数进行封包后,调用底层的transact()方法
-
transact()方法会回调onTransact()方法,进行参数的解封
-
在onTransact()方法中调用服务端对应的方法,并将结果返回
5、BpBinder和BBinder
BpBinder(客户端)对象和BBinder(服务端)对象,它们都从IBinder类中派生而来,BpBinder(客户端)对象是BBinder(服务端)对象的代理对象。
-
client端:BpBinder.transact()来发送事务请求
-
server端:BBinder.onTransact()会接收到相应事务
6.Binder有什么优势
性能方面
· 共享内存 0次数据拷贝
· Binder 1次数据拷贝
· Socket/管道/消息队列 2次数据拷贝
稳定性方面
· Binder:基于C/S架构,客户端(Client)有什么需求就丢给服务端(Server)去完成,架构清晰、职责明确又相互独立,自然稳定性更好
· 共享内存:虽然无需拷贝,但是控制复杂,难以使用
· 从稳定性的角度讲,Binder机制是优于内存共享的。
安全性方面
· 传统的IPC没有任何安全措施,安全依赖上层协议来确保。
· 传统的IPC方法无法获得对方可靠的进程用户ID/进程UI(UID/PID),从而无法鉴别对方身份。
· 传统的IPC只能由用户在数据包中填入UID/PID,容易被恶意程序利用。
· 传统的IPC访问接入点是开放的,无法阻止恶意程序通过猜测接收方地址获得连接。
· Binder既支持实名Binder,又支持匿名Binder,安全性高。
7.Binder是如何做到一次拷贝的
主要是因为Linux是使用的虚拟内存寻址方式,它有如下特性:
· 用户空间的虚拟内存地址是映射到物理内存中的
· 对虚拟内存的读写实际上是对物理内存的读写,这个过程就是内存映射
· 这个内存映射过程是通过系统调用mmap()来实现的 Binder借助了内存映射的方法,在内核空间和接收方用户空间的数据缓存区之间做了一层内存映射,就相当于直接拷贝到了接收方用户空间的数据缓存区,从而减少了一次数据拷贝
8.MMAP的内存映射原理了解吗
MMAP内存映射的实现过程,总的来说可以分为三个阶段:
(一)进程启动映射过程,并在虚拟地址空间中为映射创建虚拟映射区域
\1. 进程在用户空间调用库函数mmap,原型:void *mmap(void *start, size_t length, int prot, int flags, int fd, off_t offset);
\2. 在当前进程的虚拟地址空间中,寻找一段空闲的满足要求的连续的虚拟地址
\3. 为此虚拟区分配一个vm_area_struct结构,接着对这个结构的各个域进行了初始化
\4. 将新建的虚拟区结构(vm_area_struct)插入进程的虚拟地址区域链表或树中
(二)调用内核空间的系统调用函数mmap(不同于用户空间函数),实现文件物理地址和进程虚拟地址的一一映射关系
\1. 为映射分配了新的虚拟地址区域后,通过待映射的文件指针,在文件描述符表中找到对应的文件描述符,通过文件描述符,链接到内核“已打开文件集”中该文件的文件结构体(struct file),每个文件结构体维护着和这个已打开文件相关各项信息。
\2. 通过该文件的文件结构体,链接到file_operations模块,调用内核函数mmap,其原型为:int mmap(struct file *filp, struct vm_area_struct *vma),不同于用户空间库函数。
\3. 内核mmap函数通过虚拟文件系统inode模块定位到文件磁盘物理地址。
\4. 通过remap_pfn_range函数建立页表,即实现了文件地址和虚拟地址区域的映射关系。此时,这片虚拟地址并没有任何数据关联到主存中。
(三)进程发起对这片映射空间的访问,引发缺页异常,实现文件内容到物理内存(主存)的拷贝 注:前两个阶段仅在于创建虚拟区间并完成地址映射,但是并没有将任何文件数据的拷贝至主存。真正的文件读取是当进程发起读或写操作时。
进程的读或写操作访问虚拟地址空间这一段映射地址,通过查询页表,发现这一段地址并不在物理页面上。因为目前只建立了地址映射,真正的硬盘数据还没有拷贝到内存中,因此引发缺页异常。
\1. 缺页异常进行一系列判断,确定无非法操作后,内核发起请求调页过程。
\2. 调页过程先在交换缓存空间(swap cache)中寻找需要访问的内存页,如果没有则调用nopage函数把所缺的页从磁盘装入到主存中。
\3. 之后进程即可对这片主存进行读或者写的操作,如果写操作改变了其内容,一定时间后系统会自动回写脏页面到对应磁盘地址,也即完成了写入到文件的过程。
注:修改过的脏页面并不会立即更新回文件中,而是有一段时间的延迟,可以调用msync()来强制同步, 这样所写的内容就能立即保存到文件里了。
9.Binder机制是如何跨进程的
1.Binder驱动
· 在内核空间创建一块接收缓存区,
· 实现地址映射:将内核缓存区、接收进程用户空间映射到同一接收缓存区
2.发送进程通过系统调用(copy_from_user)将数据发送到内核缓存区。由于内核缓存区和接收进程用户空间存在映射关系,故相当于也发送了接收进程的用户空间,实现了跨进程通信。
10.说说四大组件的通信机制
1.activity (1)一个Activity通常就是一个单独的屏幕(窗口)。 (2)Activity之间通过Intent进行通信。 (3)android应用中每一个Activity都必须要在AndroidManifest.xml配置文件中声明,否则系统将不识别也不执行该Activity。
2.service (1)service用于在后台完成用户指定的操作。service分为两种:
· started(启动):当应用程序组件(如activity)调用startService()方法启动服务时,服务处于started状态。
· bound(绑定):当应用程序组件调用bindService()方法绑定到服务时,服务处于bound状态。
(2)startService()与bindService()区别:
访问的内存页,如果没有则调用nopage函数把所缺的页从磁盘装入到主存中。
\3. 之后进程即可对这片主存进行读或者写的操作,如果写操作改变了其内容,一定时间后系统会自动回写脏页面到对应磁盘地址,也即完成了写入到文件的过程。
注:修改过的脏页面并不会立即更新回文件中,而是有一段时间的延迟,可以调用msync()来强制同步, 这样所写的内容就能立即保存到文件里了。
9.Binder机制是如何跨进程的
1.Binder驱动
· 在内核空间创建一块接收缓存区,
· 实现地址映射:将内核缓存区、接收进程用户空间映射到同一接收缓存区
2.发送进程通过系统调用(copy_from_user)将数据发送到内核缓存区。由于内核缓存区和接收进程用户空间存在映射关系,故相当于也发送了接收进程的用户空间,实现了跨进程通信。
10.说说四大组件的通信机制
1.activity (1)一个Activity通常就是一个单独的屏幕(窗口)。 (2)Activity之间通过Intent进行通信。 (3)android应用中每一个Activity都必须要在AndroidManifest.xml配置文件中声明,否则系统将不识别也不执行该Activity。
2.service (1)service用于在后台完成用户指定的操作。service分为两种:
· started(启动):当应用程序组件(如activity)调用startService()方法启动服务时,服务处于started状态。
· bound(绑定):当应用程序组件调用bindService()方法绑定到服务时,服务处于bound状态。
(2)startService()与bindService()区别: