Android记录官方文档系列(1):Activity

转载请注意:http://blog.csdn.net/wjzj000/article/details/55272272


我和一帮应届生同学维护了一个公众号:IT面试填坑小分队。旨在帮助应届生从学生过度到开发者,并且每周树立学习目标,一同进步!
这里写图片描述


写在前面

随着毕业将近,越来越不知道该如何去系统的学习了。而且发现越来越难以集中注意力。有时连玩的时候都得扇自己一巴掌,玩个游戏还分心。

自己明明上了一个很差的大学,明明应该好好学习。可是就是坚持不住。
打开AS三分钟,还是看会电影吧,再看会直播吧…妈的,我发誓!要是再不干正事,就剁手…
然后用残缺的身体重新看了生化危机系列,加勒比海盗系列…

大家好,我是一个残疾程序猿…其实不用手也能过的很滋润,就是硬盘里的摇杆驱动程序的教学视频看的不是很方便。

为了能够老老实实干点正事,就开了这个博客系列:看官方文档,然后对其中的内容进行记录。

OK,正式开始:https://developer.android.google.cn/guide/index.html


Activity:

Activity 是一个应用组件,用户可与其提供的屏幕进行交互,以执行拨打电话、拍摄照片、发送电子邮件或查看地图等操作。

每个 Activity 都会获得一个用于绘制其用户界面的窗口(Window)。窗口通常会充满屏幕,但也可小于屏幕并浮动在其他窗口之上。

一个应用通常由多个彼此松散联系的 Activity 组成。每个 Activity 均可启动另一个 Activity,以便执行不同的操作。

每次新 Activity 启动时,前一 Activity 便会停止,但系统会在堆栈(“返回栈”)中保留该 Activity。 当新 Activity 启动时,系统会将其推送到返回栈上,并取得用户焦点。

返回栈遵循基本的“后进先出”堆栈机制,因此,当用户完成当前 Activity 并按“返回”按钮时,系统会从堆栈中将其弹出(并销毁),然后恢复前一 Activity。


任务

任务(Task)是指在执行特定作业时与用户交互的一系列 Activity

任务是一个有机整体,当用户开始新任务或通过“主页”按钮转到主屏幕时,可以移动到“后台”。 尽管在后台时,该任务中的所有 Activity 全部停止,但是任务的返回栈仍旧不变,也就是说,当另一个任务发生时,该任务仅仅失去焦点而已。

