《Android和PHP开发最佳实践》一2.3 Android应用框架

简介: 本节书摘来自华章出版社《Android和PHP开发最佳实践》一书中的第2章,第2.3节,作者 黄隽实,更多章节内容可以访问云栖社区“华章计算机”公众号查看

2.3 Android应用框架

前面介绍了Android的系统框架,主要目的是让大家对Android系统有整体的概念,也为日后更深入的学习打好基础。然而,目前我们更需要重点学习和掌握的则是Android的应用框架,因为是否能掌握和理解Android应用框架,直接关系到是否能学好Android应用开发。
Android的应用框架是一个庞大的体系,想要理解透彻并不是那么简单的事情,但是,好在其中有一些比较清晰的脉络可以帮助我们快速地熟悉这个系统,因此抓住这些脉络中的核心要点对于能否学好Android的应用开发来说是至关重要的。一般来说,Android应用框架中包含四个核心要点,即活动(Activity)、消息(Intent)、视图(View)和任务(Task)。
如果你觉得上述核心要点的概念很陌生,不好理解,那么我们来看看下面这个比喻:如果把一个Android应用比喻成海洋,那么每个Activity就是这个海洋中的岛屿,假设我们眼前有一项任务(也就是Task),需要我们在其中若干个岛屿上建立起自己的王国。于是问题来了,我们要怎么样从一座岛屿去到另一座岛屿呢?没错,我们需要交通工具,而Intent就是我们最重要的交通工具。当然,Intent不仅可以带我们去,而且还可以帮我们带上很多需要的东西。接着,到了岛上,我们开始建立一个自己的王国,要知道这可需要很多的资源,这个时候,我们就会想到View这个建筑公司,因为他可以帮助我们快速地建出我们需要的东西。这样,Activity、Intent、View以及Task一起配合完成了一个完整的Android应用的王国。
从以上的比喻中,我们还可以认识到,在这四个要点中,Activity是基础,Intent是关键,View是必要工具,而Task则是开发的脉络。对于开发者来说,只有掌握了Activity、Intent、View和Task这几个核心要素之后,才能够做出多种多样的应用程序。接下来,让我们分别学习一下这四个核心要点。

2.3.1 活动(Activity)

活动(Activity)是Android应用框架最基础、最核心的内容和元素,每个Android应用都是由一个或者若干个Activity构成的。在Android应用系统中,Activity的概念类似于界面,而Activity对象我们通常称之为“界面控制器”(从MVC的角度来说)。从另一个角度来理解,Activity的概念比较类似于网站(Web)开发中“网页”的概念。此外,当Android应用运行的时候,每个Activity都会有自己独立的生命周期,图2-2所示的就是Activity的生命周期。
image

其实,在Android系统内部有专门的Activity堆栈(Stack)空间,用于存储多个Activity的运行状态。一般来说,系统会保证某一时刻只有最顶端的那个Activity是处于前端的活动(foreground)状态。也正因如此,一个Activity才会有如图2-2所示的生命周期。当一个Activity启动并进入活动状态的时候,调用顺序是onCreate、onStart、onResume;退居后台的时候,调用顺序是onPause、onStop;重新回到活动状态的时候,调用顺序是onRestart、onStart、onResume;销毁的时候,调用顺序是onPause、onStop、onDestroy。我们应该深刻理解这些状态的变化过程,因为在Android应用开发的过程中我们会经常用到。至于如何更好地掌握Activity的特性,大家可以尝试将以下代码(代码清单2-1)放入Android应用中运行,并对照程序打印出的调试信息来理解Activity生命周期各阶段的使用。
代码清单 2-1

// 基础Activity类,用于测试
public class BasicActivity extends Activity {
    
    private String TAG = this.getClass().getSimpleName();
    
    public void onCreate(Bundle savedInstanceState) {
        Log.w(TAG, "TaskId:"+this.getTaskId());
    }

    public void onStart() {
        super.onStart();
        Log.w(TAG, "onStart");
    }

    public void onRestart() {
        super.onStart();
        Log.w(TAG, "onRestart");
    }
    
    public void onResume() {
        super.onResume();
        Log.w(TAG, "onResume");
    }
    
    public void onPause() {
        super.onPause();
        Log.w(TAG, "onPause");
    }
    
    public void onStop() {
        super.onStop();
        Log.w(TAG, "onStop");
    }
    
    public void onDestroy() {
        super.onDestroy();
        Log.w(TAG, "onDestroy");
    }
    
