App启动流程和启动模式
启动流程
当用户点击一个App图标时,Click事件会通过Binder IPC机制调用startActivity(Intent), 最终调用到ActivityManagerService。在ActivityManagerService中做三件事:
1、通过PackageManager的resolveIntent()收集这个intent对象的指向信息,指向信息被存储在一个intent对象中。
2、通过grantUriPermissionLocked()方法来验证用户是否有足够的权限去调用该intent对象指向的Activity。
3、如果有权限, ActivityManagerService会检查这个进程的ProcessRecord是否存在。如果不存在, ActivityManagerService会创建新的进程来实例化目标activity。
ActivityManagerService创建新的进程并实例化目标activity分为三个步骤。
第一步,创建进程
ActivityManagerService调用startProcessLocked()方法来创建新的进程,该方法会通过socket通道传递参数给“Zygote”进程。 Zygote孵化自身,并调用ZygoteInit.main()方法来实例化ActivityThread对象并最终返回新进程的pid。随后ActivityThread依次调用Looper.prepareLoop()和Looper.loop()来开启消息循环。
第二步,绑定Application
第二步要做的就是将进程和指定的Application绑定起来。这个是通过ActivityThread对象中调用bindApplication()方法完成的。该方法发送一个BIND_APPLICATION的消息到消息队列中,最终通过handleBindApplication()方法处理该消息。然后调用makeApplication()方法来加载App的classes到内存中。
第三步,启动Activity
经过前两个步骤之后,系统中已经拥有了该App的进程。后面的步骤就是从一个已经存在的进程中启动一个新进程的activity了。该步骤实际调用方法是realStartActivity(),它会调用application线程对象中的sheduleLaunchActivity()发送一个LAUNCH_ACTIVITY消息到消息队列中,通过 handleLaunchActivity()来处理该消息。
经过这三个步骤,就成功启动了一个App。
启动模式
由上述App启动流程可以看出,App启动需要判断系统中是否有该App的进程,没有的话就需要创建。故App的启动模式可以根据系统中是否存在App的进程分为热启动和冷启动两种,如下:
冷启动:App没有启动过或App进程被killed, 系统中不存在该App进程。
热启动:App进程处于后台, 系统将其从后台带到前台, 展示给用户。
Activity启动模式
任务栈:
1、程序打开时就创建了一个任务栈, 用于存储当前程序的activity,所有的activity属于一个任务栈。
2、一个任务栈包含了一个activity的集合, 去有序的选择哪一个activity和用户进行交互:只有在任务栈栈顶的activity才可以跟用户进行交互。
3、任务栈可以移动到后台, 并且保留了每一个activity的状态. 并且有序的给用户列出它们的任务, 而且还不丢失它们状态信息。
4、退出应用程序时:当把所有的任务栈中所有的activity清除出栈时,任务栈会被销毁,程序退出。
任务栈的缺点:
1、每开启一次页面都会在任务栈中添加一个Activity,而只有任务栈中的Activity全部清除出栈时,任务栈被销毁,程序才会退出,这样就造成了用,户体验差, 需要点击多次返回才可以把程序退出了。
2、每开启一次页面都会在任务栈中添加一个Activity还会造成数据冗余, 重复数据太多, 会导致内存溢出的问题(OOM)。
为了解决任务栈的缺点,我们引入了启动模式。
启动模式(launchMode)在多个Activity跳转的过程中扮演着重要的角色,它可以决定是否生成新的Activity实例,是否重用已存在的Activity实例,是否和其他Activity实例公用一个task里。这里简单介绍一下task的概念,task是一个具有栈结构的对象,一个task可以管理多个Activity,启动一个应用,也就创建一个与之对应的task。
启动模式有如下4种:
- standard
standard模式是默认的启动模式,不用配置android:launchMode属性即可,当然也可以指定值为standard。跳转时不管栈中有没有已存在的实例,都生成新的实例。 - singleTop
配置属性android:launchMode=”singleTop”。跳转时系统会先在栈中寻找是否有对应activity实例正位于栈顶,如果有则不再生成新的,而是直接使用,否则生成新的实例。 - singleTask
配置属性android:launchMode=”singleTask”。跳转时如果系统发现栈中有对应的Activity实例,则使此Activity实例之上的其他Activity实例统统出栈,使此Activity实例成为栈顶对象,显示到幕前,否则生成新的实例。 - singleInstance
配置属性android:launchMode=”singleInstance”。跳转时会启用一个新的栈,并将Activity放置于这个新的栈结构中,并保证不再有其他Activity实例进入。
应用场景:
singleTop:适合接收通知启动的内容显示页面。例如新闻客户端的新闻内容页面、推送消息处理页面。
singleTask:适合作为程序入口点。例如浏览器的主界面,电商App主页面。
singleInstance:适合需要与程序分离开的页面。例如闹铃提醒,电商App秒杀提醒。singleInstance不要用于中间页面,如果用于中间页面,跳转会有问题,比如:A -> B (singleInstance) -> C,完全退出后,再次启动,首先打开的是B。
Activity生命周期
常规生命周期
onCreate : 该方法是在Activity被创建时回调,它是生命周期第一个调用的方法,我们在创建Activity时一般都需要重写该方法,然后在该方法中做一些初始化的操作,如通过setContentView设置界面布局的资源,初始化所需要的组件信息等。 此方法中不应该做一些耗时的操作,否则可能会引起黑屏和ANR。
onStart : 此方法被回调时表示Activity正在启动,此时Activity已处于可见状态,只是还没有在前台显示,因此无法与用户进行交互。可以简单理解为Activity已显示而我们无法看见罢了。
onResume : 当此方法回调时,则说明Activity已在前台可见,可与用户交互了(处于前面所说的Active/Running形态),onResume方法与onStart的相同点是两者都表示Activity可见,只不过onStart回调时Activity还是后台无法与用户交互,而onResume则已显示在前台,可与用户交互。当Activity停止后(onPause方法和onStop方法被调用),重新回到前台时也会调用onResume方法,因此我们也可以在onResume方法中初始化一些资源,比如重新初始化在onPause或者onStop方法中释放的资源。
onPause : 此方法被回调时则表示Activity正在停止(Paused形态),一般情况下onStop方法会紧接着被回调。有一种情况是onPause方法执行后直接执行了onResume方法,这属于比较极端的现象了,这可能是用户操作使当前Activity退居后台后又迅速地再回到到当前的Activity,此时onResume方法就会被回调。当然,在onPause方法中我们可以做一些数据存储或者动画停止或者资源回收的操作,但是不能太耗时,因为这可能会影响到新的Activity的显示——onPause方法执行完成后,新Activity的onResume方法才会被执行。
onStop: 一般在onPause方法执行完成直接执行,表示Activity即将停止或者完全被覆盖(Stopped形态),此时Activity不可见,仅在后台运行。同样地,在onStop方法可以做一些资源释放的操作(不能太耗时)。
onRestart :表示Activity正在重新启动,当Activity由不可见变为可见状态时,该方法被回调。这种情况一般是用户打开了一个新的Activity时,当前的Activity就会被暂停(onPause和onStop被执行了),接着又回到当前Activity页面时,onRestart方法就会被回调。
onDestroy :此时Activity正在被销毁,也是生命周期最后一个执行的方法,一般我们可以在此方法中做一些回收工作和最终的资源释放。
异常生命周期
- 1、相关的系统配置发生改变导致Activity被杀死并重新创建(一般指横竖屏切换)
正常启动Activity时,onCreate,onStart,onResume方法都会依次被回调,而如果我们此时把竖屏的Activity人为的调整为横屏,我们可以发现onPause,onSaveInstanceState,onStop,onDestroy,onCreate,onStart,onRestoreInstanceState,onResume依次被调用,单从调用的方法我们就可以知道,Activity先被销毁后再重新创建 - 2、内存不足导致低优先级的Activity被杀死
基本类似上一种情况
当我们不想Activity在屏幕旋转后导致销毁重建时,可以设置configChange=“orientation”;当SDK版本大于13时,我们还需额外添加一个“screenSize”的值,对于这两个值含义如下:
orientation:屏幕方向发生变化,配置该参数可以解决横竖屏切换时,Activity重建问题(API<13)
screenSize:当设备旋转时,屏幕尺寸发生变化,API>13后必须配置该参数才可以保证横竖切换不会导致Activity重建。
设置了这两个参数后,当横竖屏切换时,Activity不会再重建并且也不会调用之前相关的方法,取而代之的是回调onConfigurationChanged方法。
Activity与Window/View的关系
- Window 是什么?
Window 是 Android 中窗口的宏观定义,主要是管理 View 的创建,以及与 ViewRootImpl 的交互,将 Activity 与 View 解耦。 - Activity 与 PhoneWindow 与 DecorView 之间什么关系?
一个 Activity 对应一个 Window 也就是 PhoneWindow,一个 PhoneWindow 持有一个 DecorView 的实例,DecorView 本身是一个 FrameLayout。
Activity的主要作用是生命周期的管理,Window是一个视图容器,WindowManager统一管理View。所以Activity和Window的关联主要是体现在生命周期的管理和事件的回调上,Window和View的关联体现在对View视图的处理上。
Activity与Fragment
- 生命周期不同
Activity有7个生命周期:onCreate()、onStart()、onRestart()、onResume()、onPause() 、onStop() 、onDestroy();
Fragment有11个生命周期:onAttach() 、onCreate() 、onCreateView() 、onActivityCreate()、onStart()、onResume() 、onPause() 、onStop() 、onDestroyView()、onDestroy()、onDetach();
onAttach()
作用:fragment已经关联到activity,这个时候 activity已经传进来了, 获得activity的传递的值 就可以进行 与activity的通信里, 当然也可以使用getActivity(),前提是这个fragment已经和宿主的activity关联,并且没有脱离,有且只有调用一次。
onCreate()
系统创建fragment的时候回调,在里面实例化一些变量,这些个变量主要是:当暂停、停止的时候想保持的数据、只调用一次。
onCreateView()
第一次使用的时候 fragment会在这上面画一个layout出来, 为了可以画控件 要返回一个 布局的view,也可以返回null就什么都没有显示。
当系统用到fragment的时候 fragment就要返回他的view,越快越好 ,所以尽量在这里不要做耗时操作,比如从数据库加载大量数据
onActivityCreated()
当Activity中的onCreate方法执行完后调用。当执行onActivityCreated()的时候 activity的onCreate才刚完成。所以在onActivityCreated()调用之前activity的onCreate可能还没有完成,所以不能再onCreateView()中进行与activity有交互的UI操作,UI交互操作可以在onActivityCreated()里面进行。所以呢,这个方法主要是初始化那些你需要你的父Activity或者Fragment的UI已经被完整初始化才能初始化的元素。
onStart()
和activity一致,启动Fragement 启动时回调,,此时Fragement可见。
onResume()
和activity一致 在activity中运行是可见的。激活, Fragement 进入前台, 可获取焦点时激活。
onPause()
和activity一致 其他的activity获得焦点,这个仍然可见第一次调用的时候,指的是 用户 离开这个fragment(并不是被销毁)
通常用于 用户的提交(可能用户离开后不会回来了)
onStop()
和activity一致, fragment不可见的, 可能情况:activity被stopped了或者 fragment被移除但被,加入到回退栈中,一个stopped的fragment仍然是活着的如果长时间不用也会被移除。
onDestroyView()
Fragment中的布局被移除时调用。表示fragemnt销毁相关联的UI布局, 清除所有跟视图相关的资源。然后这个知识移除视图 并没有销毁而且还没有脱离activity
onDestroy()
销毁fragment对象, 跟activity类似了。
onDetach()
Fragment和Activity解除关联的时候调用。 脱离activity。
所以Fragment比较与Activity来说会更加灵活,因为生命周期多了,可以控制的地方也就多了。 - 使用的灵活性不同
Fragment显得更加灵活。可以直接在XML文件中添加<fragment/>,Activity则不能;Fragment可以在一个界面上灵活的替换一部分页面,Activity不可以,Activity 只能进行跳转切换。 - 控件加载的方式不同
Fragment的载入是通过OnCreateView的时候通过inflater.inflate()加载布局
Activity的载入是通过OnCreate的时候通过setContentView()加载布局
Fragment 懒加载
为什么需要懒加载?
减少不必要的资源浪费
什么情况下需要懒加载?
使用viewpager+adapter作为应用大的布局时
懒加载的实现方式:
1、setUserVisibleHint + onHiddenChanged
2、FragmentTransaction.setMaxLifecycle()
网友评论