PS: Application,Task和Process的区别与联系
(原文链接:http://blog.csdn.net/zhangjg_blog/article/details/10923643

application翻译成中文时一般称为“应用”或“应用程序”,在android中,总体来说一个应用就是一组组件的集合。众所周知,android是在应用层组件化程度非常高的系统,android开发的第一课就是学习android的四大组件。当我们写完了多个组件,并且在manifest文件中注册了这些组件之后,把这些组件和组件使用到的资源打包成apk,我们就可以说完成了一个application。application和组件的关系可以在manifest文件中清晰地体现出来。如下所示:

<?xml version="1.0" encoding="utf-8"?>  
<manifest android:versionCode="1"  
        android:versionName="1"  
        xmlns:android="http://schemas.android.com/apk/res/android"  
        package="com.example.android.myapp">  

    <application android:label="@string/app_name">  
        <activity android:name=".MyActivity" android:label="@string/app_name">  
            <intent-filter>  
                <action android:name="android.intent.action.MAIN" />  
                <category android:name="android.intent.category.LAUNCHER" />  
            </intent-filter>  
        </activity>  
    <receiver android:name=".MyReceiver"/>  
    <provider android:name=".MyProvider"/>  
    <service android:name=".MyService"/>  
    </application>  
</manifest>  

由此可见,application是由四大组件组成的。在app安装时,系统会读取manifest的信息,将所有的组件解析出来,以便在运行时对组件进行实例化和调度。

而task是在程序运行时,只针对activity的概念。说白了,task是一组相互关联的activity的集合,它是存在于framework层的一个概念,控制界面的跳转和返回。这个task存在于一个称为back stack的数据结构中,也就是说,framework是以栈的形式管理用户开启的activity。这个栈的基本行为是,当用户在多个activity之间跳转时,执行压栈操作,当用户按返回键时,执行出栈操作。举例来说,如果应用程序中存在A,B,C三个activity,当用户在Launcher或Home Screen点击应用程序图标时,启动主Activity A,接着A开启B,B开启C,这时栈中有三个Activity,并且这三个Activity默认在同一个任务(task)中,当用户按返回时,弹出C,栈中只剩A和B,再按返回键,弹出B,栈中只剩A,再继续按返回键,弹出A,任务被移除。如下图所示:

这里写图片描述

task是可以跨应用的,这正是task存在的一个重要原因。有的Activity,虽然不在同一个app中,但为了保持用户操作的连贯性,把他们放在同一个任务中。例如,在我们的应用中的一个Activity A中点击发送邮件,会启动邮件程序的一个Activity B来发送邮件,这两个activity是存在于不同app中的,但是被系统放在一个任务中,这样当发送完邮件后,用户按back键返回,可以返回到原来的Activity A中,这样就确保了用户体验。

说完了application和task,最后介绍process。process一般翻译成进程,进程是操作系统内核中的一个概念,表示直接受内核调度的执行单位。在应用程序的角度看,我们用java编写的应用程序,运行在dalvik虚拟机中,可以认为一个运行中的dalvik虚拟机实例占有一个进程,所以,在默认情况下,一个应用程序的所有组件运行在同一个进程中。但是这种情况也有例外,即,应用程序中的不同组件可以运行在不同的进程中。只需要在manifest中用process属性指定组件所运行的进程的名字。如下所示:

<activity android:name=".MyActivity" android:label="@string/app_nam"  
    android:process=":remote">  
</activity>  

这样的话这个activity会运行在一个独立的进程中。


返回栈

当用户触摸应用启动器中的图标(或主屏幕上的快捷方式)时,该应用的任务将出现在前台。 如果应用不存在任务(应用最近未曾使用),则会创建一个新任务,并且该应用的“主”Activity 将作为堆栈中的根 Activity 打开。

当前 Activity 启动另一个 Activity 时,该新 Activity 会被推送到堆栈顶部,成为焦点所在。 前一个 Activity 仍保留在堆栈中,但是处于停止状态。Activity 停止时,系统会保持其用户界面的当前状态。 用户按“返回”按钮时,当前 Activity 会从堆栈顶部弹出(Activity 被销毁),而前一个 Activity 恢复执行(恢复其 UI 的前一状态)。 堆栈中的 Activity 永远不会重新排列,仅推入和弹出堆栈:由当前 Activity 启动时推入堆栈;用户使用“返回”按钮退出时弹出堆栈。 因此,返回栈以“后进先出”对象结构运行。 图 1 通过时间线显示 Activity 之间的进度以及每个时间点的当前返回栈,直观呈现了这种行为。

这里写图片描述


Activity的启动模式

使用清单文件

在清单文件中声明 Activity 时,您可以使用 元素的 launchMode 属性指定 Activity 应该如何与任务关联。

launchMode 属性指定有关应如何将 Activity 启动到任务中的指令。您可以分配给 launchMode 属性的启动模式共有四种:

“standard”(默认模式)
默认。系统在启动 Activity 的任务中创建 Activity 的新实例并向其传送 Intent。Activity 可以多次实例化,而每个实例均可属于不同的任务,并且一个任务可以拥有多个实例。

“singleTop”
如果当前任务的顶部已存在 Activity 的一个实例,则系统会通过调用该实例的 onNewIntent() 方法向其传送 Intent,而不是创建 Activity 的新实例。Activity 可以多次实例化,而每个实例均可属于不同的任务,并且一个任务可以拥有多个实例(但前提是位于返回栈顶部的 Activity 并不是 Activity 的现有实例)。
例如,假设任务的返回栈包含根 Activity A 以及 Activity B、C 和位于顶部的 D(堆栈是 A-B-C-D;D 位于顶部)。收到针对 D 类 Activity 的 Intent。如果 D 具有默认的 “standard” 启动模式,则会启动该类的新实例,且堆栈会变成 A-B-C-D-D。但是,如果 D 的启动模式是 “singleTop”,则 D 的现有实例会通过 onNewIntent() 接收 Intent,因为它位于堆栈的顶部;而堆栈仍为 A-B-C-D。但是,如果收到针对 B 类 Activity 的 Intent,则会向堆栈添加 B 的新实例,即便其启动模式为 “singleTop” 也是如此。

注:为某个 Activity 创建新实例时,用户可以按“返回”按钮返回到前一个 Activity。 但是,当 Activity 的现有实例处理新 Intent 时,则在新 Intent 到达 onNewIntent() 之前,用户无法按“返回”按钮返回到 Activity 的状态。

“singleTask”
系统创建新任务(创建新的任务这种说法不是很准确,具体内容可以参考上边提到的链接)并实例化位于新任务底部的 Activity。但是,如果该 Activity 的一个实例已存在于一个单独的任务中,则系统会通过调用现有实例的 onNewIntent() 方法向其传送 Intent,而不是创建新实例。一次只能存在 Activity 的一个实例。
注:尽管 Activity 在新任务中启动,但是用户按“返回”按钮仍会返回到前一个 Activity。

“singleInstance”
与 “singleTask” 相同,只是系统不会将任何其他 Activity 启动到包含实例的任务中。该 Activity 始终是其任务唯一仅有的成员;由此 Activity 启动的任何 Activity 均在单独的任务中打开。

PS:关于singleInstance的理解:

  • 以singleInstance模式启动的Activity具有全局唯一性,即整个系统中只会存在一个这样的实例
  • 以singleInstance模式启动的Activity具有独占性,即它会独自占用一个任务,被他开启的任何activity都会运行在其他任务中(官方文档上的描述为,singleInstance模式的Activity不允许其他Activity和它共存在一个任务中)
  • 被singleInstance模式的Activity开启的其他activity,能够开启一个新任务,但不一定开启新的任务,也可能在已有的一个任务中开启

PS:常规使用方式

<activity android:name=".MainActivity"
            android:launchMode="singleTask">

使用 Intent 标志

启动 Activity 时,您可以通过在传递给 startActivity() 的 Intent 中加入相应的标志,修改 Activity 与其任务的默认关联方式。可用于修改默认行为的标志包括:

FLAG_ACTIVITY_NEW_TASK
在新任务中启动 Activity。如果已为正在启动的 Activity 运行任务,则该任务会转到前台并恢复其最后状态,同时 Activity 会在 onNewIntent() 中收到新 Intent。
正如前文所述,这会产生与 “singleTask”launchMode 值相同的行为。

FLAG_ACTIVITY_SINGLE_TOP
如果正在启动的 Activity 是当前 Activity(位于返回栈的顶部),则 现有实例会接收对 onNewIntent() 的调用,而不是创建 Activity 的新实例。
正如前文所述,这会产生与 “singleTop”launchMode 值相同的行为。

FLAG_ACTIVITY_CLEAR_TOP
如果正在启动的 Activity 已在当前任务中运行,则会销毁当前任务顶部的所有 Activity,并通过 onNewIntent() 将此 Intent 传递给 Activity 已恢复的实例(现在位于顶部),而不是启动该 Activity 的新实例。
产生这种行为的 launchMode 属性没有值。

FLAG_ACTIVITY_CLEAR_TOP 通常与 FLAG_ACTIVITY_NEW_TASK 结合使用。一起使用时,通过这些标志,可以找到其他任务中的现有 Activity,并将其放入可从中响应 Intent 的位置。

注:如果指定 Activity 的启动模式为 “standard”,则该 Activity 也会从堆栈中移除,并在其位置启动一个新实例,以便处理传入的 Intent。 这是因为当启动模式为 “standard” 时,将始终为新 Intent 创建新实例。


实现用户界面

Activity 的用户界面是由层级式视图 — 衍生自 View 类的对象 — 提供的。每个视图都控制 Activity 窗口内的特定矩形空间,可对用户交互作出响应。 例如,视图可以是在用户触摸时启动某项操作的按钮。

您可以利用 Android 提供的许多现成视图设计和组织您的布局。“小部件”是提供按钮、文本字段、复选框或仅仅是一幅图像等屏幕视觉(交互式)元素的视图。 “布局”是衍生自 ViewGroup 的视图,为其子视图提供唯一布局模型,例如线性布局、网格布局或相对布局。 您还可以为 View 类和 ViewGroup 类创建子类(或使用其现有子类)来自行创建小部件和布局,然后将它们应用于您的 Activity 布局。

利用视图定义布局的最常见方法是借助保存在您的应用资源内的 XML 布局文件。这样一来,您就可以将用户界面的设计与定义 Activity 行为的源代码分开维护。 您可以通过 setContentView() 将布局设置为 Activity 的 UI,从而传递布局的资源 ID。不过,您也可以在 Activity 代码中创建新 View,并通过将新 View 插入 ViewGroup 来创建视图层次,然后通过将根 ViewGroup 传递到 setContentView() 来使用该布局。

PS:看中文翻译有时的确挺别扭的,不过无所谓反正这部分也不重要。


使用 Intent 过滤器(intent-filter)

<activity android:name=".ExampleActivity" android:icon="@drawable/app_icon">
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
</activity>

action 元素指定这是应用的“主”入口点。category 元素指定此 Activity 应列入系统的应用启动器内(以便用户启动该 Activity)。

如果您打算让应用成为独立应用,不允许其他应用激活其 Activity,则您不需要任何其他 Intent 过滤器。 正如前例所示,只应有一个 Activity 具有“主”操作和“launcher”类别。 您不想提供给其他应用的 Activity 不应有任何 Intent 过滤器,您可以利用显式 Intent 自行启动它们(下文对此做了阐述)。

不过,如果您想让 Activity 对衍生自其他应用(以及您的自有应用)的隐式 Intent 作出响应,则必须为 Activity 定义其他 Intent 过滤器。 对于您想要作出响应的每一个 Intent 类型,您都必须加入相应的intent-filter,其中包括一个 action 元素,还可选择性地包括一个 category 元素和/或一个 data元素。这些元素指定您的 Activity 可以响应的 Intent 类型。


保存 Activity 状态

系统会先调用 onSaveInstanceState(),然后再使 Activity 变得易于销毁。系统会向该方法传递一个 Bundle,您可以在其中使用 putString() 和 putInt() 等方法以名称-值对形式保存有关 Activity 状态的信息。然后,如果系统终止您的应用进程,并且用户返回您的 Activity,则系统会重建该 Activity,并将 Bundle 同时传递给 onCreate() 和 onRestoreInstanceState()。您可以使用上述任一方法从 Bundle 提取您保存的状态并恢复该 Activity 状态。如果没有状态信息需要恢复,则传递给您的 Bundle 是空值(如果是首次创建该 Activity,就会出现这种情况)。

不过,即使什么都不做,也不实现 onSaveInstanceState(),Activity 类的 onSaveInstanceState() 默认实现也会恢复部分 Activity 状态。具体地讲,默认实现会为布局中的每个 View 调用相应的 onSaveInstanceState() 方法,让每个视图都能提供有关自身的应保存信息。Android 框架中几乎每个小部件都会根据需要实现此方法,以便在重建 Activity 时自动保存和恢复对 UI 所做的任何可见更改。例如,EditText 小部件保存用户输入的任何文本,CheckBox 小部件保存复选框的选中或未选中状态。您只需为想要保存其状态的每个小部件提供一个唯一的 ID(通过 android:id 属性)。如果小部件没有 ID,则系统无法保存其状态。

PS:简单的实例Demo。
首先,如下几种情况下onSaveInstanceState方法会被回调:

  • 与HOME键有关操作时;
  • 按下电源按键(关闭屏幕显示)时;
  • 从Activity中启动一个新的Activity时;
  • 屏幕横竖屏方向切换时。

注意:用户主动销毁不会调用 ,比如当用户点击回退键 或者 调用了 finish() 方法, 不会调用该方法。

如果我们必须要有额外的瞬时数据需要保存的话。

    @Override
    protected void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);

        outState.putString("test", "需要恢复的系统默认不恢复的数据");

    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        if(savedInstanceState != null)
            String data=savedInstanceState.getString("test");
    }

