Android阅读手札:第一行代码(第二章)

        这是《第一行代码》阅读手札的第二篇,也就是原书的《探究活动》章节。上一篇,主要讲述了Android的架构,开发工具,日志的使用等最基本的介绍,本章节主要介绍Activity的相关内容

关键字:Activity,Intent,Activity的生命周期,Activity的四种启动方式

         Activity作为四大组件中使用率最频繁的组件,其作用和重要性不言而喻,Activtity就是展示当前页面控件的容器,当然这是我个人的理解(更多人叫它活动)在AndroidStudio中(以下简称AS)在新建项目的时候,系统会要求我们创建一个Activity


Android阅读手札:第一行代码(第二章)
AS启动页

        AS为我们提供了多选择的activity(虽然用的最多的就是Empty Activity),创建完项目以后,我们先去src目录下,找到清单文件(也就是AndroidManifest.xml)看到xml文件,对于XML,我的第一印象就是标签语言可作为资源配置文件,可以使用Xmlpull解析器等其他解析去获取配置信息等等,

         那么,先看看Android清单文件里面,最基本的配置

Android阅读手札:第一行代码(第二章)
AndroidManifest.xml

application,也就是全局apk的配置,所有的四大组件均在里面注册(可以看到application标签是一个很大的闭包)首先分析application

allowBackup : 老实说,笔者原来没怎么注意到这个标签,今天查资料才发现我的天,这个标签不得了,标签定义:是否允许备份系统和用户数据的属性。(默认是true,也就是允许备份)也就是说明,应用程序数据可以在手机未获取 ROOT的情况下通过adb调试工具来备份和恢复,这就允许恶意攻击者在接触用户手机的情况下在短时间内启动手机 USB 调试功能,来窃取那些能够受到AllowBackup 漏洞影响的应用的数据(郑秋冬盗取数据?),造成用户隐私泄露甚至财产损失。具体的细节就不在深究,这里推荐一篇博客,allowbackup相关

icon : 也就是应用的图标,比如手机上的企鹅,12306的铁路标识等等

label : 指定标题栏的内容,需要注意的是,给主活动(程序入口的activity)设置指定的label不仅会成为标题栏中的内容,还会成为启动器中应用程序的名称

supportsRtl : 声明你的application是否愿意支持从右到左(原来RTL就是right-to-left 的缩写...)的布局。如果设置为true,targetSdkVersion设置为17或更高,各种RTL的API将被激活,系统使用您的应用程序可以显示RTL布局。如果targetSdkVersion设置为16或更低的设置为false,RTL的API将被忽略或没有影响您的应用程序将具有相同的行为无论对用户现场的选择相关的布局方向(你的布局会从左至右)。没事,可能你已经蒙圈了,继续奉献博客一篇了解supportsRtl

theme : 也就是主题,关于theme比较好的理解就是,假设我现在需要将一个Activity变成对话框,也就是类似于这种的效果,(当然你说我只用Dialog也可以实现需求,这里只是为了更好的理解和演示所以请各位看官轻喷)

Android阅读手札:第一行代码(第二章)
Activity设置为Dailog

那么,这个时候,就可以设置Activity的样式,也就是将其theme,改成我们自己定义的style。关于Application的基本标签内容就是这些,当然,我们自定义application,需要设置name标签等

接下来,就是activity这个标签的说明:

name:也就是这个Activity的名称,你自己定义的Activity的名称

intent filter :也称意图过滤器。试想这样一种场景,如果一个 Intent 请求在一片数据上执行一个动作, Android 如何知道哪个应用程序(和组件)能用来响应这个请求呢?Intent Filter的出现就是为了解决这个问题(本质是根据设置的条件去过滤),它是用来注册 Activity 、 Service 和 Broadcast Receiver,让其 具有能在某种数据上执行一个动作的能力。使用 Intent Filter ,应用程序组件就会告知 Android ,它们能为其它程序的组件的动作请求提供服务,包括同一个程序的组件、本地的或第三方的应用程序。

关于intent fiflter的拓展:

intent fiflter 的一条元素至少应该包含一个 action,否则任何Intent请求都不能和该intent fiflter匹配。如果Intent请求的Action中和某某一条匹配,那么该Intent就通过了这条intent fiflter的动作测试。如果Intent请求或intent fiflter中没有说明具体的Action类型,那么会出现下面两种情况。

(1) 如果intent fiflter中没有包含任何Action类型,那么无论什么Intent请求都无法和这条匹配;

(2) 反之,如果Intent请求中没有设定Action类型,那么只要intent-filter中包含有Action类型,这个Intent请求就将顺利地通过的行为测试。

