Activity详解

Activity是什么?

我们都知道android中有四大组件(Activity 活动,Service 服务,Content Provider 内容提供者,BroadcastReceiver 广播接收器),Activity是我们用的最多也是最基本的组件,因为应用的所有操作都与用户相关,Activity 提供窗口来和用户进行交互。

官方文档这么说:

An activity is a single, focused thing that the user can do. Almost all activities interact with the user, so the Activity class takes care of creating a window for you in which you can place your UI with setContentView(View).

大概的意思:

activity是独立平等的,用来处理用户操作。几乎所有的activity都是用来和用户交互的,所以activity类会创建了一个窗口,开发者可以通过setContentView(View)的接口把UI放到给窗口上。

Android中的activity全都归属于task管理 。task 是多个 activity 的集合,这些 activity 按照启动顺序排队存入一个栈(即“back stack”)。android默认会为每个App维持一个task来存放该app的所有activity,task的默认name为该app的packagename。

当然我们也可以在AndroidMainfest.xml中申明activity的taskAffinity属性来自定义task,但不建议使用,如果其他app也申明相同的task,它就有可能启动到你的activity,带来各种安全问题(比如拿到你的Intent)。

Activity的内部调用过程

上面已经说了,系统通过堆栈来管理activity,当一个新的activity开始时,它被放置在堆栈的顶部和成为运行活动,以前的activity始终保持低于它在堆栈,而不会再次到达前台,直到新的活动退出。

还是上这张官网的activity_lifecycle图:

这里写图片描述
  • 首先打开一个新的activity实例的时候,系统会依次调用

onCreate() -> onStart() -> onResume() 然后开始running

running的时候被覆盖了(从它打开了新的activity或是被锁屏,但是它依然在前台运行, lost focus but is still visible),系统调用onPause();

该方法执行activity暂停,通常用于提交未保存的更改到持久化数据,停止动画和其他的东西。但这个activity还是完全活着(它保持所有的状态和成员信息,并保持连接到窗口管理器

接下来它有三条出路
①用户返回到该activity就调用onResume()方法重新running

②用户回到桌面或是打开其他activity,就会调用onStop()进入停止状态(保留所有的状态和成员信息,对用户不可见

③系统内存不足,拥有更高限权的应用需要内存,那么该activity的进程就可能会被系统回收。(回收onRause()和onStop()状态的activity进程)要想重新打开就必须重新创建一遍。

如果用户返回到onStop()状态的activity(又显示在前台了),系统会调用

onRestart() -> onStart() -> onResume() 然后重新running

在activity结束(调用finish ())或是被系统杀死之前会调用onDestroy()方法释放所有占用的资源。

activity生命周期中三个嵌套的循环

  • activity的完整生存期会在 onCreate() 调用和 onDestroy() 调用之间发生。

  • activity的可见生存期会在 onStart() 调用和 onStop() 调用之间发生。系统会在activity的整个生存期内多次调用 onStart() 和onStop(), 因为activity可能会在显示和隐藏之间不断地来回切换。

  • activity的前后台切换会在 onResume() 调用和 onPause() 之间发生。
    因为这个状态可能会经常发生转换,为了避免切换迟缓引起的用户等待,这两个方法中的代码应该相当地轻量化。

activity被回收的状态和信息保存和恢复过程

public class MainActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        if(savedInstanceState!=null){ //判断是否有以前的保存状态信息
             savedInstanceState.get("Key");
             }
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }
   @Override
protected void onSaveInstanceState(Bundle outState) {
    // TODO Auto-generated method stub
     //可能被回收内存前保存状态和信息,
       Bundle data = new Bundle();
       data.putString("key", "last words before be kill");
       outState.putAll(data);
    super.onSaveInstanceState(outState);
}
   @Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
    // TODO Auto-generated method stub
       if(savedInstanceState!=null){ //判断是否有以前的保存状态信息
             savedInstanceState.get("Key");
             }
    super.onRestoreInstanceState(savedInstanceState);
}
}

onSaveInstanceState方法

