第2章第2节Android主要控件

内容摘要

本节开始讲解Android开发的技术细节,我们首先讲解界面展示部分,这主要是为了满足产品经理做原型的需要,我相信不管多么强大的产品设计软件,都没法把你的想法落实到手机上执行,并且能够控制界面的按钮和跳转逻辑。

但是如果掌握Android开发中的两大控件, 足以满足你初期的产品原型开发,把界面逻辑和原型制作成一个简易APP,展示效果会非常不一样。

Android控件--Activity

Android控件-- Service

Activity

首先来感性认识一下Activity。

这是我手机上《今日头条》的两个界面,这两个界面就是通过Activity展示的,在Activity里面你可以添加图片,图片的组件叫做ImageView,所有内容罗列在一个表单中,表单的组件叫做ListView。这些叫做View的组件可以互相嵌套布局,他们最后都展示在Activity的布局中。

这些组件名称都是Android环境下的名称,在IOS环境中也有相应的组件,只是名称有所不同,用法也稍有差异,比如列表在IOS被称作TableView,而用法上,Android利用XML的布局文件说明ListView的样式或者直接用代码实现ListView的样式,而IOS则用StoryBoard直接编辑布局或者用代码实现,

最新的Android Studio虽说也可以直接图形化编辑页面布局,但是现在的阶段还不够成熟,建议首先使用XML文件编辑布局,或许再过几个版本的迭代,Android Studio也可以像IOS的Xcode那样直接图形化编辑页面布局。

实际上这2个界面还用到了另一个比较有名的组件: Fragments。这个组件其实不是所有的Android版本都支持,这是在Android 3.0之后引入的, Fragments被用作多个组件的组织单元,是各种小组件的一个集合管理对象。

Fragments相当于轻量级的Activity,他内嵌在Activity的布局中,管理具有相同业务逻辑意义的组件合集。 Fragments与Activity有很多相似的地方。

我们在讲解完Activity之后会再讲解 Fragments,之所以没有单独把它单独列出来讲,是因为这2个控件几乎有相同的用法,我们只关注他们的不同点。

Tasks and Back Stack

Activity需要重点讲解的一个知识点是Tasks and Back Stack。所有的APP是由一个或者多个Activity构成的,当你在某个页面(Activity A)上点击某个按钮并跳转到下一个页面(Activity B)的时候,系统需要保持A的状态,这样当我们按回退按钮的时候,以便能够再次显示A的内容和数据。这个过程中系统要把A的信息和状态保持到一个数据结构中,这个结构就叫做Back Stack 而为了完成某个功能,Activity A和Activity B这样一个组合就叫做一个Task。

上图描述了一个Task过程中Back Stack管理Activity的。接下来我们举个例子:


当我们点击 ”今日头条”-->“我的”-->“商城”按钮的时候,会弹出一个新的界面,此时“我的”就是示意图中Activity1,而弹出的“商城”界面就是Activity2,当我再次点击“淘宝特卖”的时候,会弹出“特卖频道”界面,这个界面就是上图中的Activity3,当我按“回退” 按钮的时候,Activity3就会从Back Stack中弹出并且销毁,Activity2就会处于激活状态,同理再次按下“回退”按钮,Activity2就会销毁而Activity1就会处在或者月状态。

因此Task就是在打开一个app并且点击各个界面时候,系统为了管理这一些列操作和状态而创建的管理对象。而Back Stack是为了管理各个节目的数据结构。实际上,一般情况下一个Task就是一个linux进程,而Back Stack就是这个进程管理APP用的数据结构。

Activity Lifecycle

Activity需要重点讲解的第二知识点就是 Lifecycle。我们讲过Activity3在用户按下“回退”键的时候,会销毁并且Activity2处于激活状态。这个过程中Activity3是被系统释放了资源的。Android有一套资源管理方式,为了在有限的手机内存上展示尽可能丰富的内容,Android会为每一个正在展示的界面创建多个状态,并为不同的状态给予不同的资源权限。

首先我首先看一下Activity的状态图,图片来自官方网站,希望大家养成阅读原版官方文档的资料,本书大部分内容是在官方文档基础上,进行的总结和解读。

总的来看Activity流程稍微有点复杂,但是如果结合上面的例子,你会对这些状态有清晰的认识,首先是“Created”,当你点击“商城”按钮之后会弹出“商城”界面,在这个界面显示之前,你需要首先解析Activity的布局文件,以便知道界面上有哪些元素需要显示,以及他们的位置,我们例子中,一个列表需要显示,列表中有三项内容:“今日特卖”,“今日电影”,“淘宝特卖”。这个解析布局文件并设置显示内容的过程就是”Create“的过程,结束之后Activity就处于“Created”。而这个过程是在onCreated()函数中执行的。

