(连载) Android SDK 1.5 中文联机文档

大东哥 发布于 2009/08/18 00:30
阅读 6K+
收藏 3

刚开始学习android,在网上找关于android方面的教程,发现没什么合适的。找来找去,发现下的android下的文档才是最好,最权威的教程。翻译下来,一是为了加深理解,二可以为了以后查看方便,三可以练一下半桶水的四级英语水平。

Android基础

一 什么是android

     android是专为移动设备定制的包括操作系统,中间件,和核心应用程序的软件集。Android SDK提供了用Java语言开发android应用程序所需要的一些工具和API。

二 Feartures

    Application Frame 可被重用

    Dalvik virtual machine 特为移动设备优化的java虚拟机

    Integrated browser 基于开源的webkit引擎

    Optimized graphics 2d图形绘制库;3D 图形,基于OpenGL ES 1.0 (硬件加速)

    SQLite 用于数据存储

    Media support 支持常见的音频,视频,图形(MPEG4, H.264, MP3, AAC, AMR, JPG, PNG, GIF)

   GSM Telephony 需硬件支持

    Bluetooth, EDGE, 3G, and WiFi  需要硬件支持

   Camera, GPS, compass, and accelerometer (需要硬件支持)

   Rich development environment 包括设备虚拟器,调试工具,内存和外观设置,eclipse开发插件。

  Android 架构图

  Android System Architecture

 

应用程序

Android内置了一些核心的应用程序,包括emial客户端,短信应用,日历,地图,浏览器,联系人等等。所有这些应用程序都由Java语言写成。

应用程序框架

开发者们可以在android提供的APIS下进行任意的开发。应用程序一开始就是为了重用而设计的;任何应用程序的功能都可以发布给其他的应用程序使用(当然会受一些android为了安全所做的限制)。用户可以在同一设备下重写或替换组件。 

    一组丰富和可扩展的用来构建应用程序的views集合。包括lists,grids,text boxes,buttons,甚至可嵌入web浏览器。
    Content Provider 能够使应用程序操作数据(例如联系人),或者共享数据。
    A Rrsource Manager,提供访问静态资料,例如国际化资源,图片,布局文件。
    A Notification Manager 使应用程序显示通知。
    An Activity Manager 管理应用程序的生命周期和提供Actitivy调度。
       更多细节,请查看记事本教程。

 Android包含一些供Android系统各种组件使用的C/C++库。这些功能通过Android应用程序框架暴露给开发者。下面是一些核心库:

 

  • System C library - 基于标准C实现的BSD-derived实现库,嵌入式Linux-based设备载体
  • Media Libraries - 基于PacketVideops OpenCORE;支持回放,录制多种流行的视频,音频格式文件,支持静态图片文件,包括 MPEG4,H.264,MP3,AAC,AMR,JPG和PNG。
  • Surface Manager 管理多应用无缝集成2D和3D图形层。
  • LibWebCore-流行的浏览器引擎用于android浏览器和web浏览器
  • SGL - 底层的2d图形引擎
  • 基于OpenGL ES 1.0APIS的实现。使用硬件加速(硬件支持)或者包括高度优化的软件光栅。
  • FreeType - 位图和矢量字体渲染
  • SQLite - 所有applications可用的强大和轻量级的关系数据库引擎 

    Android基于Linux2.6版本提供系统服务例如安全,内存管理,进程管理,网络和驱动管理。内核也扮演着一个硬件与软件堆栈的抽象层角色。

    Android 运行环境

     

    Android 为Java语言提供了一组核心库,他提供大多数有用的功能。

    每一个Android应用程序运行在他的独立进程中,并拥有他的Dalvik虚拟机实例。Dalvik被设计成能在单个设备中有效运行多VMs实例。Dalvik在Dalvik Executable(.dex)中执行经过优化的,占用最小内存的footprint.VM注册并运行经java编译和通过内置的"dx"工具转化成.dex格式的类文件。

    Dalvik虚拟机依靠Linux内核提供基础功能,例如线程和底层级别的内存管理。

    Linux 内核

    Android基于Linux2.6版本提供系统服务例如安全,内存管理,进程管理,网络和驱动管理。内核也扮演着一个硬件与软件堆栈的抽象层角色。
  • 应用程序基础

  • Application Components

     Android应用程序使用Java语言开发。被编译的类——伴随着应用程序需要的数据和资源文件——被包含在Android包中的aapt工具捆绑,并且打包压成后缀名为.apk的压缩文件。.apk文件可以分发并在设备中安装。它可以被用户下载到设备中。所有包含在单个.apk文件中的代码可以被认为是一个应用程序。

    在许多方面,每一个Android应用程序活在她自己的世界中:

    • 默认情况下,每个应用程序运行在她独享的进程中。当任何应用程序执行时,Android会为他启动一个进程,关闭进程,当应用程序不需要被使用时,以释放资源供其他应用程序使用。
    • 每个进程拥有她的Java虚拟机实例,因此应用程序中的代码数据对其他应用程序是隔离的。
    • 默认情况下,每个应用程序会被分配一个唯一的Linux user ID.文件仅对此用户可见,仅仅是对应用程序本身——尽管有方法让它可以被其他应用程序访问。

    可以让两个应用程序共享一个user ID,这种情况下,它们可以访问各自的文件。多个应用程序可以共享同一个user ID,运行在同一个进程,共享同一个虚拟机实例中。

     

    Android的一个主要特点就是一个应用程序可以使用其他应用程序的元素(提供应用程序许可)。例如:如果你的应用程序需要显示一组滚动的图片,其它应用程序已经开发出了一个合适的图片滚动组件,并且已授权其他应用可用,你可以直接调用这个组件来工作,不必开发你自己的滚动组件。你的应用程序不是将它的代码拷贝,纳入或者链接它的代码到你的代码中。而是,当需要时,其他应用程序会启动所需的部分以供调用。

     

    为完成这工作,系统必须能够启动应用程序进程当它的任何部分被调用时,并且为这些部分实例化Java对象。因此,不像大多数其他的系统,Android应用程序没有单一的入口(例如 没有main()方法)。取代的是,它们有一些基本的组件,以供系统需要时实例化并且运行。有四类组件: 

  • Activities
        
          一个activity代表着一个虚拟的用户接口用户可以开展。例如:一个activity可能代表着一组用户可选择的菜单或者它可显示图片包括它们的标题。一个短信应用程序可能有一个activity用来显示一组联系人供发消息,另一个activity来写消息和选择联系人,其他的activities来显示旧消息或者改变设置。尽管它们协作在一起,它他们彼此之间是独立的。每一个activity做为一个Activity基类的实现类存在。
         一个应用程序可能只由一个activity组成,或者像文本消息应用程序,它可能包含多个activity.每个activity的作用,和需要定义多少个activity,当然取决于应用程序的实际设计。通常情况下,定义其中的一个activity作为应程序启动时提供用户的第一个activity。通过当前的activity来启动下一个activity。

    每个activity拥有一个默认的window窗口。通常,这个窗口会填充屏幕,但他可能比屏幕尺寸要小和悬浮在其他窗口的最顶层。一个activity也能够利用其它的窗口——例如,一个显示在activity中间的弹出窗口,提示用户,或者一个窗口代表用户的重要信息,当它们在屏幕上选择特别项时。

    不同等级的视图提供窗口的视觉效果——由基础的View衍生出来的对象。每一个view在窗口中控制其特别的形状。父views包括和组织它们的子类的布局。叶子views在它们的矩形中绘制,它们直接在它们的空间中控制响应用户事件。例如,一个view可能显示一张小图片并定义当用户点击图片时的事件。Android有一系列的开发好的views供您使用——包括按钮,文本框,滚动条,菜单项,单选框等等。

    View通过Activity.setContentView()显示。Content view是在View层次中的根View对象。(查看单独的User Interface文档获取更多细节)