    public void onNewIntent() {
        Log.w(TAG, "onNewIntent");
    }
}

此外,所有的Activity必须在项目基础配置文件AndroidManifest.xml中声明,这样Activity才可以被Android应用框架所识别;如果你只写了Java代码而不进行声明的话,运行时就会抛出ActivityNotFoundException异常。关于Activity声明的具体操作,我们会在2.10.2节中结合Hello World项目进行详细介绍。

2.3.2 消息(Intent)

参考之前我们对Android应用框架的几个核心要点的比喻,我们应该知道Intent消息模块对于Android应用框架来说有多重要;如果没有它的话,Android应用的各个模块就像一座座“孤岛”,根本不可能构成一个完整的系统。在Android应用系统中,我们常常把Intent称为消息,实际上,Intent本身还是一个对象,里面包含的是构成消息的内容和属性,主要有如下几个属性,我们来分别认识一下。

  1. 组件名称(ComponentName)
    对于Android系统来说,组件名称实际上就是一个ComponentName对象,用于指定Intent对应的目标组件,Intent对象可以通过setComponent、setClass或者setClassName方法来进行设置。
  2. 动作(Action)
    消息基类(Intent)中定义了各种动作常量(字符串常量),其中比较常见的有:ACTION_MAIN(对应字符串android.intent.action.MAIN)表示应用的入口的初始化动作;ACTION_EDIT(对应字符串android.intent.action.EDIT)表示常见的编辑动作;ACTION_CALL(对应字符串android.intent.action.CALL)则表示用于初始化电话模块动作等。Intent对象常使用setAction方法来设置。
  3. 数据(Data)
    不同的动作对应不同的数据(Data)类型,比如ACTION_EDIT动作可能对应的是用于编辑文档的URI;而ACTION_CALL动作则应该包含类似于tel:xxx的URI。多数情况下,数据类型可以从URI的格式中获取,当然,Intent也支持使用setData、setType方法来指定数据的URI以及数据类型。

4.类别(Category)
既然不同的动作应该对应不同的数据类型,那么不同的动作也应该由不同的类别的Activity组件来处理,比如CATEGORY_BROWSABLE表示该Intent应该由浏览器组件来打开,CATEGORY_LAUNCHER表示此Intent由应用初始化Activity处理;而CATEGORY_PREFERENCE则表示处理该Intent的应该是系统配置界面。此外,消息对象(Intent)可以使用addCategory添加一种类型,而一个Intent对象也可以包含多种类型属性。

  1. 附加信息(Extras)
    一个Intent对象除了可以包含以上的重要信息之外,还可以存储一些自定义的额外附加信息,一般来说,这些信息是使用键值对(key value)的方式存储的。我们可以使用putExtra方法设置附加信息,信息类型非常丰富(一般还是以字符串为主);在接收的时候使用getExtras方法获取。
  2. 标志(Flags)
    除了上面提到的几个功能属性,消息基类中还定义了一系列特殊的消息行为属性(也就是标志),用于指示Android系统如何去启动Activity以及启动之后如何处理。关于标志(Flags)的使用我们还会在2.3.4节中介绍。

在Android应用中,消息(Intent)的使用方式通常有两种,一是显式消息(Explicit Intent),另一个则是隐式消息(Implicit Intent)。显式消息的使用比较简单,只需要在Intent中指定目标组件名称(也就是前面提到的ComponentName属性)即可,一般用于目标Activity比较明确的情形。比如在一个固定流程中,我们需要从一个Activity跳转到另一个,那么我们就会使用显式的消息。而隐式消息则比较复杂一点,它需要通过消息过滤器(IntentFilter)来处理,一般用于目的性不是那么明确的情形,比如应用中的某个功能需要往目的地发送消息,但是我们却不确定要使用短信发送还是微博发送,那么这个时候就应该使用隐性消息来处理了。下面是一个典型的消息过滤器的配置范例,如代码清单2-2所示。
代码清单 2-2

<activity...>
    <intent-filter>
        <action android:name="android.intent.action.SEND" />
        <category android:name="android.intent.category.DEFAULT" />
        <data android:scheme="content" android:mimeType="image/*" />
    </intent-filter>
</activity>

