一,基础部分
1,四大组件是什么
1)Activity:用户可操作的可视化界面,为用户提供一个完成操作指令的窗口。一个Activity通常是一个单独的屏幕,Activity通过Intent来进行通信。Android中会维持一个Activity Stack,当一个新Activity创建时,它就会放到栈顶,这个Activity就处于运行状态。
2)Service:服务,运行在手机后台,适合执行不需和用户交互且还需长期运行的任务。
3)ContentProvider:内容提供者,使一个应用程序的指定数据集提供给其他应用程序,其他应用可通过ContentResolver类从该内容提供者中获取或存入数据。它提供了一种跨进程数据共享的方式,当数据被修改后,ContentResolver接口的notifyChange函数通知那些注册监控特定URI的ContentObserver对象。
如果ContentProvider和调用者在同一进程中,ContentProvider的方法(query/insert/update/delete等)和调用者在同一线程中;如果ContentProvider和调用者不在同一进程,ContentProvider方法会运行在它自身进程的一个Binder线程中。
4)Broadcast Receiver: 广播接收者,运用在应用程序间传输信息,可以使用广播接收器来让应用对一个外部事件做出响应。
2,四大组件的生命周期和简单用法
1)Activity:onCreate()->onStart()->onResume()->onPause()->onStop()->onDestory()
onCreate():为Activity设置布局,此时界面还不可见;
onStart(): Activity可见但还不能与用户交互,不能获得焦点
onRestart(): 重新启动Activity时被回调
onResume(): Activity可见且可与用户进行交互
onPause(): 当前Activity暂停,不可与用户交互,但还可见。在新Activity启动前被系统调用保存现有的Activity中的持久数据、停止动画等。
onStop(): 当Activity被新的Activity覆盖不可见时被系统调用
onDestory(): 当Activity被系统销毁杀掉或是由于内存不足时调用
2)Service
a) onBind方式绑定的:onCreate->onBind->onUnBind->onDestory(不管调用bindService几次,onCreate只会调用一次,onStart不会被调用,建立连接后,service会一直运行,直到调用unBindService或是之前调用的bindService的Context不存在了,系统会自动停止Service,对应的onDestory会被调用)
b) startService启动的:onCreate->onStartCommand->onDestory(start多次,onCreate只会被调用一次,onStart会调用多次,该service会在后台运行,直至被调用stopService或是stopSelf)
c) 又被启动又被绑定的服务,不管如何调用onCreate()只被调用一次,startService调用多少次,onStart就会被调用多少次,而unbindService不会停止服务,必须调用stopService或是stopSelf来停止服务。必须unbindService和stopService(stopSelf)同时都调用了才会停止服务。
3)BroadcastReceiver
a) 动态注册:存活周期是在Context.registerReceiver和Context.unregisterReceiver之间,BroadcastReceiver每次收到广播都是使用注册传入的对象处理的。
b) 静态注册:进程在的情况下,receiver会正常收到广播,调用onReceive方法;生命周期只存活在onReceive函数中,此方法结束,BroadcastReceiver就销毁了。onReceive()只有十几秒存活时间,在onReceive()内操作超过10S,就会报ANR。
进程不存在的情况,广播相应的进程会被拉活,Application.onCreate会被调用,再调用onReceive。
4)ContentProvider:应该和应用的生命周期一样,它属于系统应用,应用启动时,它会跟着初始化,应用关闭或被杀,它会跟着结束。
3,Android 进程通讯方式
- bundle :
由于Activity,Service,Receiver都是可以通过Intent来携带Bundle传输数据的,所以我们可以在一个进程中通过Intent将携带数据的Bundle发送到另一个进程的组件。(bundle只能传递三种类型,一是键值对的形式,二是键为String类型,三是值为Parcelable类型) - ContentProvider
:ContentProvider是Android四大组件之一,以表格的方式来储存数据,提供给外界,即Content
Provider可以跨进程访问其他应用程序中的数据 - 文件
:两个进程可以到同一个文件去交换数据,我们不仅可以保存文本文件,还可以将对象持久化到文件,从另一个文件恢复。要注意的是,当并发读/写时可能会出现并发的问题。 - Broadcast :Broadcast可以向android系统中所有应用程序发送广播,而需要跨进程通讯的应用程序可以监听这些广播。
- AIDL :AIDL通过定义服务端暴露的接口,以提供给客户端来调用,AIDL使服务器可以并行处理。
- Messager :Messenger封装了AIDL之后只能串行运行,所以Messenger一般用作消息传递
- Socket通信
4,Activity各种情况下的生命周期
1)两个Activity(A->B)切换(B正常的Activity)的生命周期:onPause(A)->onCreate(B)->onStart(B)->onResume(B)->oStop(A)
这时如果按回退键回退到A onPause(B)->onRestart(A)->onStart(A)->onResume(A)->oStop(B)
如果在切换到B后调用了A.finish(),则会走到onDestory(A),这时点回退键会退出应用
2) 两个Activity(A->B)切换(B透明主题的Activity或是Dialog风格的Acivity)的生命周期:onPause(A)->onCreate(B)->onStart(B)->onResume(B)
这时如果回退到A onPause(B)->onResume(A)->oStop(B)->onDestory(B)
3) Activity(A)启动后点击Home键再回到应用的生命周期:onPause(A)->oStop(A)->onRestart(A)->onStart(A)->onResume(A)
5,横竖屏切换的时候,Activity 各种情况下的生命周期
1)切换横屏时:onSaveInstanceState->onPause->onStop->onDestory->onCreate->onStart->onRestoreInstanceState->onResume
2) 切换竖屏时:会打印两次相同的log
onSaveInstanceState->onPause->onStop->onDestory->onCreate->onStart->onRestoreInstanceState->onResume->onSaveInstanceState->onPause->onStop->onDestory->onCreate->onStart->onRestoreInstanceState->onResume
3)如果在AndroidMainfest.xml中修改该Activity的属性,添加android:configChanges=“orientation”
横竖屏切换,打印的log一样,同1)
4)如果AndroidMainfest.xml中该Activity中的android:configChanges=“orientation|keyboardHidden”,则只会打印onConfigurationChanged
6,Activity与Fragment之间生命周期比较
Fragment生命周期:onAttach->onCreate->onCreateView->onActivityCreated->onStart->onResume->onPause->onStop->onDestoryView->onDestory->onDetach
切换到该Fragment:onAttach->onCreate->onCreateView->onActivityCreated->onStart->onResume
按下Power键:onPause->onSaveInstanceState->onStop
点亮屏幕解锁:onStart->onRestoreInstanceState->onResume
切换到其他Fragment: onPause->onStop->onDestoryView
切回到该Fragment: onCreateView->onActivityCreated->onStart->onResume
退出应用:onPause->onStop->onDestoryView->onDestory->onDetach
7,Activity上有Dialog的时候按Home键时的生命周期
AlertDialog并不会影响Activity的生命周期,按Home键后才会使Activity onPause->onStop,AlertDialog只是一个组件,并不会使Activity进入后台。
8,两个Activity 之间跳转时必然会执行的是哪几个方法
前一个Activity的onPause,后一个Activity的onResume
9,前台切换后台,再回到前台,Activity生命周期回调方法。弹出Dialog,生命值周期回调方法。
1)前台切换到后台,会执行onPause->onStop,再回到前台,会执行onRestart->onStart->onResume
2)弹出Dialog,并不会影响Activity生命周期。
10,Activity的四种启动模式对比
1)standard:标准启动模式(默认),每启动一次Activity,都会创建一个实例,即使从ActivityA startActivity ActivityA,也会再次创建A的实例放于栈顶,当回退时,回到上一个ActivityA的实例。
2)singleTop:栈顶复用模式,每次启动Activity,如果待启动的Activity位于栈顶,则不会重新创建Activity的实例,即不会走onCreate->onStart,会直接进入Activity onPause->onNewIntent->onResume方法
3)singleInstance: 单一实例模式,整个手机操作系统里只有一个该Activity实例存在,没有其他Actvity,后续请求均不会创建新的Activity。若task中存在实例,执行实例的onNewIntent()。应用场景:闹钟、浏览器、电话。
4)singleTask:栈内复用,启动的Activity如果在指定的taskAffinity的task栈中存在相应的实例,则会把它上面的Activity都出栈,直到当前Activity实例位于栈顶,执行相应的onNewIntent()方法。如果指定的task不存在,创建指定的taskAffinity的task,taskAffinity的作用,进入指写taskAffinity的task,如果指定的task存在,将task移到前台,如果指定的task不存在,创建指定的taskAffinity的task. 应用场景:应用的主页面。
11,Activity状态保存于恢复
Activity被主动回收时,如按下Back键,系统不会保存它的状态,只有被动回收时,虽然这个Activity实例已被销毁,但系统在新建一个Activity实例时,会带上先前被回收Activity的信息。在当前Activity被销毁前调用onSaveInstanceState(onPause和onStop之间保存),重新创建Activity后会在onCreate后调用onRestoreInstanceState(onStart和onResume之间被调用),它们的参数Bundle用来数据保存和读取的。保存View状态有两个前提:View的子类必须实现了onSaveInstanceState; 必须要设定Id,这个ID作为Bundle的Key
12,fragment各种情况下的生命周期
正常情况下的生命周期:onAttach->onCreate->onCreateView->onActivityCreated->onStart->onResume->onPause->onStop->onDestoryView->onDestory->onDetach
1)Fragment在Activity中replace onPause(旧)->onAttach->onCreate->onCreateView->onActivityCreated->onStart->onResume->onStop(旧)->onDestoryView(旧)
2)如果添加到backStack中,调用remove()方法fragment的方法会走到onDestoryView,但不会执行onDetach(),即fragment本身的实例是存在的,成员变量也存在,但是view被销毁了。如果新替换的Fragment已在BackStack中,则不会执行onAttach->onCreate
13,说说ContentProvider、ContentResolver、ContentObserver 之间的关系
ContentProvider实现各个应用程序间数据共享,用来提供内容给别的应用操作。如联系人应用中就使用了ContentProvider,可以在自己应用中读取和修改联系人信息,不过需要获取相应的权限。它也只是一个中间件,真正的数据源是文件或SQLite等。
ContentResolver内容解析者,用于获取内容提供者提供的数据,通过ContentResolver.notifyChange(uri)发出消息
ContentObserver内容监听者,可以监听数据的改变状态,观察特定Uri引起的数据库变化,继而做一些相应的处理,类似于数据库中的触发器,当ContentObserver所观察的Uri发生变化时,便会触发它。
14,AlertDialog,popupWindow,Activity区别
1)Popupwindow在显示之前一定要设置宽高,Dialog无此限制。
2)Popupwindow默认不会响应物理键盘的back,除非显示设置了popup.setFocusable(true);而在点击back的时候,Dialog会消失。
3)Popupwindow不会给页面其他的部分添加蒙层,而Dialog会。
4)Popupwindow没有标题,Dialog默认有标题,可以通过dialog.requestWindowFeature(Window.FEATURE_NO_TITLE)取消标题
5)二者显示的时候都要设置Gravity。如果不设置,Dialog默认是Gravity.CENTER。
6)二者都有默认的背景,都可以通过setBackgroundDrawable(new ColorDrawable(android.R.color.transparent))去掉。
7)Popupwindow弹出后,取得了用户操作的响应处理权限,使得其他UI控件不被触发。而AlertDialog弹出后,点击背景,AlertDialog会消失。
二,高级部分
1,Activity启动流程(从Launcher开始)
①,第一阶段: Launcher通知AMS要启动新的Activity(在Launcher所在的进程执行)
- Launcher.startActivitySafely //首先Launcher发起启动Activity的请求
- Activity.startActivity
- Activity.startActivityForResult
- Instrumentation.execStartActivity //交由Instrumentation代为发起请求
- ActivityManager.getService().startActivity //通过IActivityManagerSingleton.get()得到一个AMP代理对象
- ActivityManagerProxy.startActivity //通过AMP代理通知AMS启动activity
②,第二阶段:AMS先校验一下Activity的正确性,如果正确的话,会暂存一下Activity的信息。然后AMS会通知Launcher程序pause Activity(在AMS所在进程执行)
- ActivityManagerService.startActivity
- ActivityManagerService.startActivityAsUser
- ActivityStackSupervisor.startActivityMayWait
- ActivityStackSupervisor.startActivityLocked // 检查有没有在AndroidManifest中注册
- ActivityStackSupervisor.startActivityUncheckedLocked
- ActivityStack.startActivityLocked // 判断是否需要创建一个新的任务来启动Activity。
- ActivityStack.resumeTopActivityLocked // 获取栈顶的activity,并通知Launcher应该pause掉这个Activity以便启动新的activity。
- ActivityStack.startPausingLocked
- ApplicationThreadProxy.schedulePauseActivity
③,pause Launcher的Activity,并通知AMS已经paused(在Launcher所在进程执行)
- ApplicationThread.schedulePauseActivity
- ActivityThread.queueOrSendMessage
- H.handleMessage
- ActivityThread.handlePauseActivity
- ActivityManagerProxy.activityPaused
④,检查activity所在进程是否存在,如果存在,就直接通知这个进程,在该进程中启动Activity;不存在的话,会调用Process.start创建一个新进程(执行在AMS进程)
- ActivityManagerService.activityPaused
- ActivityStack.activityPaused
- ActivityStack.completePauseLocked
- ActivityStack.resumeTopActivityLocked
- ActivityStack.startSpecificActivityLocked
- ActivityManagerService.startProcessLocked
- Process.start // 在这里创建了新进程,新的进程会导入ActivityThread类,并执行它的main函数。
⑤,创建ActivityThread实例,执行一些初始化操作,并绑定Application。如果Application不存在,会调用LoadedApk.makeApplication创建一个新的Application对象。之后进入Loop循环。(执行在新创建的app进程)
- ActivityThread.main
- ActivityThread.attach(false) //声明不是系统进程
- ActivityManagerProxy.attachApplication
⑥,处理新的应用进程发出的创建进程完成的通信请求,并通知新应用程序进程启动目标Activity组件(执行在AMS进程)
- ActivityManagerService.attachApplication // AMS绑定本地ApplicationThread对象,后续通过ApplicationThreadProxy来通信。
- ActivityManagerService.attachApplicationLocked
- ActivityStack.realStartActivityLocked //真正要启动Activity了!
- ApplicationThreadProxy.scheduleLaunchActivity //AMS通过ATP通知app进程启动Activity
⑦,加载MainActivity类,调用onCreate声明周期方法(执行在新启动的app进程)
- ApplicationThread.scheduleLaunchActivity //ApplicationThread发消息给AT
- ActivityThread.queueOrSendMessage
- H.handleMessage //AT的Handler来处理接收到的LAUNCH_ACTIVITY的消息
- ActivityThread.handleLaunchActivity
- ActivityThread.performLaunchActivity
- Instrumentation.newActivity //调用Instrumentation类来新建一个Activity对象
- Instrumentation.callActivityOnCreate
- MainActivity.onCreate
- ActivityThread.handleResumeActivity
- AMP.activityResumed
- AMS.activityResumed(AMS进程)
2,进程保活
此处延伸:进程的优先级是什么
当前业界的Android进程保活手段主要分为** 黑、白、灰 **三种,其大致的实现思路如下:
黑色保活:不同的app进程,用广播相互唤醒(包括利用系统提供的广播进行唤醒)
白色保活:启动前台Service
灰色保活:利用系统的漏洞启动前台Service
黑色保活
所谓黑色保活,就是利用不同的app进程使用广播来进行相互唤醒。举个3个比较常见的场景:
场景1:开机,网络切换、拍照、拍视频时候,利用系统产生的广播唤醒app
场景2:接入第三方SDK也会唤醒相应的app进程,如微信sdk会唤醒微信,支付宝sdk会唤醒支付宝。由此发散开去,就会直接触发了下面的 场景3
场景3:假如你手机里装了支付宝、淘宝、天猫、UC等阿里系的app,那么你打开任意一个阿里系的app后,有可能就顺便把其他阿里系的app给唤醒了。(只是拿阿里打个比方,其实BAT系都差不多)
白色保活
白色保活手段非常简单,就是调用系统api启动一个前台的Service进程,这样会在系统的通知栏生成一个Notification,用来让用户知道有这样一个app在运行着,哪怕当前的app退到了后台。如下方的LBE和QQ音乐这样:
灰色保活
灰色保活,这种保活手段是应用范围最广泛。它是利用系统的漏洞来启动一个前台的Service进程,与普通的启动方式区别在于,它不会在系统通知栏处出现一个Notification,看起来就如同运行着一个后台Service进程一样。这样做带来的好处就是,用户无法察觉到你运行着一个前台进程(因为看不到Notification),但你的进程优先级又是高于普通后台进程的。那么如何利用系统的漏洞呢,大致的实现思路和代码如下:
思路一:API < 18,启动前台Service时直接传入new Notification();
思路二:API >= 18,同时启动两个id相同的前台Service,然后再将后启动的Service做stop处理
熟悉Android系统的童鞋都知道,系统出于体验和性能上的考虑,app在退到后台时系统并不会真正的kill掉这个进程,而是将其缓存起来。打开的应用越多,后台缓存的进程也越多。在系统内存不足的情况下,系统开始依据自身的一套进程回收机制来判断要kill掉哪些进程,以腾出内存来供给需要的app。这套杀进程回收内存的机制就叫 Low Memory Killer ,它是基于Linux内核的 OOM Killer(Out-Of-Memory killer)机制诞生。
进程的重要性,划分5级:
前台进程 (Foreground process)
可见进程 (Visible process)
服务进程 (Service process)
后台进程 (Background process)
空进程 (Empty process)
了解完 Low Memory Killer,再科普一下oom_adj。什么是oom_adj?它是linux内核分配给每个系统进程的一个值,代表进程的优先级,进程回收机制就是根据这个优先级来决定是否进行回收。对于oom_adj的作用,你只需要记住以下几点即可:
进程的oom_adj越大,表示此进程优先级越低,越容易被杀回收;越小,表示进程优先级越高,越不容易被杀回收
普通app进程的oom_adj>=0,系统进程的oom_adj才可能<0
有些手机厂商把这些知名的app放入了自己的白名单中,保证了进程不死来提高用户体验(如微信、QQ、陌陌都在小米的白名单中)。如果从白名单中移除,他们终究还是和普通app一样躲避不了被杀的命运,为了尽量避免被杀,还是老老实实去做好优化工作吧。
所以,进程保活的根本方案终究还是回到了性能优化上,进程永生不死终究是个彻头彻尾的伪命题!
3,View,ViewGroup事件分发
1), Touch事件分发中只有两个主角:ViewGroup和View。ViewGroup包含onInterceptTouchEvent、dispatchTouchEvent、onTouchEvent三个相关事件。View包含dispatchTouchEvent、onTouchEvent两个相关事件。其中ViewGroup又继承于View。
2),ViewGroup和View组成了一个树状结构,根节点为Activity内部包含的一个ViwGroup。
3),触摸事件由Action_Down、Action_Move、Aciton_UP组成,其中一次完整的触摸事件中,Down和Up都只有一个,Move有若干个,可以为0个。
4),当Acitivty接收到Touch事件时,将遍历子View进行Down事件的分发。ViewGroup的遍历可以看成是递归的。分发的目的是为了找到真正要处理本次完整触摸事件的View,这个View会在onTouchuEvent结果返回true。
5),当某个子View返回true时,会中止Down事件的分发,同时在ViewGroup中记录该子View。接下去的Move和Up事件将由该子View直接进行处理。由于子View是保存在ViewGroup中的,多层ViewGroup的节点结构时,上级ViewGroup保存的会是真实处理事件的View所在的ViewGroup对象:如ViewGroup0-ViewGroup1-TextView的结构中,TextView返回了true,它将被保存在ViewGroup1中,而ViewGroup1也会返回true,被保存在ViewGroup0中。当Move和UP事件来时,会先从ViewGroup0传递至ViewGroup1,再由ViewGroup1传递至TextView。
6),当ViewGroup中所有子View都不捕获Down事件时,将触发ViewGroup自身的onTouch事件。触发的方式是调用super.dispatchTouchEvent函数,即父类View的dispatchTouchEvent方法。在所有子View都不处理的情况下,触发Acitivity的onTouchEvent方法。
7),onInterceptTouchEvent有两个作用:1.拦截Down事件的分发。2.中止Up和Move事件向目标View传递,使得目标View所在的ViewGroup捕获Up和Move事件。
4,AIDL理解
AIDL: 每一个进程都有自己的Dalvik VM实例,都有自己的一块独立的内存,都在自己的内存上存储自己的数据,执行着自己的操作,都在自己的那片狭小的空间里过完自己的一生。而aidl就类似与两个进程之间的桥梁,使得两个进程之间可以进行数据的传输,跨进程通信有多种选择,比如 BroadcastReceiver , Messenger 等,但是 BroadcastReceiver 占用的系统资源比较多,如果频繁的跨进程通信显然是不可取的;Messenger 进行跨进程通信时请求队列是同步进行的,无法并发执行。
Binder机制简单理解:
在Android系统的Binder机制中,是有Client,Service,ServiceManager,Binder驱动程序组成的,其中Client,service,Service Manager运行在用户空间,Binder驱动程序是运行在内核空间的。而Binder就是把这4种组件粘合在一块的粘合剂,其中核心的组件就是Binder驱动程序,Service Manager提供辅助管理的功能,而Client和Service正是在Binder驱动程序和Service Manager提供的基础设施上实现C/S 之间的通信。其中Binder驱动程序提供设备文件/dev/binder与用户控件进行交互,
Client、Service,Service Manager通过open和ioctl文件操作相应的方法与Binder驱动程序进行通信。而Client和Service之间的进程间通信是通过Binder驱动程序间接实现的。而Binder Manager是一个守护进程,用来管理Service,并向Client提供查询Service接口的能力。