Activity声明周期

这里写图片描述

  • onCreate()
    首次创建 Activity 时调用。 您应该在此方法中执行所有正常的静态设置 — 创建视图、将数据绑定到列表等等。 系统向此方法传递一个 Bundle 对象,其中包含 Activity 的上一状态,不过前提是捕获了该状态。
    始终后接 onStart()。

  • onRestart()
    在 Activity 已停止并即将再次启动前调用。
    始终后接 onStart()

  • onStart()
    在 Activity 即将对用户可见之前调用。
    如果 Activity 转入前台,则后接 onResume(),如果 Activity 转入隐藏状态,则后接 onStop()。

  • onResume()
    在 Activity 即将开始与用户进行交互之前调用。 此时,Activity 处于 Activity 堆栈的顶层,并具有用户输入焦点。
    始终后接 onPause()。

  • onPause()
    当系统即将开始继续另一个 Activity 时调用。 此方法通常用于确认对持久性数据的未保存更改、停止动画以及其他可能消耗 CPU 的内容,诸如此类。 它应该非常迅速地执行所需操作,因为它返回后,下一个 Activity 才能继续执行。
    如果 Activity 返回前台,则后接 onResume(),如果 Activity 转入对用户不可见状态,则后接 onStop()。但如果它在后台仍然可见,则不会停止。

  • onStop()
    在 Activity 对用户不再可见时调用。如果 Activity 被销毁,或另一个 Activity(一个现有 Activity 或新 Activity)继续执行并将其覆盖,就可能发生这种情况。
    如果 Activity 恢复与用户的交互,则后接 onRestart(),如果 Activity 被销毁,则后接 onDestroy()。

  • onDestroy()
    在 Activity 被销毁前调用。这是 Activity 将收到的最后调用。 当 Activity 结束(有人对 Activity 调用了 finish()),或系统为节省空间而暂时销毁该 Activity 实例时,可能会调用它。 您可以通过 isFinishing() 方法区分这两种情形。