在activity 可能被回收之前 调用,用来保存自己的状态和信息,以便回收后重建时恢复数据(在onCreate()或onRestoreInstanceState()中恢复)。旋转屏幕重建activity会调用该方法,但其他情况在onRause()和onStop()状态的activity不一定会调用 ,下面是该方法的文档说明。

One example of when onPause and onStop is called and not this method is when a user navigates back from activity B to activity A: there is no need to call onSaveInstanceState on B because that particular instance will never be restored, so the system avoids calling it. An example when onPause is called and not onSaveInstanceState is when activity B is launched in front of activity A: the system may avoid calling onSaveInstanceState on activity A if it isn't killed during the lifetime of B since the state of the user interface of A will stay intact.

也就是说,系统灵活的来决定调不调用该方法,但是如果要调用就一定发生在onStop方法之前,但并不保证发生在onPause的前面还是后面。

onRestoreInstanceState方法

这个方法在onStart 和 onPostCreate之间调用,在onCreate中也可以状态恢复,但有时候需要所有布局初始化完成后再恢复状态。

onPostCreate:一般不实现这个方法,当程序的代码开始运行时,它调用系统做最后的初始化工作。

启动模式

启动模式什么?

简单的说就是定义activity 实例与task 的关联方式。

为什么要定义启动模式?

为了实现一些默认启动(standard)模式之外的需求:

  • 让某个 activity 启动一个新的 task (而不是被放入当前 task )

  • 让 activity 启动时只是调出已有的某个实例(而不是在 back stack 顶创建一个新的实例)

  • 或者,你想在用户离开 task 时只保留根 activity,而 back stack 中的其它 activity 都要清空

怎样定义启动模式?

定义启动模式的方法有两种:

使用 manifest 文件

在 manifest 文件中activity声明时,利用 activity 元素的 launchMode 属性来设定 activity 与 task 的关系。

        <activity
            ......
            android:launchMode="standard"
             >
           .......
        </activity>

注意: 你用 launchMode 属性为 activity 设置的模式可以被启动 activity 的 intent 标志所覆盖。

有哪些启动模式?

  • "standard" (默认模式)

当通过这种模式来启动Activity时, Android总会为目标 Activity创建一个新的实例,并将该Activity添加到当前Task栈中。这种方式不会启动新的Task,只是将新的 Activity添加到原有的Task中。

  • "singleTop"

该模式和standard模式基本一致,但有一点不同:当将要被启动的Activity已经位于Task栈顶时,系统不会重新创建目标Activity实例,而是直接复用Task栈顶的Activity。

  • "singleTask"

Activity在同一个Task内只有一个实例。
  
  如果将要启动的Activity不存在,那么系统将会创建该实例,并将其加入Task栈顶;

如果将要启动的Activity已存在,且存在栈顶,直接复用Task栈顶的Activity。

如果Activity存在但是没有位于栈顶,那么此时系统会把位于该Activity上面的所有其他Activity全部移出Task,从而使得该目标Activity位于栈顶。

  • "singleInstance"

无论从哪个Task中启动目标Activity,只会创建一个目标Activity实例且会用一个全新的Task栈来装载该Activity实例(全局单例).

如果将要启动的Activity不存在,那么系统将会先创建一个全新的Task,再创建目标Activity实例并将该Activity实例放入此全新的Task中。

如果将要启动的Activity已存在,那么无论它位于哪个应用程序,哪个Task中;系统都会把该Activity所在的Task转到前台,从而使该Activity显示出来。

使用 Intent 标志

在要启动 activity 时,你可以在传给 startActivity() 的 intent 中包含相应标志,以修改 activity 与 task 的默认关系。

              Intent i = new Intent(this,NewActivity.class);
        i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        startActivity(i);

可以通过标志修改的默认模式有哪些?

  • FLAG_ACTIVITY_NEW_TASK

与"singleTask"模式相同,在新的 task 中启动 activity。如果要启动的 activity 已经运行于某 task 中,则那个 task 将调入前台。

  • FLAG_ACTIVITY_SINGLE_TOP