我们看到,配置消息过滤器使用的是标签,一般需要包含三个要素:action、category以及data。其中,action是必需的,category一般也是需要的,而data则允许没有设置。接下来,我们学习一下这几个要素的使用方法。
:在Android应用中,一般会通过元素来匹配消息(Intent),如果找到Action就表明匹配成功,否则就是还没找到目标。需要注意的是,如果消息过滤器没有指定元素,那么此消息只能被显式消息匹配上,不能匹配任何的隐式消息;相反,当消息没有指定目标组件名称时,可以匹配含有任何包含的消息过滤器,但不能匹配没有指定信息的消息过滤器。
:元素用于标注消息的类别。值得注意的是,假如我们使用元素来标识消息类别,系统在调用Context.startActivity方法或者Context.startActivityForResult方法时都会自动加上DEFAULT类别。因此,除了Intent已经指定为Intent.ACTION_MAIN以外,我们还必须指定为android.intent.category.DEFAULT,否则该消息将不会被匹配到。另外,对于Service和BroadcastReceiver,如果Intent中没有指定,那么在其消息过滤器中也不必指定。
< data/>:通过data字段来匹配消息相对来讲比较复杂,通常的data字段包含uri、scheme(content, file, http)和type(mimeType)几种字段。对于Intent来说,我们可以使用setData和setType方法来设置,对于IntentFilter来讲,则可以通过android:scheme和android:mimeType属性分别来指定,使用范例如代码清单2-3所示。
代码清单 2-3

<activity ...>
    <intent-filter>
        <action android:name="android.intent.action.SEND" />
        <category android:name="android.intent.category.DEFAULT" />
        <data android:scheme="file" android:mimeType="image/*" />
    </intent-filter>
</activity>

以上的配置表明该Activity可以发送图片,而且内容必须是单独的一个文件,也就是说,该文件的URI路径必须是以“file://”开头的。当然,如果我们把这里的“android:scheme”改成“content”的话,则表明该图片内容必须是由ContentProvider提供的,即URI必须是以“content://”开头的。
至此,我们已经介绍了消息(Intent)和消息过滤器(IntentFilter)的基本概念和用法。我们必须清楚的是,消息分为显式消息和隐式消息两种,而消息过滤器一般是提供给隐式消息使用的。Android消息过滤器的过滤规则比较严格,只要我们申明了除了默认值(DEFAULT)之外的action、category和data,那么,只有当对应消息对象的动作(action)、类别(category)和数据类型(data)同时符合消息过滤器的配置时才会被考虑。关于标签的具体使用方法,我们将会在本书7.2.4节中结合实例进行讲解。

2.3.3 视图(View)

视图(View)系统主管Android应用的界面外观显示,因此也称作Android UI系统,是Android应用框架中最重要的组成部分之一。我们在Activity中展示或者操作的几乎所有控件都属于View。Android应用框架的View System包含View和ViewGroup两类基础组件。下面我们来理解一下Android视图系统的层次结构,如图2-3所示。
image

视图类(View)是所有视图(UI)控件(包括ViewGroup)的基类。视图组(ViewGroup)则类似于集合,一个视图组可以包含多个ViewGroup和View,类似于Html标签中的层(div)。接下来,我们再来看看View中会经常使用的一些UI控件(见表2-3),你也可以在Android SDK参考文档(Reference)中的android.widget包下找到它们。
从表2-3中可以看出,Android应用框架为我们提供了非常丰富的视图控件,从某种程度上来说,Android应用的界面是通过各种各样的视图控件组合起来的。至于这些视图控件的具体用法,我们将在第7章中结合项目实例进行介绍。

image

本节只是从应用程序框架组成部分的角度简单地介绍了Android UI系统的概念,关于UI系统的更多知识以及UI控件的具体用法,我们将在本章2.7节中更系统地介绍。

2.3.4 任务(Task)

本节介绍Android任务(Task)的概念。区别于以上介绍的活动、消息和视图这几个要点,任务的概念显得比较抽象,且我们在日常编码过程中也不会直接接触到,但是,理解任务却是理解整个Android应用框架的关键。
首先,我们来认识一下Android系统中的任务是如何运行的。简单来说,当我们在手机的应用列表(Application Launcher)中点击某个应用图标的时候,一个新的Task就启动了,后面的操作可能会涉及多个应用中不同Activity的界面,而这些Activity的运行状态都会被存储到Task的Activity堆栈(Activity Stack)中去。和其他的堆栈一样,Activity堆栈采用的是“后进先出”的规则。图2-4展示就是一个常见任务中Activity堆栈的变化情况。
每次启动一个新的Activity,其都会被压入(push)到Activity堆栈的顶部,而每次按“BACK”键,当前的Activity就会被弹出(pop)Activity堆栈;另外,如果按了“HOME”键的话,该Task会失去焦点并被保存在内存中;而一旦重新启动,Task会自动读出并显示上次所在的Activity的界面。那么,从一个应用进入另一个应用的情况是怎样呢?比如,应用中需要配置一些系统设置,那么我们就需要考虑一下多任务切换的情况了,如图2-5所示。