加载中
0
大东哥
大东哥

Services

        服务没有可视用户接口,但在一定时间内,重复不断的运行于后台。例如,某服务可能在后台播放音乐,而用于在执行其他的操作,或者它通过网络抓取数据或者执行某些计算,将结果提供给activity。每一个服务继承于Service基类。

        一个典型的例子就是从播放列表中播放音乐。用户可能利用一个或多个activity选择歌曲播放。然而,音乐在后台播放而不被activity处理,因为用户希望他们退出播放器窗口进行其他操作时,音乐能持续的播放。为完成这个操作,音乐播放activity可以启动一个服务(service),运行于后台。系统将会负责音乐的持续播放。

        可以访问连接(绑定)正在运行的服务(或者当服务没启动时,可以启动服务)。当连接服务时,你可以与通过服务提供的接口跟它通信。像音乐服务,它可能提供一个接口,允许用户暂停,回放,停止,和重放。

         类似activities和其他的组件,服务运行于应用程序进程的主线程中。因为它们不会中断其它组件或者用户接口,它们常常(像音乐)。稍候请查看Processes and Threads.

Broadcast receivers 
        Broadcast receivers接收和响应通知。很多通知来源于系统——例如,发送时区变换的通知,电池电量不足,图片被采样,或者用户改变语言设置。应用程序也可以发出广播通知——如,通知其它应用程序,数据已经下载完毕,可供系统和应用使用。
        应用程序可以拥有任意数量的广播接收机来接收任何它认为重要的通知。所有接收机承继于BroadcastReceiver基类。
        Broadcast receivers不提供可视接口。但是,它可以启动actitivy响应接收到的通知,或者利用NotificationManager来通知用户。Notifications可以通过几种方式提示用户——闪动背光,振动设备,播放一段音乐等等。它们常常在状态栏中以图标显示,让用户获取通知。

Content providers

Content provider将应用程序数据组织成特定的集合供其它应用程序使用。数据可以是存储在文件系统中,或者在SQLite数据库中,或者其它任何用户可以操作数据的地方。content provider继承于ContentProvider基类,并且实现一组标准的方法,使应用程序可以检索和存储它控制的数据。然而,应用程序不是直接调用这些实现方法。而是通过ContentResolver对像调用方法。ContentResolver能够通知任何的content provider;它可以参与这些content provider进程间的管理合作。
查看Content Providers文档获取更多细节。
任何时候,当请求处理一个特别的组件时,Android这个应用程序的进程正在运行,如果有需要,启动,且组件的适当的进程实例将被创建。

Activating components: intents

当Content provider接收到ContentResolver的请求时,它将被激活。其它三种组件——activities,services,和broadcast receivers,被称为intent的异步消息对象激活。Intent是Intent类的对象,持有消息数据。例如activity和service,它指派它们被请求的行为和特别的URI标识的数据。如,intent为actitivy转达显示图片或让用户编辑文本的请求。对于broadercast receivers,intent对象指派它通知事件。例如,它可能指派broadcast receiver宣布它对摄像头按下的事件感兴趣。

以下是激活每类组件的个别方法:

  • activity通过Intent对象承载,用Context.startActivity()或者Actitivy.startActivityForResult()来启动新项(开展新活动)。被响应的activity通过调用getIntent()可以查看指派它启动的Intent对象。Android调用activity的onNewIntent()方法传给随后的intents。

      一个activity通常启动下一个activity。当希望获得下一activity启动的返回值时,可以调用startActivityForResult()方法取代startActivity()方法。例如启动一个能让用户选择图片的 activity ,可能希望获得选择的图片对象。返回值保存在一个Intent对象中,通过调用activity的onActivityResult()获得。

  • 启动一个服务(或者给一个正在运行的服务发新指示),通过一个Intent对象传递,调用Content.startService()方法。Android调用service的onStart()方法并且传给Intent对象。

  • 通常,一个intent通过Context.bindService()来建立调用组件和目标服务的连接。服务通过onBind()接收Intent对象。(如果服务未动行,bindService()方法会启动它)。例如,一个activity很容易与后台音乐服务建立连接,因此可以通过服务接口控制音乐服务。Activity利用bindService()方法建立连接,调用服务定义的方法。

       后面章节,Remote procedure calls,将描述更多绑定服务的细节。

  • 应用程序启动广播,可以通过Intent对象传递,如Context.sendBroadcast(),Context.sendOrderedBroadcast(),Context.sendStickyBroadcast()。Android调用onReceive()方法传递intent(意图)给所有感兴趣的接收者。