因此,当意图过滤器设置为 action android:name="android.intent.action.MAIN",即声明,该注册的Activity,为当前应用的主入口点


Activity:

Activity作为四大组件中使用最频繁的组件(简单理解,就是界面),其作用不言而喻,首先,需要继承系统的Activity或者AppCompatActivity,既然是系统的类,那么肯定要实现(重写)一些系统的方法去私人订制,比如,设置具体的View,点击事件,跳转新的界面等等,

首先,既然是新创建Activity。

第一步,需要去清单文件去配置注册,AS默认在新创建Activity之后会去清单文件配置注册,但还是建议自己去检查一下是否正常注册,假设未在清单文件里面注册(也就是配置Activity的名字),如果从当前的Activity跳转到未注册界面会报如下异常:

Android阅读手札:第一行代码(第二章)
ActivityNotFoundException

其次,既然是不同的Activity,肯定需要展示不同的内容,那么,一般我们通过 调用这个方法setContentView(布局文件)去展示不同的View。

Intent:

Intent,是Android程序中各组件之间进行交互的一种重要方式,它不仅可以指明当前组件想要执行的动作,还可以在不同组件之前传递数据。Intent一般可以用于启动活动(也就是跳转界面),启动服务以及发送广播,因此,Intent在这里起着一个媒体中介的作用,专门提供组件互相调用的相关信息。

意图的属性包括:Action(动作),Category(附加信息),Data(数据,具体内容),Tpye(类型)等等,举个例子,说白了意图就是启动一个组件的的完整的动作信息。

就像玩游戏,玩就是Action动作,游戏就是Data内容,而Type就是类型,玩什么呢?玩梦幻,type就是指的类型,只有这些信息全了才能执行一个完整的意图,当然还有一些信息,比如scheme就是URI类型的数据的前缀,sms,还有host主机名,path路径等。

Intent分为以下两种:

显式意图:简单理解,就是简单明了显示的意图,也就是毫不顾忌的从当前Activity跳转到另外一个Activity,一般直接调用:

startActivity (new Intent (this ,LoginActivity.class) );

这是Intent的一个重载方法,参数说明:

参数一:当前上下文对象

参数二:也就是你要跳转到具体的Activity

既然有显式意图,那么与之对应,应该有隐式意图。


隐式意图,即Intent的发送者在构造Intent对象时,并不知道也不关心接收者是谁,这样有利于降低发送者和接收者之间的耦合,它一般用在没有明确指出目标组件名称的前提下,一般是用于在不同应用程序之间,如下:

Intent it = new Intent();

it.setAction("com.google.test");

startActivity(it);

由于隐式意图, 没有指明接收者, 只是给了一个action作为接收者的过滤条件。

对于显式Intent,Android不需要去做解析,因为目标组件已经很明确,Android需要解析的是那些隐式Intent,通过解析,将Intent映射给可以处理此Intent的Activity、IntentReceiver或Service。

当然,在隐式意图这种情况下,开启新的Activity需要在清单文件配置相对应的参数

界面跳转传递数据

跳转界面,并从当前Activity跳转到新的Activity,是很常见的。那么根据这种跳转界面并传递数据的情况,就分为以下两种情形,

A:从当前界面(姑且称为First)跳转到新界面(姑且称为Second),从First将数据传递给Second,Second来接受数据并使用,(这种场景是非常常见的)

B:从当前界面(依旧称为First)跳转到新界面(依旧称为Second),从First将数据传递给Second,Second来接受数据并使用,但是当Second界面关闭或者按下返回键的时候,需要将结果回传给First

先来讨论A情形,

首先,在First中传递数据的写法:

Intent intent = new Intent (FirstActivity.this,SecondActivity.class); //这里使用Bundle携带数据并传递

Bundle bundle = new Bundle(); 

bundle.putString("skip", "从FirstActivity传递数据到SecondActivity");  

参数一:需要跟新界面的Key完全一致

参数二:传递的数据

intent.putExtras(bundle);

startActivity(intent);


那么,在Second中,因为数据已经从First传递过来了,那么接受First的数据写法如下:

Bundle bundle = getIntent().getExtras(); 

String dataInfo= bundle.getString("skip");  //括号里面的Key需要和First传递过来的key符合一致

其中,这里的dataInfo,就是从first中获取到的数据


接下来,讨论B情形

首先,在First中,开启Second的方式需要变成如下API
startActivityForResult(Intent intent, int requestCode);

参数一:intent实例化对象

参数二:如果 requestCode > = 0,当Activity结束时 requestCode 将归还在onActivityResult()中。以便确定返回的数据是从哪个Activity中返回(简单理解,就是一个标识)