image

image

我们假设Task A是应用A的任务,也是我们所在的任务,当运行到Activity 3的时候我们按了“Home”键,于是Task A中的所有Activity就都被停止了,同时Task A暂时退居到后台(Background);这时,我们点击应用B的图标激活了Task B,于是Task B就被推到了前台(Foreground),并展示出最上层的Activity Z;当然,我们还可以用类似的操作把Task A激活并放置到前台进行操作。以上也是我们使用Android系统最经常使用的行为操作,大家可以结合实际情况好好理解一下。
以上的策略已经可以满足大部分Android应用的需求。此外,Android还提供了一些其他的策略来满足一些特殊的需求。比较常见的,如我们可以在Android基础配置文件(Menifest File)中使用元素的launchMode属性来控制Activity在任务中的行为特征。launchMode有以下四种模式可供选择。
Standard模式:Standard模式为默认模式,无论是打开一个新的Activity,还是接收Intent消息,系统都会为这个Activity创建一个新的实例(instance);每个Activity都可以被实例化多次,并且每个任务都可以包含多个实例。此模式最常用,但是其缺点就是太耗费系统资源。
singleTop模式:该模式下的行为和Standard模式下的行为基本相同,如果该Activity正好在运行状态(也就是在Activity堆栈的顶部),那么其接收Intent消息就不需要重新创建实例,而是通过该类的onNewIntent()方法来处理接收到的消息。这种处理方式在一定程度上会减少一些资源浪费。
singleTask模式:此模式保证该Activity在任务中只会有一个实例,并且必须存在于该Task的根元素(即栈底)。此模式比较节省资源,手机浏览器使用的就是这种模式。
singleInstance模式:此模式与singleTask模式类似,不同之处是该模式保证Activity独占一个Task,其他的Activity都不能存在于该任务的Activity堆栈中。当然,Activity接收Intent消息也是通过onNewIntent方法实现。
此外,我们还可以通过设置Intent消息的flag标志来主动改变Activity的调用方式,比较常见的flag如下。
FLAG_ACTIVITY_NEW_TASK:在新的Task中启动目标Activity,表现行为和前面提到的singleTask模式下的行为一样。
FLAG_ACTIVITY_SINGLE_TOP:如果目标Activity正好位于堆栈的顶部,则系统不用新建Activity的实例并使用onNewIntent()方法来处理接收到的消息。表现行为和前面提到的singleTop模式下的行为一样。
FLAG_ACTIVITY_CLEAR_TOP:如果目标Activity的运行实例已经存在,使用此方法系统将会清除目标Activity所处的堆栈上面的所有Activity实例。
需要注意的是,官方文档中建议多使用默认的Task行为模式,因为该模式比较简单也易于调试。对于一些特殊的需求,如果需要使用到其他模式的话,需要模拟不同的情况多进行一些测试,以防止在一些特殊情况下出现不符合预期的情况。当然,说句实话,目前主流移动设备上的Android版本都还比较旧,对多任务管理的支持和体现还不够明显,不过,我们应该可以在Android最新版本(如Android 4.0)里看到对系统任务管理功能的加强。