PS: 测试1:横竖屏切换时生命周期如下:

//设置屏幕为横屏的俩种方式
<activity android:name=".save.SaveActivity"
            android:screenOrientation="landscape">

setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);

onPause -> onStop -> onDestroy -> onCreate -> onStart -> onResume

测试2:从AActivity切换至BActivity:
AActivity:onPause -> BActivity:onCreate -> BActivity:onStart ->BActivity:onResume
->AActivity:onStop

官方注释:
Activity A 的 onPause() 方法执行。
Activity B 的 onCreate()、onStart() 和 onResume() 方法依次执行。(Activity B 现在具有用户焦点。)
然后,如果 Activity A 在屏幕上不再可见,则其 onStop() 方法执行。

测试3:当前是BActivity是,按返回键:
BActivity:onPause -> AActivity:onRestart -> AActivity:onStart -> AActivity:onResume
->BActivity:onStop ->BActivity:onDestroy(BActivity被弹出返回栈)

测试4:如果按Home键:
AActivity:onPause -> AActivity:onStop(不发生意外情况下)

PS:横竖屏切换Activity,清单文件中不设置android:configChanges属性时,先销毁onPause()、onStop()、onDestroy()再重新创建onCreate()、onStart()、onResume()方法,设置orientation|screenSize(一定要同时出现)属性值时,不走生命周期方法,只会执行onConfigurationChanged()方法。