接着,在First中,调用onActivityResult(int requestCode, int resultCode, Intent data)

参数一:这个整数requestCode提供给onActivityResult,是以便确认返回的数据是从哪个Activity返回的。

这个requestCode和上面startActivityForResult()中的requestCode相对应,也就是需要匹配标识。

参数二:这整数resultCode是由Second通过其setResult()方法返回。

参数三:Intent实例化对象,这个data,就是返回的数据。


最后,在second中,(因为你的数据需要传到A,所以,必须通过Intent将数据传递出去)

调用:setResult(int resultCode, Intent data) ,此方法把Second想要返回的数据返回到first

参数一:当Activity结束时,resultCode将归还在onActivityResult()中,一般为RESULT_CANCELED , RESULT_OK。

参数二:一个Intent对象,返回给first的数据。(与步骤二相对应)

这样既可满足情形二的需求。

返回栈

Android是使用任务Tsk来管理活动(Activity)的,一个任务就是一组存放在栈里面的活动集合,这个栈也被称为返回栈。栈是一种后进先出的数据结构,默认情况下,每当我们启动了一个新的活动,它会在返回栈中入栈,并处于栈顶的位置。简单理解就是,当我们按下返回键或者手动finish方法去销毁活动时,处于栈顶的活动会出栈,这时,前一个入栈的活动,就会重新处于栈顶的位置。系统总是会显示处于栈顶的活动给用户。

活动状态

1:运行状态

当一个活动处于栈顶的时候,这时活动就处于栈顶运行状态。系统最不愿意回收的就是处于被运行状态的活动,(运行的你强制销毁,那就呵呵哒)

2:暂停状态

当一个活动不再处于栈顶位置,但依旧可见这时活动就进入了暂停状态,简单的理解就是,对话框形式的Activity只会占用屏幕中间的部分区域,你很快就会看到后面的Activity,处于暂停状态的活动仍然是完全存活着的,系统也不愿意去回收这种活动(因为他还是可见的,只是暂时无法使用,但它时刻准备着供用户使用);只有当内存极低的情况下,系统才会去考虑回收这种活动。

3:停止状态

当一个Activity不再处于栈顶位置,并且完全不可见的时候,就进入了停止状态。系统依旧会为这种活动保存相应的状态和成员变量,但是这并不是完全可靠的,当其他地方需要内存的时候,处于停止状态的活动有可能会被系统回收。

4:销毁状态

当一个Activity从返回栈中移除后就变成了销毁状态。系统优先会回收处于这种状态的活动,从而保证手机的内存,提高运行效率。

活动的生命周期:

关于Activity的生命周期,额,还是上图吧,虽然老司机们已经不想在看生命周期的东西了


Android阅读手札:第一行代码(第二章)
生命周期

关于生命周期,这里就不多说什么,

总之认准一个理,在onCreate里面做加载,初始化的事情;在onDestory里面做释放资源,解绑的事情。这样理解,准没错。

Activity的启动模式:

启动模式是指,可以根据实际开发需求为Activity设置对应的启动模式,从而可以避免创建大量重复的Activity等问题。

1)standard

standard为Activity的默认启动模式,可以不用写配置。在这个模式下,都会默认创建一个新的实例(处于栈顶)。因此,在这种模式下,可以有多个相同的实例,也允许多个相同Activity叠加。(点back键会依照栈顺序依次退出)

2)singleTop

singleTop模式下,Activity可以有多个实例,但是不允许多个相同Activity叠加。即,如果Activity在栈顶的时候,启动相同的Activity,不会创建新的实例,而会调用其onNewIntent方法。

3)singleTask

singleTask表示只有一个实例。在同一个应用程序中启动他的时候,若Activity不存在,则会在当前task创建一个新的实例,若存在,则会把task中在其之上的其它Activity destory掉并调用它的onNewIntent方法。如果是在别的应用程序中启动它,则会新建一个task,并在该task中启动这个Activity,singleTask允许别的Activity与其在一个task*存,也就是说,如果我在这个singleTask的实例中再打开新的Activity,这个新的Activity还是会在singleTask的实例的task中。

4)singleInstance

只有一个实例,并且这个实例独立运行在一个task中(会有一个单独的返回栈来管理这个活动),这个task只有这个实例,不允许有别的Activity存在。

总结:书中第二章的基本内容就是这些,

学而不思则罔,思而不学则殆。加油!

上一篇:Cookie、Session、Token那点事儿(原创)


下一篇:设计模式之死磕观察者模式(原创)