相关文章
|
1月前
|
PHP 开发者 UED
PHP中的异常处理:理解与应用
在编程的世界中,错误和异常就像是不请自来的客人——总是在你最不希望它们出现的时候敲门。对于PHP开发者来说,学会优雅地处理这些“不速之客”是提升代码质量和用户体验的关键。本文将带你深入理解PHP中的异常处理机制,通过实际的代码示例,展示如何捕获、处理以及自定义异常,让你的应用程序更加健壮和灵活。准备好迎接挑战,让我们共同探索PHP异常处理的奥秘吧!
101 66
|
24天前
|
搜索推荐 前端开发 API
探索安卓开发中的自定义视图:打造个性化用户界面
在安卓应用开发的广阔天地中,自定义视图是一块神奇的画布,让开发者能够突破标准控件的限制,绘制出独一无二的用户界面。本文将带你走进自定义视图的世界,从基础概念到实战技巧,逐步揭示如何在安卓平台上创建和运用自定义视图来提升用户体验。无论你是初学者还是有一定经验的开发者,这篇文章都将为你打开新的视野,让你的应用在众多同质化产品中脱颖而出。
48 19
|
24天前
|
JSON Java API
探索安卓开发:打造你的首个天气应用
在这篇技术指南中,我们将一起潜入安卓开发的海洋,学习如何从零开始构建一个简单的天气应用。通过这个实践项目,你将掌握安卓开发的核心概念、界面设计、网络编程以及数据解析等技能。无论你是初学者还是有一定基础的开发者,这篇文章都将为你提供一个清晰的路线图和实用的代码示例,帮助你在安卓开发的道路上迈出坚实的一步。让我们一起开始这段旅程,打造属于你自己的第一个安卓应用吧!
52 14
|
27天前
|
Java Linux 数据库
探索安卓开发:打造你的第一款应用
在数字时代的浪潮中,每个人都有机会成为创意的实现者。本文将带你走进安卓开发的奇妙世界,通过浅显易懂的语言和实际代码示例,引导你从零开始构建自己的第一款安卓应用。无论你是编程新手还是希望拓展技术的开发者,这篇文章都将为你打开一扇门,让你的创意和技术一起飞扬。
|
25天前
|
XML 存储 Java
探索安卓开发之旅:从新手到专家
在数字时代,掌握安卓应用开发技能是进入IT行业的关键。本文将引导读者从零基础开始,逐步深入安卓开发的世界,通过实际案例和代码示例,展示如何构建自己的第一个安卓应用。我们将探讨基本概念、开发工具设置、用户界面设计、数据处理以及发布应用的全过程。无论你是编程新手还是有一定基础的开发者,这篇文章都将为你提供宝贵的知识和技能,帮助你在安卓开发的道路上迈出坚实的步伐。
32 5
|
24天前
|
开发框架 Android开发 iOS开发
安卓与iOS开发中的跨平台策略:一次编码,多平台部署
在移动应用开发的广阔天地中,安卓和iOS两大阵营各占一方。随着技术的发展,跨平台开发框架应运而生,它们承诺着“一次编码,到处运行”的便捷。本文将深入探讨跨平台开发的现状、挑战以及未来趋势,同时通过代码示例揭示跨平台工具的实际运用。
|
25天前
|
XML 搜索推荐 前端开发
安卓开发中的自定义视图:打造个性化UI组件
在安卓应用开发中,自定义视图是一种强大的工具,它允许开发者创造独一无二的用户界面元素,从而提升应用的外观和用户体验。本文将通过一个简单的自定义视图示例,引导你了解如何在安卓项目中实现自定义组件,并探讨其背后的技术原理。我们将从基础的View类讲起,逐步深入到绘图、事件处理以及性能优化等方面。无论你是初学者还是有经验的开发者,这篇文章都将为你提供有价值的见解和技巧。
|
24天前
|
PHP 开发者 UED
PHP中的异常处理机制深度剖析与最佳实践####
本文深入探讨了PHP中的异常处理机制,通过实例解析了try-catch结构、自定义异常类以及多级异常捕获的高级用法。不同于常规摘要的概览性质,本文的摘要直接指向核心——即如何在复杂的项目开发中有效利用PHP的异常处理功能来提升代码的稳定性和可维护性,为读者揭示了构建健壮PHP应用程序的关键策略。 ####
27 1
|
25天前
|
搜索推荐 前端开发 测试技术
打造个性化安卓应用:从设计到开发的全面指南
在这个数字时代,拥有一个定制的移动应用不仅是一种趋势,更是个人或企业品牌的重要延伸。本文将引导你通过一系列简单易懂的步骤,从构思你的应用理念开始,直至实现一个功能齐全的安卓应用。无论你是编程新手还是希望拓展技能的开发者,这篇文章都将为你提供必要的工具和知识,帮助你将创意转化为现实。
|
25天前
|
PHP 开发者 容器
PHP命名空间深度解析及其最佳实践####
本文深入探讨了PHP中引入命名空间的重要性与实用性,通过实例讲解了如何定义、使用及别名化命名空间,旨在帮助开发者有效避免代码冲突,提升项目的模块化与可维护性。同时,文章还涉及了PHP-FIG标准,引导读者遵循最佳实践,优化代码结构,促进团队协作效率。 ####
24 1