0
大东哥
大东哥

清单文件

在Android可以启动应用程序组件之前,它必须知道该组件的存在。因此,应用程序在清单文件中声明它们的组件,该文件包含在Android包中, .apk 文件还包含应用程序代码、文件和资源。

清单文件是结构化的XML文件,对于所有应用程序,文件名均为AndroidManifest.xml。它除了声明应用程序组件外,还做一些额外工作,比如指出应用程序需要链接的库(除了Android默认的库)、标明应用程序被授予的权限。

但是,清单文件的主要任务是报告Android应用程序的组成部分。例如,活动可以如下那样声明:

<?xml version="1.0" encoding="utf-8"?>
<manifest . . . >
<application . . . >
    <activity android:name="com.example.project.FreneticActivity"
              android:icon="@drawable/small_pic.png"
              android:label="@string/freneticLabel"
              . . .  >
    </activity>
    . . .
</application>
</manifest>

<activity>元素的 name属性命名实现了活动的 Activity子类。 iconlabel属性,指出代表活动、呈现给用户的包含图标和标签的资源文件。

其它组件也许以类似的方式声明— <service>元素用于服务, <receiver>元素用于广播接收者, <provider>元素用于内容提供者。活动、服务和内容提供者如果不在清单文件中声明的话,对系统是不可见的,因此永远不会运行。广播接收者可以在清单文件中声明,也可以通过代码(作为 BroadcastReceiver对象)动态生成,并通过调用 Context.registerReceiver()注册到系统中。

关于如何组织你的应用程序的清单文件,参见 AndroidManifest.xml 文件

意图过滤器

意图对象可以精确的指定目标组件名。如果指定了,Android将会找到该组件(基于清单文件中的声明),并激活它。但是,如果目标不是精确的名称, Android就必须定位到最适合的组件来响应意图。它将意图对象与意图过滤器的可能的目标作比较。组件的意图过滤器,可以通知Android关于该组件可处理的多种意图。象其它的组件的基本信息一样,他们也在清单文件中声明。这里是对前一个例子的扩展,为活动增加了两个意图过滤器:

<?xml version="1.0" encoding="utf-8"?>
<manifest . . . >
    <application . . . >
        <activity android:name="com.example.project.FreneticActivity"
                  android:icon="@drawable/small_pic.png"
                  android:label="@string/freneticLabel"
                  . . .  >
            <intent-filter . . . >
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
            <intent-filter . . . >
                <action android:name="com.example.project.BOUNCE" />
                <data android:type="image/jpeg" />
                <category android:name="android.intent.category.DEFAULT" />
            </intent-filter>
        </activity>
        . . .
    </application>
</manifest>

例子中的第一个过滤器 — 动作" android.intent.action.MAIN" 和分类" android.intent.category.LAUNCHER"的组合 — 这是一个普通的例子。它标记该活动可以显示在应用程序启动器(列出用户可以在设备上启动的应用程序的画面)中。也就是说,该活动是应用程序的入口点,当用户从启动器运行应用程序时看到的第一个活动。

第二个过滤器声明了一个动作,该活动可以处理特殊类型的数据。

组件可以有很多过滤器,每一个声明一种不同的能力。如果它没有任何过滤器,就只能通过提供了组件的精确名的意图来启动。

对于通过代码创建和注册的广播接收者,意图过滤器直接作为 IntentFilter 对象实例化。所有其它过滤器都通过清单文件设置。

关于意图过滤器的更多信息,参见文档 意图和意图过滤器

活动和任务

前面提到过,一个活动可以启动另一个活动,包括属于其它应用程序的活动。例如,你可能想让用户显示某个地方的地图。已经有一个活动可以完成它,那么你的应用程序要做的,只是传递包含必要信息的意图对象到 startActivity()中。地图查看器将会显示它。当用户按下回退键时,你的活动将再次出现在屏幕上。

对于用户来说,地图查看器就象是该应用程序的一部分,即使它定义于其它应用程序,运行于那个应用程序的进程。 Android通过将两个活动归入同一个任务task来维护用户体验。简单的说,任务就是用户认为的"应用程序"。它就是被安排在堆中的、有关联的一组活动。根活动是堆中用于启动任务的活动, — 一般来说,它是用户在应用程序启动器中选择的活动。堆顶部的活动是当前运行的活动 — 具有焦点,并对用户的动作作出反应。当启动另一个活动时,新活动被压入堆中;成为当前活动。前面的活动保留在堆中。当用户按下回退键时,当前活动弹出堆,前一个活动恢复为当前活动。

堆保存这些对象,如果一个任务包含两个以上的同一个活动子类的实例 —比如多个地图查看器— 每个实例在堆中有各自的入口。活动在堆中不会被重新排列,只是压入和弹出。

任务是活动的堆,不是一个类或清单文件的一个元素。因此,无法为任务中的某个独立的活动赋值。对于一个任务整体,值是赋给它的根活动的。比如下一节会谈到的"任务中的关系";值是通过设到任务的根活动中的关系取得的。

任务中的所有活动作为一个整体移动。整个任务(整个活动堆)可以被带到前台或送到后台。比如,当前任务的堆中有四个活动 —有三个在当前活动下面。用户按下HOME键、打开应用程序启动器、选择一个新的应用程序(实际上就是一个任务)。当前任务转为后台运行,新任务的根活动被显示出来。过了一会儿,用户返回了HOME,并选择了之前的应用程序(前一个任务)。堆中包含四个活动的任务转为前台。当用户按下回退键时,并不显示刚刚离开的活动(前一个任务的根活动)。而是堆顶部的活动被移除,显示堆中的前一个活动。

该行为是活动和任务的缺省行为。有办法修改这些行为。活动与任务结合、任务中活动的行为,由启动活动时设置到意图对象中的标志,和清单文件中活动对应的 <activity>元素的属性来控制。请求方和应答方对该行为都有发言权。

主要的意图标志如下:

FLAG_ACTIVITY_NEW_TASK
FLAG_ACTIVITY_CLEAR_TOP
FLAG_ACTIVITY_RESET_TASK_IF_NEEDED
FLAG_ACTIVITY_SINGLE_TOP

