《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)里看到对系统任务管理功能的加强。

相关文章
|
4天前
|
Linux 编译器 Android开发
FFmpeg开发笔记(九)Linux交叉编译Android的x265库
在Linux环境下,本文指导如何交叉编译x265的so库以适应Android。首先,需安装cmake和下载android-ndk-r21e。接着,下载x265源码,修改crosscompile.cmake的编译器设置。配置x265源码,使用指定的NDK路径,并在配置界面修改相关选项。随后,修改编译规则,编译并安装x265,调整pc描述文件并更新PKG_CONFIG_PATH。最后,修改FFmpeg配置脚本启用x265支持,编译安装FFmpeg,将生成的so文件导入Android工程,调整gradle配置以确保顺利运行。
24 1
FFmpeg开发笔记(九)Linux交叉编译Android的x265库
|
1天前
|
数据库 Android开发 开发者
安卓应用开发:构建高效用户界面的策略
【4月更文挑战第24天】 在竞争激烈的移动应用市场中,一个流畅且响应迅速的用户界面(UI)是吸引和保留用户的关键。针对安卓平台,开发者面临着多样化的设备和系统版本,这增加了构建高效UI的复杂性。本文将深入分析安卓平台上构建高效用户界面的最佳实践,包括布局优化、资源管理和绘制性能的考量,旨在为开发者提供实用的技术指南,帮助他们创建更流畅的用户体验。
|
2天前
|
移动开发 Java Android开发
构建高效Android应用:采用Kotlin协程优化网络请求
【4月更文挑战第24天】 在移动开发领域,尤其是对于Android平台而言,网络请求是一个不可或缺的功能。然而,随着用户对应用响应速度和稳定性要求的不断提高,传统的异步处理方式如回调地狱和RxJava已逐渐显示出局限性。本文将探讨如何利用Kotlin协程来简化异步代码,提升网络请求的效率和可读性。我们将深入分析协程的原理,并通过一个实际案例展示如何在Android应用中集成和优化网络请求。
|
2天前
|
调度 Android开发 开发者
构建高效Android应用:探究Kotlin协程的优势与实践
【4月更文挑战第24天】随着移动开发技术的不断演进,提升应用性能和用户体验已成为开发者的核心任务。在Android平台上,Kotlin语言凭借其简洁性和功能性成为主流选择之一。特别是Kotlin的协程功能,它为异步编程提供了一种轻量级的解决方案,使得处理并发任务更加高效和简洁。本文将深入探讨Kotlin协程在Android开发中的应用,通过实际案例分析协程如何优化应用性能,以及如何在项目中实现协程。
|
3天前
|
存储 缓存 安全
Android系统 应用存储路径与权限
Android系统 应用存储路径与权限
6 0
Android系统 应用存储路径与权限
|
3天前
|
存储 安全 Android开发
Android系统 自定义系统和应用权限
Android系统 自定义系统和应用权限
18 0
|
6天前
|
Java Go PHP
开发语言漫谈-PHP
PHP即“Hypertext Preprocessor”
|
7天前
|
安全 编译器 PHP
PHP 8.1版本发布:引领Web开发新潮流
PHP编程语言一直是Web开发的主力军,而最新发布的PHP 8.1版本则为开发者们带来了更多创新和便利。本文将介绍PHP 8.1版本的主要特性,包括更快的性能、新的语言功能和增强的安全性,以及如何利用这些功能来提升Web应用程序的质量和效率。
|
8天前
|
缓存 移动开发 Android开发
构建高效Android应用:从优化用户体验到提升性能表现
【4月更文挑战第18天】 在移动开发的世界中,打造一个既快速又流畅的Android应用并非易事。本文深入探讨了如何通过一系列创新的技术策略来提升应用性能和用户体验。我们将从用户界面(UI)设计的简约性原则出发,探索响应式布局和Material Design的实践,再深入剖析后台任务处理、内存管理和电池寿命优化的技巧。此外,文中还将讨论最新的Android Jetpack组件如何帮助开发者更高效地构建高质量的应用。此内容不仅适合经验丰富的开发者深化理解,也适合初学者构建起对Android高效开发的基础认识。
7 0
|
8天前
|
移动开发 Android开发 开发者
构建高效Android应用:采用Kotlin进行内存优化的策略
【4月更文挑战第18天】 在移动开发领域,性能优化一直是开发者关注的焦点。特别是对于Android应用而言,由于设备和版本的多样性,确保应用流畅运行且占用资源少是一大挑战。本文将探讨使用Kotlin语言开发Android应用时,如何通过内存优化来提升应用性能。我们将从减少不必要的对象创建、合理使用数据结构、避免内存泄漏等方面入手,提供实用的代码示例和最佳实践,帮助开发者构建更加高效的Android应用。
12 0