创建结束之后就要显示这个界面了,完成显示之后Activity就处于“Started”状态了,在创建之后,显示之前,如果你还要做些什么事情,那就调用onStart()函数。

界面显示之后,你还有机会做一些事情,这个动作可以在onResume()中进行,执行完成这个函数之后,Activity就会处于“Resumed”状态了。

接下来2个状态是“Paused”和“Stopped”,这两个状态一般情况下是连续同时出现的,如果从当前界面点击了一个按钮,按钮的功能是弹出一个新的Activity,那么当前的Actvity就会首先处于“Paused”的状态,进入此状态前会首先调用onPause()方法,然后再调用onStop()方法,进入Stopped的状态,而新的Activity要重新走一遍Created到Resumed的流程。

在这个过程中,如果点击按钮之后不是显示一个新的Activity,而是弹出一个确认对话框,那么当前的Activity只会进入Paused的状态,而不会进入Stopped,并且只有onPause()方法会被执行到。因为此时当前的Activity只是部分可见,而不是完全被覆盖掉。这个地方是容易被开发者混淆的知识点。

当确认按钮被点击并重新返回当前Activity之后,Activity又重新进入了Resumed状态。此时onResume()函数会被执行到。

如果当前的Activity不是被新的Activity覆盖掉,而是用户点击“回退”按钮,此时的Activity就会从Back Stack中弹出,然后它使用的资源会被释放掉,进入“Destroyed”状态,再此之前会先后进入Paused和Stopped状态,在进入Stopped状态之后,Destroy之前,系统会调用onDestroy()方法,你可以在此方法中做一些存储数据的动作,以便下次再显示次界面时使用。

在IOS中,也有类似的状态变迁,只不过状态发生变迁时调用的方法名称不相同而已,比如下列四个方法:

- (void)viewWillAppear:(BOOL)animated;    --界面将要显示

- (void)viewDidAppear:(BOOL)animated;  --界面已经显示

- (void)viewWillDisappear:(BOOL)animated;--界面将要消失

(void)viewDidDisappear:(BOOL)animated;  --界面已经消失

以上四个函数只是状态变迁时,系统可以调用的函数的一部分,用户可以在界面状态将要或者已经发生变化时,做相应的逻辑处理。

不同点是Android在系统内存不够的情况下,会释放掉处于Stopped状态的Activity以腾出资源给当前正在显示的界面,而IOS则是先发送告警到用户的回调函数,由用户来处理这个问题。IOS处理内存告警的方法是:

-(void)didReceiveMemoryWarning;

Activity Layout

Layout 是需要重点说明的第三点。在Activity的onCreate()方法中,我们提到需要加载界面的布局文件,然后设置他们的属性。每个Activity都需要一个XML文件来描述起界面各个元素的位置和属性。这就是设计模式中的MVC。Activity就是MVC中的C即控制层,而XML描述文件就是V,即展示层,而M数据层我们会在后面的章节中讲解。在下一节的讲解中,我们会详细讲解布局文件。


Fragment

Fragment

Activity就讲解这些,需要额外说明的是与Activity非常类似的一个控件: Fragment。我只讲解一下 Fragment与Activity的不同之处,相同地方不再赘述。

首先要说明的是 Fragment与Activity的关系就像操作系统中线程与进程的区别,如果你了解这方面的知识,那么这两大控件的区别就非常容易理解了。如果你熟悉面向对象编程,那么Fragment与Activity的关系就像子类与父类之间的关系,这个比喻不是很恰当,但是可以暂时这么理解。

再就是状态Fragment要比Activity多,但是它的状态依赖于Activity的状态,也就是说不存在Fragment处于Resumed而它所属的Activity却处于Stopped的这情况。具体的状态和状体依赖关系见图。这里不再详细解释,相信如果你看懂Activity的状态变迁,就能很好的理解Fragment的状态图。

其次是Fragment的布局文件可以是Activity的一部分,即Activity的xml文件中,有一个控件叫做Fragment。Fragment设计之初就是为了将复杂的Activity文件中的控件,分组进行管理和展示,这个分组就是Fragment。

Fragment还有一些其它特殊属性,不过最重要的特点就是,Fragment是为了在同一个Activity中展示复杂的界面并且节省了Activity在start和destroy的时候所消耗的CPU资源。