主要的 <activity>属性如下:

taskAffinity
launchMode
allowTaskReparenting
clearTaskOnLaunch
alwaysRetainTaskState
finishOnTaskLaunch

下一节介绍这些标志和属性的用处、如何交互和它们应该如何使用。

关系和新任务

默认情况下,应用程序中的所有活动之间都有关系— 因为它们有共同点,就是都属于同一个任务。但是可以使用<activity> 元素的taskAffinity属性来为活动设置单独的关系。属于不同应用程序的活动可以共享相同的关系,同一应用程序中的活动也可以拥有不同的关系。关系在两种情况下发挥作用:当意图对象包含FLAG_ACTIVITY_NEW_TASK标志来启动活动时、和活动的 allowTaskReparenting属性的值为"true"时。

FLAG_ACTIVITY_NEW_TASK标志
如前所述,新的活动默认运行于调用 startActivity()的活动的任务中。它被放入与调用者相同的堆中。如果传递给 startActivity()的意图对象包含 FLAG_ACTIVITY_NEW_TASK标志,系统将为该新活动寻找不同的任务。看到该标志就会想到它表示新任务。不过它有可能不是。如果有既存的任务与该新活动有相同的关系,该活动会被并入该任务。如果没有这样的任务,则开始一个新任务。
allowTaskReparenting属性
如果一个活动的 allowTaskReparenting属性被设成了" true",则它可以从启动它的任务移动到与它具有相同关系的前台任务中。例如,一个报告选定地点的天气状况的活动,属于某个旅行应用程序的一部分。它与同一应用程序的其它活动具有相同的关系(默认关系),并且它允许重置从属关系。你的一个活动启动了天气报告程序,那么它与你的活动属于同一个任务。当旅行应用程序接下来运行时,天气报告程序会被重新指定为在旅行应用程序的任务中显示信息。

如果一个.apk 文件包含两个以上的"应用程序"(以用户的角度),你应该为每个活动集合准备不同的关系。

运行模式

<activity> 元素的 launchMode属性可以接受四种运行模式:

standard"(缺省模式)
singleTop
singleTask
singleInstance

各模式间的差别有如下四点:

  • 对意图做出反应的活动在哪个任务中运行 对于"standard"和" singleTop"模式,它就是发出意图的任务(调用 startActivity()的任务) — 除非意图对象中包含 FLAG_ACTIVITY_NEW_TASK标志。这种情况下,象前一节 关系和新任务中讲的一样,会选择不同的任务。

    对应的"singleTask "和" singleInstance"模式标记该活动总是作为任务的根。它们定义的任务永远不会在其它任务中运行。

  • 是否可以运行活动的多个实例。"standard"或" singleTop "模式的活动可以实例化多次。它们可以属于多个任务,一个任务中也可以存在多个相同活动的实例。

    对应的"singleTask "和"singleInstance” 模式的活动被限制为只能拥有一个实例。当这样的活动是任务的根活动时,该限制意味着,在该设备上不会同时存在两个以上的相同任务的实例。

  • 实例是否允许其任务中存在其它活动。"singleInstance "模式的活动作为其任务中唯一的活动,如果它启动其它活动,那么被启动的活动会被分配到其它任务中运行,忽略其运行模式 — 就象在意图中指定了 FLAG_ACTIVITY_NEW_TASK标志一样。所有的其它方面,"singleInstance"模式与"singleTask"模式相同。

    另外三种模式允许一个任务中存在多个活动。" singleTask"活动总是作为任务的根活动,但它可以在自己的任务中运行其它活动。" standard"和" singleTop"的活动可以在堆的任何位置上运行。

  • 为了处理新意图是否运行类的新实例。对于默认的" standard "模式,响应每个新意图都会创建新的实例。每个实例只响应一个意图。对于" singleTop"模式,如果它位于目标任务的活动堆的顶层,既存的类的实例将会重用,来处理新的意图。如果它不在顶层,将不会被重用。而是创建一个新的实例,压入堆中,来处理新的意图。

    例如,假设一个任务的活动堆中包含根活动A、活动B、活动C、和顶层活动D,堆中的内容为A-B-C-D。在收到一个活动D的意图时。如果D是默认的"standard"运行模式,类的新实例将会运行,堆变成了A- B-C-D-D。可是,如果D的运行模式是" singleTop",既存实例会处理新意图(因为它在堆的顶部),堆保持为A-B-C-D。

    另一种情况,如果到达的是B的意图,无论B的模式是"standard"还是" singleTop ",一个B的新实例都会被创建 (因为B不在堆的顶部),堆变成了A-B-C-D-B。

    如上所述,对于" singleTask"或" singleInstance"的活动,不会出现多于一个实例的情况。所以,该实例会处理新的意图。" singleInstance"活动总是位于堆的顶层(因为它是任务的唯一活动),因此它总是会响应意图。在堆中,"singleTask "活动可能有也可能没有其它活动在它的上面。如果有的话,它将不能处理意图,意图将被丢掉。(虽然意图被丢掉了,但是该任务也会象正常处理意图一样被带到前台。)

当既存的活动被要求处理新意图时,意图对象通过调用 onNewIntent()传入活动。(被调用的活动可以通过调用 getIntent()取得。)

注意,当创建一个新实例来处理活动时,用户可以按回退键返回前一个状态(前一个活动)。但是,当活动的一个既存的实例来处理新意图时,用户不能通过按回退键返回收到新意图前的状态。

更多关于运行模式的信息,参见 <activity>元素。

堆的清除

如果用户离开任务时间较长,系统会清除任务中根活动以外的所有活动。当用户再次返回时,只能看到最初的活动。这样做的原因是,一段时间之后用户可能不关心之前做了什么,而是返回该任务做一些新的事情。

这是默认情况。有一些活动属性可以用于控制和修改这些行为:

alwaysRetainTaskState属性
如果任务的根活动的该属性被设为" true",上述默认行为不会发生。任务会保留堆中的所有活动,即使过了很长时间。
clearTaskOnLaunch属性
如果任务的根活动的该属性被设为" true",当用户离开任务再返回时,堆将清除到只剩任务的根活动。换句话说,它与 alwaysRetainTaskState完全相反。用户总是返回到任务的初始状态,即使离开一瞬间。
finishOnTaskLaunch属性
该属性与 clearTaskOnLaunch类似,但它的操作对象是单独的活动、而不是整个任务。并且它可以应用与任何活动,包括根活动。当它被设为"true "时,活动只保留当前状态。如果用户离开后再返回任务,它将不再存在。

有其它办法可以强制从堆中清除活动。如果意图对象包含 FLAG_ACTIVITY_CLEAR_TOP标志,并且目标任务中包含可以处理意图的活动的实例,则该实例上层的活动都会被清除,以使它处于堆的顶层,来响应意图。如果活动本身的运行模式是" standard",则它也会被从堆中清除,并运行新实例来处理收到的意图。这是因为运行模式为" standard "的活动总是创建新实例来响应新的意图。

FLAG_ACTIVITY_CLEAR_TOP经常与 FLAG_ACTIVITY_NEW_TASK一起使用。当同时使用时,意味着在其它任务中找到既存活动,并使其可以响应意图。

任务的启动

通过为活动指定一个动作为“ android.intent.action.MAIN”、分类为“ android.intent.category.LAUNCHER ”的意图过滤器,可以将活动设为任务的入口。(前面的意图过滤器一节中有这种过滤器的例子。)这种过滤器会在应用程序启动器中显示一个图标和标签,让用户可以启动一个任务或者返回已运行的任务。

第二个能力比较重要:用户必须能在离开任务后还可以回到该任务。正因为如此,会导致初始化新任务的两种运行模式,“singleTask” 和“singleInstance”,只有在活动具有 MAINLAUNCHER过滤器时才可以使用。想象一下,如果没有上述过滤器的话会是什么情况:意图启动了一个“singleTask”的活动,初始化了一个新任务,用户用户在该任务上操作了一段时间。之后用户按下了HOME键,这时该任务被挡在了主屏幕的后面。因为它没有出现在应用程序启动器中,因此用户无法返回该任务。

它与启动带时带有FLAG_ACTIVITY_NEW_TASK标志有些许不同。如果该标志导致启动了新的任务,并且用户按下了HOME键,离开了该任务。则必须有办法使用户通过某种途径回到该任务。一些活动(比如提示管理器)总是在其它的任务中启动活动,从不在自己的任务中启动。因此,其总是将 FLAG_ACTIVITY_NEW_TASK标志放入意图,传给startActivity()。如果你的活动可以由外部活动来调用,你可以使用该标志。注意,一定要保证用户可以返回其启动的任务。则必须有办法使用户通过某种途径回到该任务。

在不想让用户返回活动时,可以将<activity>的元素finishOnTaskLaunch 设为“true”。参见前面的堆的清除

进程和线程

当应用程序的第一个组件需要运行时,Android会为其启动一个包含一个线程的Linux进程。默认情况下,该应用程序的所有组件都会在该进程的该线程中运行。

然而,你可以使组件运行于其它进程,或者为任何进程启动一个线程。

进程

进程是由清单文件控制的,组件运行的地方。组件元素 — <activity><service><receiver><provider> — 都有一个process属性,用于指定组件在哪个进程中运行。这些属性可以设为每个组件有其独立的进程,也可以设成一些组件共享一个进程,其它的独占一个进程。也可以设置成,不同应用程序的组件在同一进程中运行 — 只要这些应用程序是由同一个作者签名并共享同一个Linux用户ID。 <application> 也有一个process属性用于设置应用到所有组件的默认值。

所有的组件都在指定的进程的主线程中运行,系统调用该线程中的组件。不同的线程不会创建不同的实例。因此,响应用户动作的方法比如View.onKeyDown() 以及后面的组件生命周期 中讨论的生命周期消息,总是只在进程的主线程中发生。这意味着,由系统调用的组件不应该长时间运行或阻断其它操作(比如网络操作、循环运算),因为这将阻碍进程中其它组件的运行。象下面的 线程中讨论的那样,你可以为耗时的操作分配单独的线程。

当更贴近用户的进程需要内存,并且内存不足时,Android可以在某一时点停止某个进程的执行。进程中的应用程序组件也被消毁。当它们再次运行时,进程重新启动这些组件。

当决定哪个进程被终止时,Android评估它们对用户的重要度。例如,与包含用户可见的活动的进程相比,包含用户不可见的活动的进程更容易被终止。因此,决定是否终止一个进程的执行,依赖于运行于该进程的组件的状态。这些状态作为组件生命周期一节的主题。

线程

虽然你可以限制你的应用程序只在一个进程中执行,也会有需要其它线程来做一些后台处理。因为用户界面应该总是能够快速响应用户的动作,因此运行活动的线程不能同时运行象网络下载这样的耗时的操作。任何不能立即完成的操作都应该在不用的线程中执行。

线程是在代码中,使用标准JavaThread 对象创建的。 Android提供了一些方便的工具来管理线程 —Looper 用于在线程中执行消息循环、 Handler 用于处理消息、HandlerThread 用于设置一个带有消息循环的线程。

远程过程调用

Android拥有轻量级的远程调用机制 (RPC) — 方法在本地调用,在远程执行(在其它进程中),结果返回给调用者。 这意味着将方法调用及其附带的数据分解为操作系统可以理解的形式,将其由本地进程和地址空间传送到远程进程和地址空间中,在远程重新装配并执行该调用。返回值沿着相反的方向传递。Android提供了实现该机制的所有代码,因此你只需要关注于如何定义和实现该RPC接口本身。

RPC接口只能包含方法。所有的方法都是同步执行的(本地方法被阻断,直到远程方法结束),即使没有返回值。

简而言之,该机制的流程如下:你由使用简单的IDL(接口定义语言)定义要实现的RPC接口。根据接口的定义, aidl 工具生成本地和远程进程必要的Java接口的定义。它包含下图所示的两个内部类:

内部类中包含管理你用IDL生成的远程过程调用需要的所有代码。两个内部类都实现了IBinder 接口。其中一个在本地由系统内部使用,写代码时可以忽略它。另一个叫做 Stub,扩展自Binder 类。作为对执行IPC调用的内部代码补充,它包含你在RPC接口中声明的方法。象图中说明的那样,你应该继承Stub来实现这些方法。

一般远程过程由服务来管理(因为服务可以通知系统关于进程和它连接的其它进程的信息)。它既有aidl。服务的客户端只有由aidl生成的接口文件。

接下来是服务和其客户端是如何建立连接的:

  • 服务的客户端(为位于本地)应该实现onServiceConnected()onServiceDisconnected() 方法,这样它们就可以在成功与远程服务建立或断开连接后收到消息。它们应该调用bindService() 来设置连接。
  • 服务的onBind() 方法应该被实现用作根据收到的意图(传入bindService()的意图),决定接受或拒绝连接。
  • 如果连接被接受,它返回一个Stub的子类。如果服务接受了连接,Android调用客户端的onServiceConnected() 方法并传入一个IBinder对象,由服务管理的Stub子类的代理。通过该代理,客户端可以调用远程服务。

上述简单的描述忽略了一些RPC机制的细节。更多信息参见用AIDL设计远程接口IBinder 类的描述。

线程安全方法

一些情况下,你实现的方法可能被一个以上的线程调用,因此它必须是线程安全的。

这对于可以远程调用的方法,这是完全正确的—比如前一节讨论的RPC机制。当从IBinder的同一进程调用IBinder中实现的方法时,该方法将在掉调用者的线程中执行。然而,当该调用来自另一个进程时,该方法执行的线程是从由Android维护与IBinder处于相同进程的进程池中选择的;它不会在进程的主线程中执行。例如,服务的进程的主线程中的onBind() 方法被调用,onBind()返回的对象(比如实现了RPC方法的Stub子类)中实现的方法会在池中的线程中被调用。因为服务可以有一个以上的客户端,所以同时可以有一个以上的线程在执行同一个IBinder方法。因此,IBinder的方法必须是线程安全的。

同样,内容提供者可以接受来自其它进程的数据请求。虽然ContentResolver和ContentProvider类隐藏了如何管理进程间通信的细节,但是相应这些请求的ContentProvider方法— query()insert()delete()update()getType() —是从内容提供者进程的线程池中被调用的。因为这些方法可以同时从多个线程中调用,所以它们也必须是线程安全的。

组件生命周期

应用程序组件具有生命周期—从Android生成它们用于响应一个意图开始,到实例被销毁为止。在这期间,它们可能有时处于激活状态,有时处于非激活状态,以及活动对用户是否可见。本节讨论活动、服务和广播接受器的生命周期—包括它们生存期间的状态、通知状态变化的方法、以及这些状态对实例的销毁和所在进程是否被中止的可能的影响。

活动的生命周期

活动有三种状态:

  • 激活状态或者运行状态,当它在屏幕的前台显示时(在当前活动堆的顶层)。就是具有用户操作焦点的活动。
  • 暂停状态,如果活动已经失去焦点、但是对用户依然可见。就是说另外的活动在它上面,但是它是透明的或者没有占满整个屏幕,通过它还可以看到暂停的活动。暂停的活动处于完全的生存状态(它维护着所有的状态和成员信息,保持着属于它的窗口管理器)。但是当系统内存极低时会被杀调。

  • 停止状态,如果它被其它活动完全遮挡。它仍然保持着所有的状态和成员信息。然而它对用户不可见,因此窗口被隐藏,当其它地方需要内存时,它将被系统杀掉。

如果活动处于暂停或停止状态,系统可以通知它结束运行(调用的它的 finish()方法),或者简单的杀掉进程。当它再次呈现给用户时,它必须重新启动并恢复到之前的状态。

当活动的状态发生变化时,系统通过调用如下保护方法来通知该变化:

void onCreate(Bundle savedInstanceState)
void onStart()
void onRestart()
void onResume()
void onPause()
void onStop()
void onDestroy()

这些方法都是钩子,你可以重写它们,在状态变化时做一些适当的工作。所有的活动必须实现 onCreate() ,当对象首次实例化时做一些初始设置。很多活动会实现onPause() ,用于提交数据和准备结束与用户的交互。

这七个方法定义了活动的整个生命周期。实现它们,你可以监视三层嵌套的循环:

  • 活动的整个生命周期从调用onCreate() 开始,直到最后调用onDestroy()为止。活动在onCreate()中做所有“全局“状态的设置,在onDestroy()中释放所有剩余的资源。例如,如果有一个线程在后台从网络上下载数据,它可能在onCreate()中创建该进程,在 onDestroy()中停止该进程。
  • 当调用onStart()方法时,对用户可见的activity的生命周期开始,直到onStop()方法被调用而结束。在这期间,用户可以在屏幕上看到这activity,尽管它可能不位于前台与用户交互。在这两个方法间,你可以操作activity需要显示的数据给用户。如,你可以在onStart()方法中注册一个BroadcastReceiver通知UI的变化情况,用户不需要显示时,在onStop()方法中卸载它。onStart()方法和onStop()方法可以被调用多次,activity可以选择对用户显示或隐藏。
  • activity生命周期通过onResume()方法恢复到前台,直至onPause()调用挂起至后台。这期间,这个activity会在屏幕上置顶于所有其他的activity与用户交互。一个activity可以在onResume()和onPause()方法中多次改变状态,如,当设备处于休眠或一个新的activity启动时,调用onPause(),当另一新的activity无效或新的intent传递时,调用onResume()方法。可见,这两个方法都是相当轻量级的。
  • 下面的图表举例说明了actitivy生命周期间的循环转换过程。椭圆框代表着activity所处的状态。矩形矩代表着,当actitivy状态转换时,你可以实现的回调方法,进行一些操作。

     

      下面的表格中的方法,显示了activity生命周期中的更多细节。

 MethodDescriptionKillable?Next
onCreate()

当activity第一次启动时调用。在这,你可以进行一些“全局”的初始工作--创建视图,给list绑定数据等等。

如果状态被保存了,该方法通过Bundle对象捕获activity之前的状态。(稍候请查看 Saving Actitivy State)。

