Activity是Android中最重要的组件之一,因为它是我们平时使用最频繁的、唯一可以直接和用户交互且对用户可见的一个组件,所以掌握它还是很有必要的。虽然我们最经常使用它,也应该最了解它,但是还是会有一些细节被我们忽略。最近在梳理自己的知识体系,忽然发现对Activity了解的还是不够深刻,因此有了这边文章。
一、Activity生命周期
Activity生命周期
这幅图是Android官方提供的Activity的生命周期。可以看到在正常情况下,Activity会经历如下几个生命周期:
- onCreate:表示Activity正在被创建,这是Activity生命周期的第一个方法,也是我们在android开发中接触的最多的生命周期方法。它本身的作用是进行Activity的一些初始化工作,比如使用setContentView加载布局,对一些控件和变量进行初始化等。
- onStart:表示Activity正在启动,这是Activity生命周期的第二个方法。此时Activity已经可见了,但是还没出现在前台,我们还看不到,无法与Activity交互。每次Activity从后台切换到前台时都会重新调用onStart方法。
- onResume:表示Activity已经处于前台了,Activity在这个阶段已经真正的显示在前台且对用户可见,此时已经可以相应用户的操作了。需要注意的是onStart和onResume区别,其实两者都表示Activity已经可见了,但是onStart时Activity还处于后台,而onResume时Activity已经处于前台。具体区别后面会细讲。
- onPause:Activity正在被停止。当Activity正常关闭或从前天切换到后台时都会调用此方法。需要注意的是,在onPause中不能做耗时的操作,因为在跳转Activity时只有当上一个Activity执行完了onPause方法后另一个Activity才会启动,而且android中指定如果onPause在500ms即0.5秒内没有执行完毕的话就会强制关闭Activity。从生命周期图中发现可以在这快速重启,但这种情况其实很罕见,比如用户切到下一个Activity的途中按back键快速得切回来。
- onStop:表示Activity正在被停止。此时Activity已经不可见了,但是Activity对象还在内存中,没有被销毁。在这个阶段可以做一下稍微重量级一些的资源回收操作,但不能太耗时,比如关闭动画等。
- onDestroy:表示Activity即将被销毁,这是Activity生命周期的最后一个方法,执行完这个方法后,Activity将会被销毁掉。在这个方法中可以做一些回收工作,和一些资源的释放工作。
- onRestart:表示Activity重新可见,当用户按Home键切换到桌面后又切回来或者从后一个Activity切回前一个Activity就会触发这个方法。
Activity前后台切换生命周期变化:
按home键后:onPause()——>onStop()
重新返回程序: onRestart()——>onStart()——> onResume()
activity在后台被回收后重新返回当前Activity: onStop()——>onCreate()——> onStart()——> onResume()
点击打开新的Activity:第一个Activity onPause()——>第二个Activity onCreate()——>onStart()——>onResume()
——>第一个Activity onStop()
回退到当前Activity:第二个Activity onPause() ——> 第一个Activity onRestart()——>onStart()——>onResume()
——>第二个Activity onStop()——>onDestroy()
注意:1、在锁屏和恢复的时候onPause和onResume会被调用,而onStart和onStop不会被调用。
2、onDestroy方法不是每次都会被调用的(比如:当前应用处于后台,系统内存不足Activity被回收时在onstop之后Activity直接被回收掉,重新打开时,Activity会重新走onCreate-->onStart-->onResume)
因此,在Activity切换时,前一个Activity执行完onPause之后后一个Activity才会
执行onCreate以及之后的生命周期。因此onPause方法中不能做耗时的操作,
否则会影响到下一个Activity的启动速度。
二、启动Activity
Intent启动Activity的两种方式
Intent启动Activity分为两种:显式启动和隐式启动,显式启动就是在初始化Intent对象的时候直接引用需要启动的Activity的字节码,显示引用的好处就是可以直接告诉Intent对象启动的Activity对象不需要执行intent filter索引需要启动哪一个Activity,但是显示引用不能启动其他进程的Activity对象,因为无法获取其他进程的Activity对象的字节码,而隐式启动则可以通过配置Intent Filter启动其他进程的Activity对象,因此在应用内,我们一般都是使用显式启动的方式启动Activity,而如果需要启动其他应用的Activity时,一般使用隐式启动的式。
Activity的启动进程
在Manifest.xml中定义Activity的时候,Activity默认是属于进程名称为包名的进程的,当然这时候是可以指定Activity的启动进程。所以在Activity启动时首先会检测当前Activity所属的进程是否已经启动,若进程没有启动,则首先会启动该进程,并在该进程启动之后才会执行Activity的启动过程。
系统启动一个Activity
- 我们知道android系统在启动过程中会执行这样的逻辑:
Zygote进程 –> SystemServer进程 –> 各种系统服务 –> 应用进程
在Actvity启动过程中,其实是应用进程与SystemServer进程相互配合启动Activity的过程,其中应用进程主要用于执行具体的Activity的启动过程,回调生命周期方法等操作,而SystemServer进程则主要是调用其中的各种服务,将Activity保存在栈中,协调各种系统资源等操作。 -
Android系统进程间通讯Binder机制
Android系统存了Zygote进程和SystemServer进程以及各种应用进程等,为了能够实现各种进程之间的通讯,Android系统采用了自己的进程间通讯方式Binder机制。其中主要涉及到了四种角色:Binder Client,Binder Server,Binder Manager, Binder driver。各种角色之间的关系可以参考下面这张图的介绍:
具体Activity的启动流程请参照:
Android源码解析之(十四)-->Activity启动流程
三、Activity启动模式
Activity总共有四种启动模式:
- standard
这个模式是默认的启动模式,即标准模式,在不指定启动模式的前提下,系统默认使用该模式启动Activity,每次启动一个Activity都会重写创建一个新的实例,不管这个实例存不存在,这种模式下,谁启动了该模式的Activity,该Activity就属于启动它的Activity的任务栈中。这个Activity它的onCreate(),>onStart(),onResume()方法都会被调用。
activity默认的启动模式就是standard,在未配置Manifest中android:launchMode
时Activity就会以standard模式启动
- singleTop
这个模式下,如果新的activity已经位于栈顶,那么这个Activity不会被重写创建,同时它的onNewIntent方法会被调用,通过此方法的参数我们可以去除当前请求的信息。如果栈顶不存在该Activity的实例,则情况与standard模式相同。需要注意的是这个Activity它的onCreate(),onStart()方法不会被调用,因为它并没>有发生改变。
- singleTask
这个模式十分复杂,有各式各样的组合。在这个模式下,如果栈中存在这个Activity的实例就会复用这个Activity,不管它是否位于栈顶,复用时,会将它上面的Activity全部出栈,并且会回调该实例的onNewIntent方法。其实这个过程还存在一个任务栈的匹配,因为这个模式启动时,会在自己需要的任务栈中寻找实例,这个任务栈就是通过taskAffinity属性指定。如果这个任务栈不存在,则会创建这个任务栈。
- singleInstance
该模式具备singleTask模式的所有特性外,与它的区别就是,这种模式下的Activity会单独占用一个Task栈,具有全局唯一性,即整个系统中就这么一个实例,由于栈内复用的特性,后续的请求均不会创建新的Activity实例,除非这个特殊的任务栈被销毁了。以singleInstance模式启动的Activity在整个系统中是单例的,如果在启动这样的Activiyt时,已经存在了一个实例,那么会把它所在的任务调度到前台,重用这个实例。
四、broadCastReceive或者service中的context为什么不能启动Activity或者弹出一个dialog?
这里就要提到另外一个问题:Activity mActivity =new Activity()
这种直接new一个Activity的方式到底是否可行?
我们平时应该经常和Activity打交道,对如何创建一个Activity类也应该写起来得心应手。但是是否去想过这个问题呢?其实Android是基于java实现的,而java 中所有类都可以通过new方式创建一个对象,所以在Android中也一样。但问题是Android的应用模型是基于组件的应用设计模式,组件的运行要有一个完整的Android工程环境,在这个环境下,Activity、Service等系统组件才能够正常工作,而这些组件并不能采用普通的Java对象创建方式,new一下就能创建实例了,而是要有它们各自的上下文环境,也就是Context。
出于安全原因的考虑,Android是不允许Activity或Dialog凭空出现的,一个Activity的启动必须要建立在另一个Activity的基础之上,也就是以此形成的返回栈。而Dialog则必须在一个Activity上面弹出(除非是System Alert类型的Dialog)。因此service、broadCastReceive中的Activity不能启动Activity或是弹出dialog。
但是世事无绝对,在service或是broadCastReceive中,想要启动一个Activity也不是完全不行:
打开一个Activity:
Intent intent1=newIntent(context,main.class);
intent1.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(intent1);
显示一个dialog:
1. 申请悬浮窗权限(针对6.0以上手机)
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
2. 设置Dialog的类型为TYPE_SYSTEM_ALERT
alertDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
之所以通过这种方式就可以打开一个Activity,而通过默认启动模式打开Activity会报错是因为,启动一个Activity之后需要把Activity添加到任务栈中,而service或者 是broadCastReceive中的context不是启动Activity所需要的context,也就不能把Activity添加到任务栈。而通过FLAG_ACTIVITY_NEW_TASK模式启动的Activity会默认创建一个新的任务栈,所以可以正常启动。需要注意的是,这种方式启动的Activity会运行在一个新的任务栈中。
对于一个dialog而言,一个普通的dialog的启动必须要通过Activity的context来实现,因为通常情况下,dialog的启动会检查启动它的context中的token值,如果token不存在就会报错:所以如果想显示一个dialog的话,只能将dialog设置为系统级别dialog。
更多细节请阅读:
android中Context到底是什么
部分内容援引自网络