重新认识Activity

Activity是Android中最重要的组件之一,因为它是我们平时使用最频繁的、唯一可以直接和用户交互且对用户可见的一个组件,所以掌握它还是很有必要的。虽然我们最经常使用它,也应该最了解它,但是还是会有一些细节被我们忽略。最近在梳理自己的知识体系,忽然发现对Activity了解的还是不够深刻,因此有了这边文章。

一、Activity生命周期

Activity生命周期

activity生命周期

这幅图是Android官方提供的Activity的生命周期。可以看到在正常情况下,Activity会经历如下几个生命周期:

  1. onCreate:表示Activity正在被创建,这是Activity生命周期的第一个方法,也是我们在android开发中接触的最多的生命周期方法。它本身的作用是进行Activity的一些初始化工作,比如使用setContentView加载布局,对一些控件和变量进行初始化等。
  2. onStart:表示Activity正在启动,这是Activity生命周期的第二个方法。此时Activity已经可见了,但是还没出现在前台,我们还看不到,无法与Activity交互。每次Activity从后台切换到前台时都会重新调用onStart方法。
  3. onResume:表示Activity已经处于前台了,Activity在这个阶段已经真正的显示在前台且对用户可见,此时已经可以相应用户的操作了。需要注意的是onStart和onResume区别,其实两者都表示Activity已经可见了,但是onStart时Activity还处于后台,而onResume时Activity已经处于前台。具体区别后面会细讲。
  4. onPause:Activity正在被停止。当Activity正常关闭或从前天切换到后台时都会调用此方法。需要注意的是,在onPause中不能做耗时的操作,因为在跳转Activity时只有当上一个Activity执行完了onPause方法后另一个Activity才会启动,而且android中指定如果onPause在500ms即0.5秒内没有执行完毕的话就会强制关闭Activity。从生命周期图中发现可以在这快速重启,但这种情况其实很罕见,比如用户切到下一个Activity的途中按back键快速得切回来。
  5. onStop:表示Activity正在被停止。此时Activity已经不可见了,但是Activity对象还在内存中,没有被销毁。在这个阶段可以做一下稍微重量级一些的资源回收操作,但不能太耗时,比如关闭动画等。
  6. onDestroy:表示Activity即将被销毁,这是Activity生命周期的最后一个方法,执行完这个方法后,Activity将会被销毁掉。在这个方法中可以做一些回收工作,和一些资源的释放工作。
  7. 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。各种角色之间的关系可以参考下面这张图的介绍:


    Android系统进程间通讯Binder机制

具体Activity的启动流程请参照:
Android源码解析之(十四)-->Activity启动流程

三、Activity启动模式

Activity总共有四种启动模式:

  1. standard

这个模式是默认的启动模式,即标准模式,在不指定启动模式的前提下,系统默认使用该模式启动Activity,每次启动一个Activity都会重写创建一个新的实例,不管这个实例存不存在,这种模式下,谁启动了该模式的Activity,该Activity就属于启动它的Activity的任务栈中。这个Activity它的onCreate(),>onStart(),onResume()方法都会被调用。

activity默认的启动模式就是standard,在未配置Manifest中android:launchMode时Activity就会以standard模式启动

  1. singleTop

这个模式下,如果新的activity已经位于栈顶,那么这个Activity不会被重写创建,同时它的onNewIntent方法会被调用,通过此方法的参数我们可以去除当前请求的信息。如果栈顶不存在该Activity的实例,则情况与standard模式相同。需要注意的是这个Activity它的onCreate(),onStart()方法不会被调用,因为它并没>有发生改变。

  1. singleTask

这个模式十分复杂,有各式各样的组合。在这个模式下,如果栈中存在这个Activity的实例就会复用这个Activity,不管它是否位于栈顶,复用时,会将它上面的Activity全部出栈,并且会回调该实例的onNewIntent方法。其实这个过程还存在一个任务栈的匹配,因为这个模式启动时,会在自己需要的任务栈中寻找实例,这个任务栈就是通过taskAffinity属性指定。如果这个任务栈不存在,则会创建这个任务栈。

  1. 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到底是什么

你所不知道的Context的各种细节

部分内容援引自网络

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,718评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,683评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,207评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,755评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,862评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,050评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,136评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,882评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,330评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,651评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,789评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,477评论 4 333
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,135评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,864评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,099评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,598评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,697评论 2 351