它的下一个方法常常是onStart()。

No onStart()
     onRestart()

当activity停止时调用,仅仅指示它再次启动。仅随的下一方法通常是onStart()。

No onStart()
onStart()

当activity对用户可用时,调用。接下来调用onResume()方法,将activity显示到前台,或者onStop()方法

将activity隐藏。

No onResume()
or
onStop()
     onResume()

仅当activity与用户准备开始交互时。些时,这一activity位于activitiy的栈顶,让用户快速多次的使用。仅接着是通常是onPause()。

No onPause()
onPause()

调用,当系统准备恢复其他activity时。该方法通常用在提交未保存的持久化数据,停止动画和其他可能占用CPU的资源等等的情况。该方法内应该尽快的完成操作,因为下一个activity直到onPause()结束时,才会恢复。

使actitivy从后台恢复,通过onResume(),通过onStop()使它对用户不可见。

Yes onResume()
or
onStop()
onStop()

当activity对用户不可见时调用。可能会在actitivy被销毁或者其它actitivy恢复和覆盖时调用(无论是已存在或是新的activity)。接着,actitivy恢复与用户交互时,调用onRestart(),销毁时,调用onDestroy()。

Yes onRestart()
or
onDestroy()
onDestroy()

在actitivy销毁前调用。是actitivy对象销毁前可调用的最后一个方法。它会在activity即将完成时(有些人在它上面调用finish()方法),或者系统临时的销毁此实例存放于硬盘空间时调用。你可以通过isFinishing()方法来识别这两种方案。

Yes nothing
0
大东哥
大东哥

注意上面表格的Killable列。它表示在不执行其他actitivy's代码的情况下,系统是否可以在方法调用返回的任何时间杀死activity进程。三个方法(onPause(),onStop()和onDestroy())标识为"是"。因为onPause()是三个方法中的第一个,它是唯一一个在进程被杀死前保证被调用的方法——(onStop()和onDestroy())不行。因此,你应该在onPause()方法内执行持久化操作(如用户编辑数据)。

在Killable列中被标识为"No"的方法,保护寄存actitivy的进程,在方法调用期间不被杀死。

 稍候的摘要,Processes and lifecycle,将描述,一个actitivy在应用上定义为不可被杀死,仍然有可能被系统杀死——但它仅仅发生在极端的和没有其他资源可用的情况下。

 

Saving activity state 保存actitivy状态

当系统,而不是用户,在关闭一个actitivy时,用户可能希望返回和找到它的上一个状态。

为了在actitivy关闭之前捕获状态,你可以实现actitivy的onSaveInstanceState()方法。Android会在actitivy被销毁前——在onPause()被调用前,调用这个方法。它会传递一个你可以记录activity动态状态的Bundle对象,使用name-value键值对。当actitivy再次启动时,onCreate()和onStart()方法,都会通过Bundle调用onRestoreInstanceState(),因此两个方法都可以重新创建之前被捕获的状态。

不像之前讨论的onPause和其他的方法,onSaveInstanceState()和onRestoreInstanceState()不是生命周期方法。它们常常不会调用。如,在actitivy变成易被系统销毁的状态时,Android调用onSaveInstanceState(),但它不会在这个actitivy实例,实际上是被用户销毁(如按下BACK)的情况下调用。那种情况下,用户不希望返回activity,因为不必保存状态。

因为onSaveInstanceState()不常被调用,你应该仅仅用它来记录过渡的actitivy状态,不要用来记录持久化数据,而应该用onPause()替代。

 

 Coordinating activities

当一个actitivy启动其他activity时,它们同样的都会经历生命周期过渡。当一个暂停和停止时,另一个开始。一些场合下,你可能需要这些状态之间配合。

生命周期的顺序被良好的定义,特别是在两个actitivy在同一个进程当中时:

 1.   当前的actitivy的onPause()方法被调用。

 2.   接下来,正在启动的actitivy的onCreate(),onStart(),和onResume()方法会按顺序被调用。

 3.   然后,如果已启动的actitivy在屏幕上不可见时,它的onStop()方法被调用。

 void onCreate() 
     void onStart(Intent intent) 
     void onDestroy()

Service lifecycle 服务的生命周期

服务可以用两种方式调用:

  • 它启动和运行,直到某东西停止或自己停止时。这种模式下,它被Context.startService()启动,而被Context.stopService()停止。它可通过Service.stopSelf()或Service.stopSelfResult()停止自身。不管startService()被调用多少次,仅需一个stopService()方法来停止service。
  • 它可被提供给对外用户的接口所操作。客户端与服务对象建立连接并调用服务。连接被Context.bindService()方法建立,被Context.unbindService()关闭。多个客户端可以绑定同一服务端。如果服务没有准备好,bindService()可以启动该服务。

两种模式不是完全分开的。你可以绑定一个被startService()启动的服务。如,一个后台音乐服务可以被一个Intent对象定义音乐播放,通过调用startService()启动。随后,用户想实行一些控制或获取正在播放的音乐信息时,activity可通过bindService()建立连接。这种情况,stopService()不会停止服务,直到最后一个绑定的连接关闭时。

类似actitivy,服务也有一些生命周期方法,你可以实现它们改变状态。但它们比actitivy要少,仅仅三个方法,他们是public的,不是protected:
         

实现这些方法,你可以操作两种嵌套的服务生命周期循环:

  • 服务的完整的生命周期,在onCreate()被调用和onDestroy()返回间。类似actitivy,服务在onCreate()方法中进行全局初始化,在onDestroy()方法中释放所有系统资源。如,音乐播放服务可能在onCreate()方法中创建音乐播放线程,在onDestroy()方法中停止线程。
  • 服务的活动生命周期,在调用onStart()后。该方法被Intent对象示意调用startService()方法。音乐服务将分析这个Intent来确定播放什么音乐,并开始播放。

    当服务停止时,没有回调方法调用。--on stop()方法。

onCreate()和onDestroy()可被所有服务对象调用,不管是被Context.startService()还是Context.bindService()方法启动。然而,onStart()仅被那些通过startService()启动的服务调用。

如果服务允许外界绑定它,有三个附加的回调方法可供实现:

IBinder onBind(Intent intent) 
boolean onUnbind(Intent intent) 
void onRebind(Intent intent)

 

onBind()回调方法通过被授意调用bindService的Intent对象传递,而onUnbind()通过被授意调用unbindService()传递。如果服务允许绑定,onBind()方法返回客户端与服务联系统的通讯频道。

下面的图表说明了服务的生命周期方法。尽管被startService()启动的服务和bindService()启动的服务是分开的,但请注意,不管服务是怎么启动的,客户端都可以绑定它,因此任何服务可以接收到onBind()和onUnbind()请求。

 

 

 

Processes and lifecycles 进程和生命周期

 

Android系统试图尽可能的长久维护应用程序进程,但最终,当内存很低时,它需要移除老的进程。为了确定哪些进程维持,哪些进程要被杀,Android把每一个进程放入到一个"重要性层次"中,组件的运行和状态是基于这个"重要性层次"的。最低重要性的进程首先被淘汰,然后是那些次重要的,依此类推。该层次分五层。下面的列表显示重要性层次顺序:

1.  前台进程是当前正在操作的进程。拥有以下几个条件之一的进程,将被认为是前台进程:

进程正在运行一个activity,用户正在与它交互。(这个activity对象的onResume()方法已经被调用)
进程是某服务的寄主,用户正在交互的actitivy绑定了该服务
进程中拥有某个正在执行它的其中一个生命周期回调方法(onCreate(),onStart()或onDestroy())的服务。
进程中拥有某个正在执行onReceive()方法的广播接收器。
仅有一些前台服务会在任何给定的时间退出。当他们作为——如果内存空间不足以让他们继续运行时。通常,在那刻,设备已经到了内存分页状态,因此必须杀死一些前台进程保持用户接口能够响应。

 
2.      一个可视(显示)进程,是没有任何前台组件,但仍然能影响用户屏幕所见的进程。拥有以下两条件其一的进程被认为是可视进程:


(1)进程寄存着一个没有在前台,但引然对用户可见的actitivy(它的onPause()方法已经被调用)。它将发生在,例如,此activity是一个对话框,允许之前的activity在它下面显示。

(2)进程寄存着一个被可视actitivy绑定的服务。

可视进程被认为是非常的重要,除非杀死它可以保持所有的前台进程运行,否则,他不会被杀死。

3.      服务进程运行着某个已经被startService()方法启动的服务and that does not fall into either of the two higher categories. 尽管服务进程没有直接对用户可见,但它们通常执行着用户关心的任务(如后台播放中的mp3或者正在网络上下载数据),因此系统会保持它们的运行,除非没有足够的内存空间让他们与前台进程和可视进程一同运行时。

4.      后台进程是寄存着对当前对用户不可见的activity(onStop()已经被调用)的进程。这些进程没有直接影响用户体验,可能会在任何时间被暗杀来回收内存为前台,可视和服务进程使用。通常会有很多后台进程运行,它们被保持在LRU(最近最少用户使用)列表中,确保拥有最常被用户使用的activity在它们中,最后被暗杀。如果一个actitivy正确的实现了它的生命周期方法,并且捕获了它的当前状态,杀死该进程,将不会危害用户体验。

5.      闲置(空)进程是没有寄存任何activity应用程序组件的进程。保持这类进程的唯一原因是作为一个提高下次需要寄存在它们里面运行的组件的启动速度的缓存。系统常常会杀死这些进程,为了保持进程缓存与底层的内核缓存的平衡。

 

Android会基于寄存着当前activity的多个组件进程的重要性,尽量安排它的最高级别的组件。例如,如果某进程寄存一个服务和一个可视actitivy,该进程将被列为可视进程,而不是服务进程。

另外,一个进程的等级可能被提升,因为其他的进程基于他。一个正服务于其他进程的进程,级别永远不会比正被它服务的进程低。如,在进程A中的content provider正在服务于进程B中的一个客户端,或者如果进程A中的服务被进程B的组件绑定,In addition, a process's ranking may be increased because other processes are dependent on it. A process that is serving another process can never be ranked lower than the process it is serving. For example, if a content provider in process A is serving a client in process B, or if a service in process A is bound to a component in process B, process A will always be considered at least as important as process B.(译者注:这段话好像有点矛盾,不太敢翻译)

 

因为运行服务的线程比运行后台actitivies的进程级别要高,一个activity需要开始执行一个长时间操作时,应该启动一个服务,而不是通常的自己创建一个线程——特别是如果操作时间可能比activity的生命周期要长时。类似的例子如后台播放音乐和上传摄像头拍下的照片到网站上时。用一个服务来保证操作至少是在"服务进程"优先级别,不管actitivy发生什么。像在Broadcast receiver lifecycle摘要中的描述,同样的原因,广播接收者也应该雇用服务而不是将一个耗时的操作放到一个线程中处理。

Broadcast receiver lifecycle 广播接收器生命周期

广播接收器只有一个生命周期方法:

void onReceive(Context curContext, Intent broadcastMsg)

当广播消息到达接收者时,Android调用他的onReceive()方法,并给他传调保存着信息的Intent对象。仅当调用该方法时,广播接收者被认为是活动的。当onReceive()返回时,它就是不活动的。

 

拥有活动的广播接收者的进程,会被保护不被杀死。但是仅仅拥有不活动的广播组件时,当系统认为内存应该被其他进程使用时,可以在任何时间被系统杀死。

 

当与广播组件频繁交互时,会有一些问题,因此,某任务应当在独立的线程中做,与管理其他的用户接口组件运行的主线程分开。如果onReceive()产生线程然后返回,这个完整的进程,包括新产生的线程,被判断为不活动的(),会被提交为将被杀死的进程。解决这个问题的办法是为onReceive()启动一个服务,让服务做这工作,因此系统会认为在进程中仍然活动的工作在做。

下节有更多关于易被暗杀进程的描述。

0
爆皮王
爆皮王

东明,继续努力~~!!我来棒你的场啦。。我是阿勇!~!

0
曾赛
曾赛

好贴啊,这部分我看了好多遍都没有感觉,现在有些感觉了,呵呵

返回顶部
顶部