与 "singleTop"模式相同,如果要启动的 activity位于back stack 顶,系统不会重新创建目标Activity实例,而是直接复用Task栈顶的Activity。

  • FLAG_ACTIVITY_CLEAR_TOP

此种模式在launchMode中没有对应的属性值。如果要启动的 activity 已经在当前 task 中运行,则不再启动一个新的实例,且所有在其上面的 activity 将被销毁。

关于启动模式的一些建议

一般不要改变 activity 和 task 默认的工作方式。 如果你确定有必要修改默认方式,请保持谨慎,并确保 activity 在启动和从其它 activity 返回时的可用性,多做测试和安全方面的工作。

Intent Filter

android的3个核心组件——Activity、services、广播接收器——是通过intent传递消息的。intent消息用于在运行时绑定不同的组件。在 Android 的 AndroidManifest.xml 配置文件中可以通过 intent-filter 节点为一个 Activity 指定其 Intent Filter,以便告诉系统该 Activity 可以响应什么类型的 Intent。

intent-filter 的三大属性

Action

一个 Intent Filter 可以包含多个 Action,Action 列表用于标示 Activity 所能接受的“动作”,它是一个用户自定义的字符串。

<intent-filter >
 <action android:name="android.intent.action.MAIN" />
 <action android:name="com.scu.amazing7Action" />
……
 </intent-filter>

在代码中使用以下语句便可以启动该Intent 对象:

Intent i=new Intent();
i.setAction("com.scu.amazing7Action");

Action 列表中包含了“com.scu.amazing7Action”的 Activity 都将会匹配成功

URL

在 intent-filter 节点中,通过 data节点匹配外部数据,也就是通过 URI 携带外部数据给目标组件。

<data
  android:mimeType="mimeType"
  android:scheme="scheme"
  android:host="host"
  android:port="port"
  android:path="path"/>

注意:只有data的所有的属性都匹配成功时 URI 数据匹配才会成功

Category

为组件定义一个 类别列表,当 Intent 中包含这个类别列表的所有项目时才会匹配成功。

<intent-filter . . . >
   <action android:name="code android.intent.action.MAIN" />
   <category android:name="code android.intent.category.LAUNCHER" />
</intent-filter>

Activity 种 Intent Filter 的匹配过程

①加载所有的Intent Filter列表

②去掉action匹配失败的Intent Filter

③去掉url匹配失败的Intent Filter

④去掉Category匹配失败的Intent Filter

⑤判断剩下的Intent Filter数目是否为0。如果为0查找失败返回异常;如果大于0,就按优先级排序,返回最高优先级的Intent Filter

开发中Activity的一些问题

  • 一般设置Activity为非公开的
<activity  
......
android:exported="false" />

注意:非公开的Activity不能设置intent-filter,以免被其他activity唤醒(如果拥有相同的intent-filter)。

  • 不要指定activity的taskAffinity属性

  • 不要设置activity的LaunchMode(保持默认)

注意Activity的intent最好也不要设定为FLAG_ACTIVITY_NEW_TASK

  • 在匿名内部类中使用this时加上activity类名(类名.this,不一定是当前activity)

  • 设置activity全屏

在其 onCreate()方法中加入:

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

推荐阅读更多精彩内容

  • 3.2 Activity详解 3.2.1 生命周期分析 典型情况下生命周期分析 一般情况下,当当前Activity...
    jianhuih阅读 1,106评论 0 0
  • 【Android Activity】 什么是 Activity? 四大组件之一,通常一个用户交互界面对应一个 ac...
    Rtia阅读 3,798评论 3 18
  • Activity 一、四种形态 运行状态: 当 Activity 处于栈的顶层,可见,并可与用户进行交互 onRe...
    任教主来也阅读 1,643评论 1 10
  • 目录 Activity 生命周期 任务栈 启动模式 Intent Flag taskAffinity属性 1.Ac...
    gaaaaaaaaaao阅读 2,678评论 10 12
  • 第二天的酸疼 还没开始 抗拒开始 跟不上的节奏 跟不上的动作 对抗,push 终于快进停止 还是分泌雨丝 未知
    歌德爱科技阅读 112评论 0 0