了解了整体的android创建流程之后,就分析一下到底这个过程中做了什么?
activity创建中开始时由activityStack中的realstartActivityLocked函数中调用了activityThread中的scheduleLaunchActicity,然后利用H发送消息调用handleLaunchActivity,由此就开始了创建activity的全过程。
创建和生命周期:
1.在handleLaunchActivity中调用了performLanuchActivity:
tip:其中有一个ActivityClientRecord,这个是activity的一个数据对象,里面有创建activity的数据,创建activity就需要他,同时在服务程序中ActivityStack中使用的是ActivityRecord。
这个函数中执行:加载packageinfo的信息,也就是你的包的信息,创建程序总有一些配置和管理的package信息。获取CompontName,这个是用来匹配启动activity的信息的,然后就是利用Instramentation创建出activity的实例。
tip:这里的Instramentation就是专门去管理activity的操作类,里面包括了创建activity和调用activity的生命周期方法。这个时候会考虑既然activityThread就是管理activity的为什么还要有一个Instramentation?其实,这样考虑,avtivityThread相当于是actvity的主人,主要负责管理activity的生命周期和外界的交互。但是Instramentation仅仅是一个管家,他要做的是就是activity紧密相关的。avtivityThread做的更加广,Instramentation做的更加细致。
这个时候要考虑一个问题,创建出来activity,就是我们的activity了吗?他仅仅是一个类而已怎么和app的生命关联在一起的呢?接着又做了一件事,利用Instramentation创建了application,这也是一个对象而已,又是怎么成为app全局存在?重点在于Context,context是上下文的意思,是整个进程的运行环境依赖,也就是运行时候的重要数据和操作资源的入口。所以要是和Context联系在一起,就变得不是寻常的类,这个时候无论activity和application都用了attach函数,就是挂在和自己相关的context。
tip:又有一个常见的问题出现了application context 和activity context 一样吗?不一样,我们看源码可以发现者两者的Context都是new出来并且都是ContextImpl对象,虽然不一样,他了的不同之处到底在哪里?全局只有一个application,但是每一个activity都有一个context。在创建ContextImpl的时候要传递参数有3个:loadedapk,binder,activityThread。loadedapk就是加载和处理包信息的。创建的时候只有binder对象传的不同,这个binder是用来和activityMangaerService交互的。application创建的时候没有传递这个参数,所以注定了application Context是和activity无关的,也就说:在和activity相关的类使用activity Context,其他情况都可以。但是application Context的域范围更加广泛。
这也就有一个activity中内存泄露的问题,activity context只在其生命周期有效,假如被外界利用了,并且保存了引用,如果要是activity 被销毁了,但是Context内存并不能释放,长期运行下去,一定会有问题。所以合理的使用Context对于运行时间长的app一定要注意。这个时候就
有了一个application的使用技巧:到底application可以做什么?一般来说application是全局的,我们可以利用application来数据交互,也就是在application定义一些数据集合比如hashmap,list之类,由于数据时贯穿app生命中的,所以每一个activity都可以使用。还有就是定义一些全局的广播接收者,接收全局数据或消息,还有一个用处就是定义一个Activity的stack,由于有时候需要关闭所有的activity.利用stack来保存。还有一个用处就是捕获全局的一场,其实Thread就有一个可以捕获线程异常的方法Thread.setDefaultUncaughtExceptionHandler(handler),但是activity的生命有限,application是整个生命周期的,正好可以用来做这个。
又有一个问题产生了,在多个activity的时候会使用抽象一个父类baseactivity,为什么不在baseActivity中做呢?当你产生一个异常的时候重启应用会把之前的数据清空了,所有的数据都会丢失掉,但是在application中的数据不会。我不知道这个重启的操作中做了什么,但是实验就是这个结果,以后如果看源码知道了在贴出。
挂在Context在application和actvity对象上,activity的attach方法更加复杂一些,要创建phonewindow等操作。之后会设置activity的主题Theme。接着就调用Instramentation调用了oncreate。然后会调用performStart,也就是onStart,所以感觉这个onstart方法十分鸡肋,oncreate和onstart之间并没有什么特殊动作,我感觉onstart是为了更加明确的分辨activity的生命周期。这也就是之后我们要讨论为什么这么划分生命周期?然后看一下是否是重启应用也就是之前的一个state中有无数据,有的话就要调用onRestoreInstanceState() 到处两个生命周期方法就结束了。
tip:其中为了明确分辨到底是处于什么生命周期,用的就是boolean的标志,有mCalled这个主要的作用是到底调用了activity的方法没有比如你的activity中没有用super.onCreate()那么就会有一个runtime异常。来强制你调用父类。还有一些mStoped,mPaused,mResume,mFInished都是用来标示生命周期的。
2.接着调用了HandlResmeActivity,里面有performResumeActivity:
检查一下r.pendIntent是否要执行onnewIntent,也就是当你是singletask模式的时候,会直接执行这个方法。然后调用了performResume,也就是onResume,这时候设置paused,stoped为fasle,进入了可交互时期。然后会挂载我们的布局。
到此为止创建activity的生命周期方法就都完成了,我们可以看到一个activity有些什么?父类和接口是一个可以做的动作的表现形式,Context,window和key的callback等,所以activity一定可以处理和调用当前应用系统的资源,处理window和key的事件,也就是处理用户交互的动作包括按键和触屏。实际上着也就是actvity要做的事情,在创建的生命周期中做好这些处理,挂在Context,然后生成phonewindow利用里面的viewroot进行用户交互(按键和触屏)。当然我们要联系所有app应用的调用,当一个actvity不显示的时候,也就是被覆盖了,你就不可以和用户交互了,失去焦点了。一定要有onPause,当你的activity界面消失,不是销毁。比如按了home。就要调用onstop,但是没有调用ondestory,这是因为在产生activity的时候要创建一系列类要和AMS(activityMangerService)交互,这个过程很麻烦,很可能我还要在开启这个应用,那么我索性就不销毁了,等到下一次直接onstart就可以。所以这也就是onstart的作用,这个动作是一定要有的,在查看源码的时候要考虑用户交互和效率的问题。
所以生命周期函数实际上就是诠释了activity的交互流程,什么时候该销毁,什么时候该消失,什么时候失去焦点。
3然后我们就说一下对应的结束调用,实际上后面的这几个动作就是为了销毁时候做好收尾动作
finish的时候,ActivityManagerNative告诉ctivityMangerService在调用activityStack发送消息给activityThread的HandleDestoryActivity,然后依次调用onSaveInstanceState,onPause,onstop,ondestory.其中没有什么特殊操作,仅仅是方法调用而已。设置标志位mstoped,mpasued等
tip:其实还有一个疑问就是调用生命周期函数onstart,onrestart,onresume,onstop时候都要调用Activity的performXXX然后回调Instramentation的callActivityonXXX,但是唯独onpause是直接调用的Instramentation的方法。并且其他的操作都只是判断一下mCalled也就是调用了父类没有,但是onpasue里面还要检测是否当前sdk版本和os的sdk版本是否一致,这个让我很困惑,到底为什么?(未解决)
4最后简单的说一下Activity的显示模式:一共有四个standard,singletop,singletask和singleInstance。其中特殊一点的就是singleIntance,在系统服务中会保存各种的actvity的启动和销毁的信息,就比如历史启动的信息HistoryRecord,都有一些栈。但是运行的栈也是不同的,之前的模式都是在一个栈结构里面,但是singleInstance是单独的栈结构。浏览器就是这种模式,主要是为了所有的应用都可以使用这个实例。而singletask也有一点特殊,就是他是只有一个实例,但是不会开任务栈,假如有该实例,就会把在他上面的栈元素都清空。这个两种都是很单一的实例。但是特性是不同的。在设计界面交互的时候一定要考虑好跳转关系,actvity的调度顺序一定要优化好。通常现在的应用不会产生很多activity,而是使用在同一个界面中的选项卡来进行交互。这样很好的处理了跳转逻辑,但是一定要处理好跳转的信息处理。有利有弊。