Service

我们同样首先对Service有个感性的认识,见下图:

通过手机 设置->应用程序管理->运行中,你可以看到很多在后台运行进程和服务,点击其中一个,你会进入一个更详细的界面,注意“NotifyService”这个服务,如果没有猜错的话,他是用来发送“PUSH”消息的,就是当我们的APP进程被杀死之后,依然能够收到该APP的推送提醒,比如有新的消息或者有新的内容可以查看。点击微信的图标,试着停止里面的服务,当有朋友发送消息给你的时候,看看有什么不同。

关于Service,我们主要记住的就是它的作用,当我们的APP没有处于激活状态的时候,我们需要APP与我们的业务服务器进行一些交互功能,此时需要一个不同于Activity的控件来完成此功能,Service就是为此而设计的。

关于Service我们需要讲解几个要点。

Service有2种类型,Started和Bound。

这2种类型的的Service从创建到销毁以及用途都有所不同,对于Started类型的Service,如果要启动一个Service对象来执行特定的任务,需要通过 startService()调用,当任务执行完成之后,你需要用代码来明确的停止它,并且它启动之后就不在与启动它的控件(Activity)交互。

对于Bound类型的Service,你需要通过调用 bindService()来启动它,并且需要定义一个接口,来说明Service是如何与其他组件进行信息交互的。多个控件可以同时绑定同一个Service,如果一个Service没有控件绑定,类似指针引用计数为0,则系统会停止这个Service并释放其使用的资源。

Service是在当前APP的main thread中运行的。

Service是在当前绑定的它的进程的主线程中运行的。相信你的Android手机曾经提醒过你,某个APP长时间没有响应,请问是否关闭该APP。这个就是因为主线程太长时间没有响应用户的UI操作。因此如果Service执行太繁重的任务(一般情况下都会这样),那么我们必须单独创建一个线程来执行这个任务,而不要在主线程中执行这个繁重的任务。

有2个类可以用来实现Service控件:Service和IntentService

Service和IntentService是Android里面的两个Service控件的实现类,他们的不同是IntentService会自动创建一个新的线程,所有的业务逻辑只要在onHandleIntent()这个函数实现就可以了,这个函数自动在主线程之外的线程中执行,避免了上面所说的APP无响应的问题。同时系统会自动实现一个队列,所有对IntentService的多线程调用都是先把调用请求放入队列,然后依据队列顺序的调用onHandleIntent(),这就避免了多线程下调用IntentService可能产生的问题。

而Service类需要自己管理线程,不过它的优势就是你可以根据自己的需要,创建足够多得线程去处理每一个发送到Service请求,也就是说Service类有更大的灵活性。

Service也可以在前台运行,比如音乐播放器的Service

有些Service是可以在前台运行的,这些Service需要与用户进行频繁的交互,同时又需要与服务器后台进行交互, 因此可以把这类Service设置为前台Service,比较典型的就是播放音乐的Service即使在系统资源紧张的时候,也不会被系统回收资源。

Service的生命周期

Service的生命周期要比Activity的简单很多,只是需要注意的是Service运行时间越久,当系统在资源紧张的情况下,被系统杀死并回收资源的概率越大。

当Service被系统杀死并回收资源之后,系统会在资源充足的情况下试图恢复该Service,当然要看该Service在启动时候的配置,这个配置分为3类:

START_NOT_STICKY:不需要重新启动该Service。

START_STICKY:需要重建该Service,但是忽略之前通过Intent传入的参数。

START_REDELIVER_INTENT:需要重新启动该Service,并且重新输入之前传入的参数

这3种类型是Service在创建的时候,调用onStartCommand() 时的返回值。

本部分内容我们讲解了Service的几个重要属性,需要注意的是除此之外,Service的另一个重要的属性是,它可以被其它APP调用,我们可以在配置文件中配置一下改Service的属性,将该Service设置为仅为本APP可以调用。

本节主要从理论和经验方面,讲解了Android主要控件的主要特性,本书限于设计的知识面过于广泛,无法从所有的细节讲解所有的技术,希望大家通过本书的讲解,对于技术细节的学习有所有帮助,作为一个提纲挈领的工具书来使用,在下一节中我们将讲解本书工程中用到的技术的细节,同时我会把代码上传到代码库中,希望大家一边对照代码一边对照本书,进行仔细的学习。

欢迎关注公众号

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

推荐阅读更多精彩内容