启动 Activity

PS:直接复制官网的例子。

Intent intent = new Intent(this, SignInActivity.class);
startActivity(intent);

如果您想允许用户发送电子邮件,可以创建以下 Intent:

Intent intent = new Intent(Intent.ACTION_SEND);
intent.putExtra(Intent.EXTRA_EMAIL, recipientArray);
startActivity(intent);

您可能希望用户选取其中一位联系人,以便您的 Activity 对该联系人中的信息执行某项操作。 您可以通过以下代码创建此类 Intent 并处理结果:

private void pickContact() {
    // Create an intent to "pick" a contact, as defined by the content provider URI
    Intent intent = new Intent(Intent.ACTION_PICK, Contacts.CONTENT_URI);
    startActivityForResult(intent, PICK_CONTACT_REQUEST);
}

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    // If the request went well (OK) and the request was PICK_CONTACT_REQUEST
    if (resultCode == Activity.RESULT_OK && requestCode == PICK_CONTACT_REQUEST) {
        // Perform a query to the contact's content provider for the contact's name
        Cursor cursor = getContentResolver().query(data.getData(),
        new String[] {Contacts.DISPLAY_NAME}, null, null, null);
        if (cursor.moveToFirst()) { // True if the cursor is not empty
            int columnIndex = cursor.getColumnIndex(Contacts.DISPLAY_NAME);
            String name = cursor.getString(columnIndex);
            // Do something with the selected contact's name...
        }
    }
}

尾声

这里仅仅是粘贴了官方文档的一些说明。后期会不断的加入自己的测试和理解。

最后希望各位看官可以star我的GitHub,三叩九拜,满地打滚求star:
https://github.com/zhiaixinyang/PersonalCollect
https://github.com/zhiaixinyang/MyFirstApp

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值