一、Activity简介
1. 什么是Activity?
首先来看看Google Android开发官网上对于Activity的定义:Activity 是一个应用组件,用户可与其提供的屏幕进行交互,以执行拨打电话、拍摄照片、发送电子邮件或查看地图等操作。 每个 Activity 都会获得一个用于绘制其用户界面的窗口。窗口通常会充满屏幕,但也可小于屏幕并浮动在其他窗口之上。
通俗的来说呢Activity是一个可以包含用户界面的组件,一个应用程序中可以包含零个或多个Activity(包含零个的应用,属于无法被看到的应用这种应用很少见),一般会指定其中一个Activity作为MainActivity,而每个Activity均可以启动另一个Activity。Activity是用户接口程序,会提供给用户一个交互式的接口功能,是Activity应用程序的基本功能单元。
2.如何创建Activity?
同样的,在开发官网上也做了比较详细的解释:要创建 Activity,您必须创建 Activity 的子类(或使用其现有子类)。您需要在子类中实现 Activity 在其生命周期的各种状态之间转变时(例如创建 Activity、停止 Activity、恢复 Activity 或销毁 Activity 时)系统调用的回调方法。 两个最重要的回调方法是:
onCreate()
您必须实现此方法。系统会在创建您的 Activity 时调用此方法。您应该在实现内初始化 Activity 的必需组件。 最重要的是,您必须在此方法内调用 setContentView(),以定义 Activity 用户界面的布局。
onPause()
系统将此方法作为用户离开 Activity 的第一个信号(但并不总是意味着 Activity 会被销毁)进行调用。 您通常应该在此方法内确认在当前用户会话结束后仍然有效的任何更改(因为用户可能不会返回)。
从上面的解释中可以看出三点信息:a.要创建Activity,首先需要创建Activity的子类(即创建类并extends Activity)。b.Activity在被创建时,首先调用的是onCreate()方法,当用户离开Activity时,调用的是onPause()方法[具体的这两个回调方法会在下一章的生命周期中详细讲解] 。c.Activity本身并没有界面用于显示,它只是创建了一个窗口[窗口通常充满屏幕,也可以小于屏幕而浮于其他界面之上],开发者在Activity中重要的回调方法onCreate()方法内调用setContentView()方法来显示用户界面。
而上文提到的setContentView()方法中需要的是.xml文件,所以我们对于Activity的界面布局就需要写到在此Activity中onCreate()中调用的setContentView()中参数一致的xml文件中,这样子在Activity被创建之后,才会将你想要显示的界面元素一一显示在用户界面中。而简单来说的xml是可扩展标记性语言而具体的情况可以去这里看看,里面介绍的很详细而且还有举例说明{http://blog.csdn.net/fengbingchun/article/details/38978591}
3.xml文件的几种加载方式
上文提到的我们可以在xml文件中对Activity显示的界面元素进行布局,那么如何将xml文件加载到Activity中就成为了最重要的问题。一般情况下我们可以通过如下几种方法来实现加载。
①setContentView(),这种加载一般在activity中的onCreat()方法中用于加载应用的布局内容显示和标题显示。也就是说它加载的俩个部分,一个是标题部分,一个是内容部分。
②LayoutInflater实例化的对象,通过inflate()方法可以获取布局文件,这个获取的布局文件和findViewById()的方式不同,inflate()方法是在res/layout中获取xml格式的文件,同时返回的是view对象,可以让我们动态添加布局文件。findViewById(,这种方式是在某一个布局中根据id获取一个控件,同样也可以获取一个布局控件。
③继承PreferenceActivity的类加载xml文件,使用方法addPreferencesFromResource(R.xml.preferences)来加载xml文件。
④布局中使用<include>标签,此方法是在一个xml文件中加载另一个xml中的布局,而将xml加载入Activity来显示的方法可以使用上文所说的任一种方法。
在我们将xml文件加载入Activity准备用于显示后,我们就需要进行最重要的这一步了,即在manifest中声明此Activity。
4.在manifest中声明Activity
Manifest属于AndroidManifest.xml文件,这个文件里面包含了你的应用的所有信息[http://my.oschina.net/weiCloudS/blog/367709 这个是关于AndroidManifest文件的详细介绍],所以创建的Activity还需要在AndroidManifest.xml文件中进行声明,这样才能保证你的应用在运行之前所有的信息被系统所了解以便于显示出你想要显示的内容。具体的声明方式如下:
<manifest ... >
<application ... >
<activity android:name=".ExampleActivity" />
...
</application ... >
...
</manifest >
就这样子一个Activity就已经被创建好了,在IDE中run起来之后就可以看到自己所定义的界面了。
5. AndroidManifest文件的详细介绍
每个应用的根目录中都必须包含一个 AndroidManifest.xml 文件(且文件名精确无误)。 清单文件为 Android 系统提供有关您的应用的基本信息,系统必须获得这些信息才能运行任意应用代码。 此外,清单文件还可执行以下操作:
• 为应用的 Java 软件包命名。软件包名称充当应用的唯一标识符
• 描述应用的各个组件,即:构成应用的 Activity、服务、广播接收器和内容提供程序。 为实现每个组件的类命名并发布其功能(例如,它们可以处理的 Intent 消息)。根据这些声明,Android 系统可以了解这组件具体是什么,以及在什么条件下可以启动它们
• 确定将托管应用组件的进程
• 声明应用必须具备哪些权限才能访问 API 中受保护的部分并与其他应用交互
• 还声明其他应用与该应用组件交互所需具备的权限
• 列出 Instrumentation 类,这些类可在应用运行期间提供分析和其他信息。这些声明只会在应用处在开发和测试阶段时出现在清单文件中;它们会在应用发布之前被删除
• 声明应用所需的最低 Android API 级别
• 列出应用必须链接到的库
一般情况下在AndroidManifest文件的结构如下[已经包含了所有的元素]:
<?xml version="1.0" encoding="utf-8"?>
<manifest>
<uses-permission />
<permission />
<permission-tree />
<permission-group />
<instrumentation />
<uses-sdk />
<uses-configuration />
<uses-feature />
<supports-screens />
<compatible-screens />
<supports-gl-texture />
<application>
<activity>
<intent-filter>
<action />
<category />
<data />
</intent-filter>
<meta-data />
</activity>
<activity-alias>
<intent-filter> . . . </intent-filter>
<meta-data />
</activity-alias>
<service>
<intent-filter> . . . </intent-filter>
<meta-data/>
</service>
<receiver>
<intent-filter> . . . </intent-filter>
<meta-data />
</receiver>
<provider>
<grant-uri-permission />
<meta-data />
<path-permission />
</provider>
<uses-library />
</application>
</manifest>
在AndroidManifest文件中只有<manifest>和<application>是必须的,并且只能出现一次,其余大部分元素可以出现多次或不出现。同一级别的元素出现的顺序并没有什么格式,但是<activity-alias>必须跟在别名所指的 <activity>之后。并且在编程过程中我们都可以借用以上的元素其中之一或一部分来实现我们所需要实现的功能和需求。关于详细的讲解还需要进行自己动手编程来体验和理解其工作的原理。在此我们只对其中一部分进行讲解。
①action:动作标签,存在intent-filter标签中,如果与一个activity绑定,可以表示该活动具有该动作,如果有一个意图发出来,并且带有这种一个Activity定义的动作,那么,它就有可能定位到该activity,一般与category一起用。eg:
②activity:活动标签,定义大量的属性来设置Activity的一些特性,譬如:android:configChanges、android:label、android:launchMode等,这些属性的详细的内容可以在官方文档中进行查看。
③activity-alias:活动标签替代者,一般用于对Activity来设置别名,其最重要的属性是android:targetActivity,通过这个属性来设置指向的Activity。
④application:应用程序声明,具有大量的属性,并且Activity、Service、Receiver均定义在此标签内部,并且还可以进行一种类似于全局的配置作用,譬如设定主题。
⑤intent-filter:意图过滤器,定义了Activity,Service或者广播可以接收到的Intent,必须包含action标签。
⑥meta-data:元数据标签,用于定义一些系统可以用到的数据。但是必须为元数据定义一个独一无二的名字。
⑦manifest:主配置标签,类似于网页的<html>标签一样。
6.如何启动Activity?
经过上面的两步我们就已经有一个拥有界面元素的Activity了,接下来研究的就是如何将Activity启动起来了。通常情况下我们启动MainActivity时只需要在IDE中将你的应用Run起来之后就可以在虚拟机或者设备上就可以看到了,但是一般来说一个应用总是由多个Activity松耦合来一起组成的,这就需要我们能够从MainActivity或者任意一个Activity去跳转到所需要的Activity去。一般在启动Activity时有三种不同的方式来启动一个Activity:
①显示启动:通俗来讲就是通过代码来直白的启动一个Activity。代码如下:
Intent intent = new Intent(this, SignInActivity.class);
startActivity(intent);
②隐式启动:与显示启动相对立,通俗地说就是不那么直接的启动一个Activity,而是通过在manifest文件中添加inten-filter,再代码中启动这个intent即可。代码如下:
manifest中的配置:
Activity中的启动:
③设置别名启动:这种启动方式其实主要是在Activity标签中设置activity-alias标签,并设置intent-filter,此时运行到模拟器或者真机上之后会生成两个入口图标,一个是原始Activity的入口,另一个是设置别名之后的入口。
7.如何结束Activity?
在启动Activity之后,我们不能一直将其开启,这样子会占用很大一部分的内存资源,所以就需要在恰当的时候讲开启的Activity结束掉。就可以通过调用 Activity 的 finish() 方法来结束该 Activity。您还可以通过调用 finishActivity() 结束您之前启动的另一个 Activity。{但是一般情况下不建议使用此方法直接结束掉Activity,应该在下文介绍的Activity的生命周期中对应的回调方法中进行}
8.Intent和IntentFilter简介
Intent负责对操作的动作、动作涉及数据、附加数据进行描述,Android则根据此Intent的描述,负责找到对应的组件,将 Intent传递给调用的组件,并完成组件的调用。因此,Intent起着一个媒体中介的作用,专门提供组件互相调用的相关信息,实现调用者与被调用者之间的解耦。在应用中,我们可以以两种形式来使用Intent:
直接Intent:指定了component属性的Intent(调用setComponent(ComponentName)或者setClass(Context, Class)来指定)。通过指定具体的组件类,通知应用启动对应的组件。
间接Intent:没有指定component属性的Intent。这些Intent需要包含足够的信息,这样系统才能根据这些信息,在在所有的可用组件中,确定满足此Intent的组件。
对于直接Intent,Android不需要去做解析,因为目标组件已经很明确。
Android需要解析的是那些间接Intent,通过解析,将 Intent映射给可以处理此Intent的Activity、IntentReceiver或Service。Intent解析机制主要是通过查找已注 册在AndroidManifest.xml中的所有IntentFilter及其中定义的Intent,最终找到匹配的Intent。
Intent Filter 描述了一个组件愿意接收什么样的 Intent 对象,Android 将其抽象为 android.content.IntentFilter 类。在 Android 的 AndroidManifest.xml 配置文件中可以通过 <intent-filter >节点为一个 Activity 指定其 Intent Filter,以便告诉系统该 Activity 可以响应什么类型的 Intent。
当使用 startActivity(intent) 来启动另外一个 Activity 时,如果直接指定 intent 对象的 Component 属性,那么 Activity Manager 将试图启动其 Component 属性指定的 Activity。否则 Android 将通过 Intent 的其它属性从安装在系统中的所有 Activity 中查找与之最匹配的一个启动,如果没有找到合适的 Activity,应用程序会得到一个系统抛出的异常。
9.如何判断是Activity还是控件?
当我们不确定时候,就要想办法去验证,这里主要是介绍验证的方法。
①使用hierarchyview工具,工具上会显示root的手机正在运行的activity或者控件
②使用grep “关键字” -inrs ./ 命令。查找界面上的关键字定位具体的类。
总之当我们确定显示的代码是什么,是控件还是Activity就很清楚了。
二、Activity生命周期
1.生命周期介绍
生命周期,从字面意思都能看来它对于Activity的影响是最多的,所以对于生命周期的学习是整个Activity的学习中最重要的部分。整体来说Activity的生命周期总共有7个,分别是:onCreate()、onStart()、onResume()、onPause()、onStop()、onDestroy()、onRestart()。具体的生命周期的流程见图2.1。可以看出来除了onRestart()之外其他的生命周期都是两两相对应的。所以在记忆和学习的时候就可以两两相互对应比对着学习。
2.onCreate()
在每个Activity中都会重写这个方法的,它会在Activity第一次被创建的时候调用,应该在此方法中执行所有正常的静态设置—创建视图、将数据绑定到列表等等。系统向此方法传递一个 Bundle 对象,其中包含 Activity 的上一状态,不过前提是捕获了该状态,始终后接 onStart()。
3.onStart()
在 Activity 即将对用户可见之前调用。如果 Activity 转入前台,则后接 onResume(),如果 Activity 转入隐藏状态,则后接 onStop()。
4.onResume()
在 Activity 即将开始与用户进行交互之前调用。 此时,Activity 处于 Activity 堆栈的顶层,并具有用户输入焦点。
始终后接 onPause()。
5.onPause()
当系统即将开始继续另一个 Activity 时调用。 此方法通常用于确认对持久性数据的未保存更改、停止动画以及其他可能消耗 CPU 的内容,诸如此类。 它应该非常迅速地执行所需操作,因为它返回后,下一个 Activity 才能继续执行。
如果 Activity 返回前台,则后接 onResume(),如果 Activity 转入对用户不可见状态,则后接 onStop()。
6.onStop()
Activity 对用户不再可见时调用。如果 Activity 被销毁,或另一个 Activity(一个现有 Activity 或新 Activity)继续执行并将其覆盖,就可能发生这种情况。如果 Activity 恢复与用户的交互,则后接 onRestart(),如果 Activity 被销毁,则后接 onDestroy()。
7.onDestroy()
在 Activity 被销毁前调用。这是 Activity 将收到的最后调用。 当 Activity 结束(有人调用 Activity 上的 finish()),或系统为节省空间而暂时销毁该 Activity 实例时,可能会调用它。 您可以通过 isFinishing() 方法区分这两种情形。
8.onRestart()
在 Activity 已停止并即将再次启动前调用。始终后接onStart()
9.Activity的三种生存期
在本章第一小节时已经说过Activity的生命周期基本是两两对应的,由此就可以将其分为三个阶段:A、完整生存期:即在onCreate()和onDestroy()之间经历的就是完整生存期,一般而言Activity会在onCreate()中完成所有的初始化,在onDestroy()中完成内存释放等操作。B、可见生存期:即在onStart()和onStop()之间经历就是可见生存期,在此期间内整个Activity对于用户是可见的,即便有时无法和用户进行交互。我们可以利用这一特性从而开发出更加节省空间的Activity,例如:在onStart()中加载资源,在onStop()中释放资源,就可以节省Activity在停止过程中不会占用过多的资源。C、前台生存期:即活动在onResume()和onPasue()之间经历的就是前台生存期,Activity总是处于活动状态,此期间的Activity总是和用户进行交互的,平时接触最多的也是这个状态期间的Activity。
10.Activity状态保存
通常情况下在Activity暂停或者停止时,Activity 的状态会得到保留。因为当 Activity 暂停或停止时,Activity 对象仍保留在内存中—有关其成员和当前状态的所有信息仍处于 Activity 状态。 因此,用户在 Activity 内所做的任何更改都会得到保留,这样一来,当 Activity 返回前台(当它“继续”)时,这些更改仍然存在。
但是,当Activity进入暂停或停止状态时,系统内存不足时会优先将此状态的Activity销毁掉,其Activity对象也被销毁,当用户返回Activity时,系统无法保存其完好状态返回此Activity,只能重新创建Activity对象,但是对于用户来说,他可能会认为此Activity无任何变化,在这种情况下就需要通过onSaveInstanceState()方法来保存有关于Activity的状态信息,确保有关 Activity 状态的重要信息得到保留。系统会先调用 onSaveInstanceState(),然后再使 Activity 变得易于销毁。系统会向该方法传递一个 Bundle,可以在其中使用 putString()和 putInt()等方法以名称-值对形式保存有关 Activity 状态的信息。然后,如果系统终止了应用进程,并且用户返回到了此Activity,则系统会重建该Activity,并将Bundle同时传递给onCreate()和 onRestoreInstanceState()。您可以使用上述任一方法从 Bundle 提取您保存的状态并恢复该 Activity 状态。如果没有状态信息需要恢复,则传递给您的 Bundle 是空值(如果是首次创建该 Activity,就会出现这种情况)。图2.2展示了当Activity不可见时,被kill掉重新返回和直接reStart Activity时所需要实现的方法。
图2.2:Activity中断、恢复、kill掉返回时流程
11.横竖屏切换时Activity生命周期的变换
学习生命周期的调用顺序和调用时机最好的方法是将七个回调方法都重写一遍在其中打上log输出在logcat中查看是最好的,所以就先给你们看看横竖屏切换时Activity生命周期log输出。
从中可以看出每次横竖屏切换时,都会重新调用一次完整的生命周期,相当于是重新创建了一次Activity。这样就会导致有时在Activity中的数据由于横竖屏切换的原因而导致数据的丢失问题,用户体验就会降低,但是从图中可以看出每次切换时,在Activity不可见之前总是会调用onSaveInstanceState()这个方法,并且在Activity重新可见之前会调用onRestoreInstanceState()方法,所以在横竖屏切换时可以用onSaveInstanceState()方法来保存一些Activity的状态信息、数据信息等。而用onRestoreInstanceState()方法来恢复Activity的状态信息、数据信息等,以提高用户体验。
也可以通过设置将Activity固定为横屏或竖屏,就不会出现横竖屏切换时生命周期的影响了。可以通过下列两种方式来设置:a. 在主配置清单中直接通过android:screenOrientation属性来设置。b.在onCreate()方法中通过setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);或setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);来设置。
如果必须要有横竖屏的切换时,也可以在manifest文件中的activity标签下加入android:configChanges="orientation|keyboardHidden|screenSize"属性来进行控制。在这个属性中orientation代表重力方向,keyboardHidden表示将输入键盘隐藏,screenSize表示适应整个屏幕。
注:在设置android:configChanges属性时,Google给我们提供了一共13个值来进行我们需要的设置。设置时,这13个值是可以一起进行设置的,只需要在中间用“|”进行隔开即可,如:android:configChanges="mcc|mnc|locale|touchscreen|keyboard|keyboardHidden|navigation|screenLayout|fontScale|uiMode|orientation|screenSize|smallestScreenSize"对于这13个值官网上具体的解释如下:
12.finish()的调用
在前文的介绍中已经提到了finish()这个方法。主要要作用是结束Activity。但是在Activity的生命周期中也有结束Activity的方法,本节主要是讲述在Activity的生命周期的个方法中调用finish方法之后,Activity生命周期的走向。
①onCreate():
log:
②onStart():
log:
③onResume:
log:
④onPause:
log:
⑤onStop:
log:
⑥onDestroy:
log:
⑦onRestart:
log:
⑧onRestoreIntanceState:
log:
⑨onSavedInstanceState:
log:
从整个的log中可以看出生命周期是两两相互对应的,例如在onCreate中执行finish,则只执行onDestroy;在onStart中执行finish,则只执行onStop和onDestroy。从这就可以看出有先必有后,执行了第一个生命周期的方法,必定会执行与之对应的生命周期方法。但是其中onSavedInstanceState和onRestoreIntanceState这两个方法并不是完全相对应的,因为这两个方法是用于保存Activity状态和恢复状态的方法,并且onRestoreIntanceState仅仅会在横竖屏切换时才会执行此方法,这一点需要注意。
onDestroy()方法的作用也是销毁Activity的作用,所以在onDestroy中调用finish方法的时候需要注意的是要首先判断下当前Activity是否已经被destroy掉,否则在某种条件下是会报错的。
三、Activity之间的通信、栈式管理
1.Activity之间传递消息
在第一章已经简单的介绍了startActivityForResult()方法来进行Activity之间消息的相互传递。但是有时候我们需要传递消息的Activity实例可能运行在不同的进程之中,因此在传递消息时,就需要通过Intent对象来进行消息的传递,Android 中通过 Intent 对象来表示一条消息,一个 Intent 对象不仅包含有这个消息的目的地,还可以包含消息的内容。对于这个Intent 对象来说目的地是必须的,而内容是可选的。关于Intent的详细介绍和使用已经在第一章中做过介绍了,此处就不再赘述。
2.Activity的栈式管理
四、Activity的启动模式以及用法
有时候在项目中我们需要根据特殊的需求来为Activity指定合适的启动模式。启动模式一共分为四种,分别是standard、singleTop、singleTask和singleInstance,可以在主配置清单文件中<activity>标签下设定android:launchMode来选择启动模式。默认情况下启动方式为standard。
1.standard
为默认的启动模式,简单来说就是在这种模式下每次启动一个Activity都会创建一个新的实例,入栈并处于栈顶。
2.SingleTop
当启动模式为singleTop时,每当启动Activity时,若发现栈顶已经是此Activity则直接使用此实例,若栈顶不是同一Activity则重新创建实例,并置入栈顶。
3.SingleTask
当启动模式为SingleTask时,每次启动Activity时系统首先会在栈中检查是否存在着该Activity的实例,若存在则直接使用此实例,并将此Activity之上的所有Activity出栈,若不存在,则创建新的实例。
4.SingleInstance
SingleInstance这种启动模式是最为复杂的模式,以SingleInstance模式启动Activity时,会启用一个新的栈来管理这个活动,这样的方式就会使得之外的任一程序来访问此Activity时,都会共用同一个栈来进行处理,也就能够实现共享Activity实例的功能。
注:在SingleInstance模式下实现共享的前提是所有的Activity都没有被销毁。
1. 什么是Activity?
首先来看看Google Android开发官网上对于Activity的定义:Activity 是一个应用组件,用户可与其提供的屏幕进行交互,以执行拨打电话、拍摄照片、发送电子邮件或查看地图等操作。 每个 Activity 都会获得一个用于绘制其用户界面的窗口。窗口通常会充满屏幕,但也可小于屏幕并浮动在其他窗口之上。
通俗的来说呢Activity是一个可以包含用户界面的组件,一个应用程序中可以包含零个或多个Activity(包含零个的应用,属于无法被看到的应用这种应用很少见),一般会指定其中一个Activity作为MainActivity,而每个Activity均可以启动另一个Activity。Activity是用户接口程序,会提供给用户一个交互式的接口功能,是Activity应用程序的基本功能单元。
2.如何创建Activity?
同样的,在开发官网上也做了比较详细的解释:要创建 Activity,您必须创建 Activity 的子类(或使用其现有子类)。您需要在子类中实现 Activity 在其生命周期的各种状态之间转变时(例如创建 Activity、停止 Activity、恢复 Activity 或销毁 Activity 时)系统调用的回调方法。 两个最重要的回调方法是:
onCreate()
您必须实现此方法。系统会在创建您的 Activity 时调用此方法。您应该在实现内初始化 Activity 的必需组件。 最重要的是,您必须在此方法内调用 setContentView(),以定义 Activity 用户界面的布局。
onPause()
系统将此方法作为用户离开 Activity 的第一个信号(但并不总是意味着 Activity 会被销毁)进行调用。 您通常应该在此方法内确认在当前用户会话结束后仍然有效的任何更改(因为用户可能不会返回)。
从上面的解释中可以看出三点信息:a.要创建Activity,首先需要创建Activity的子类(即创建类并extends Activity)。b.Activity在被创建时,首先调用的是onCreate()方法,当用户离开Activity时,调用的是onPause()方法[具体的这两个回调方法会在下一章的生命周期中详细讲解] 。c.Activity本身并没有界面用于显示,它只是创建了一个窗口[窗口通常充满屏幕,也可以小于屏幕而浮于其他界面之上],开发者在Activity中重要的回调方法onCreate()方法内调用setContentView()方法来显示用户界面。
而上文提到的setContentView()方法中需要的是.xml文件,所以我们对于Activity的界面布局就需要写到在此Activity中onCreate()中调用的setContentView()中参数一致的xml文件中,这样子在Activity被创建之后,才会将你想要显示的界面元素一一显示在用户界面中。而简单来说的xml是可扩展标记性语言而具体的情况可以去这里看看,里面介绍的很详细而且还有举例说明{http://blog.csdn.net/fengbingchun/article/details/38978591}
3.xml文件的几种加载方式
上文提到的我们可以在xml文件中对Activity显示的界面元素进行布局,那么如何将xml文件加载到Activity中就成为了最重要的问题。一般情况下我们可以通过如下几种方法来实现加载。
①setContentView(),这种加载一般在activity中的onCreat()方法中用于加载应用的布局内容显示和标题显示。也就是说它加载的俩个部分,一个是标题部分,一个是内容部分。
②LayoutInflater实例化的对象,通过inflate()方法可以获取布局文件,这个获取的布局文件和findViewById()的方式不同,inflate()方法是在res/layout中获取xml格式的文件,同时返回的是view对象,可以让我们动态添加布局文件。findViewById(,这种方式是在某一个布局中根据id获取一个控件,同样也可以获取一个布局控件。
③继承PreferenceActivity的类加载xml文件,使用方法addPreferencesFromResource(R.xml.preferences)来加载xml文件。
④布局中使用<include>标签,此方法是在一个xml文件中加载另一个xml中的布局,而将xml加载入Activity来显示的方法可以使用上文所说的任一种方法。
在我们将xml文件加载入Activity准备用于显示后,我们就需要进行最重要的这一步了,即在manifest中声明此Activity。
4.在manifest中声明Activity
Manifest属于AndroidManifest.xml文件,这个文件里面包含了你的应用的所有信息[http://my.oschina.net/weiCloudS/blog/367709 这个是关于AndroidManifest文件的详细介绍],所以创建的Activity还需要在AndroidManifest.xml文件中进行声明,这样才能保证你的应用在运行之前所有的信息被系统所了解以便于显示出你想要显示的内容。具体的声明方式如下:
<manifest ... >
<application ... >
<activity android:name=".ExampleActivity" />
...
</application ... >
...
</manifest >
就这样子一个Activity就已经被创建好了,在IDE中run起来之后就可以看到自己所定义的界面了。
5. AndroidManifest文件的详细介绍
每个应用的根目录中都必须包含一个 AndroidManifest.xml 文件(且文件名精确无误)。 清单文件为 Android 系统提供有关您的应用的基本信息,系统必须获得这些信息才能运行任意应用代码。 此外,清单文件还可执行以下操作:
• 为应用的 Java 软件包命名。软件包名称充当应用的唯一标识符
• 描述应用的各个组件,即:构成应用的 Activity、服务、广播接收器和内容提供程序。 为实现每个组件的类命名并发布其功能(例如,它们可以处理的 Intent 消息)。根据这些声明,Android 系统可以了解这组件具体是什么,以及在什么条件下可以启动它们
• 确定将托管应用组件的进程
• 声明应用必须具备哪些权限才能访问 API 中受保护的部分并与其他应用交互
• 还声明其他应用与该应用组件交互所需具备的权限
• 列出 Instrumentation 类,这些类可在应用运行期间提供分析和其他信息。这些声明只会在应用处在开发和测试阶段时出现在清单文件中;它们会在应用发布之前被删除
• 声明应用所需的最低 Android API 级别
• 列出应用必须链接到的库
一般情况下在AndroidManifest文件的结构如下[已经包含了所有的元素]:
<?xml version="1.0" encoding="utf-8"?>
<manifest>
<uses-permission />
<permission />
<permission-tree />
<permission-group />
<instrumentation />
<uses-sdk />
<uses-configuration />
<uses-feature />
<supports-screens />
<compatible-screens />
<supports-gl-texture />
<application>
<activity>
<intent-filter>
<action />
<category />
<data />
</intent-filter>
<meta-data />
</activity>
<activity-alias>
<intent-filter> . . . </intent-filter>
<meta-data />
</activity-alias>
<service>
<intent-filter> . . . </intent-filter>
<meta-data/>
</service>
<receiver>
<intent-filter> . . . </intent-filter>
<meta-data />
</receiver>
<provider>
<grant-uri-permission />
<meta-data />
<path-permission />
</provider>
<uses-library />
</application>
</manifest>
在AndroidManifest文件中只有<manifest>和<application>是必须的,并且只能出现一次,其余大部分元素可以出现多次或不出现。同一级别的元素出现的顺序并没有什么格式,但是<activity-alias>必须跟在别名所指的 <activity>之后。并且在编程过程中我们都可以借用以上的元素其中之一或一部分来实现我们所需要实现的功能和需求。关于详细的讲解还需要进行自己动手编程来体验和理解其工作的原理。在此我们只对其中一部分进行讲解。
①action:动作标签,存在intent-filter标签中,如果与一个activity绑定,可以表示该活动具有该动作,如果有一个意图发出来,并且带有这种一个Activity定义的动作,那么,它就有可能定位到该activity,一般与category一起用。eg:
②activity:活动标签,定义大量的属性来设置Activity的一些特性,譬如:android:configChanges、android:label、android:launchMode等,这些属性的详细的内容可以在官方文档中进行查看。
③activity-alias:活动标签替代者,一般用于对Activity来设置别名,其最重要的属性是android:targetActivity,通过这个属性来设置指向的Activity。
④application:应用程序声明,具有大量的属性,并且Activity、Service、Receiver均定义在此标签内部,并且还可以进行一种类似于全局的配置作用,譬如设定主题。
⑤intent-filter:意图过滤器,定义了Activity,Service或者广播可以接收到的Intent,必须包含action标签。
⑥meta-data:元数据标签,用于定义一些系统可以用到的数据。但是必须为元数据定义一个独一无二的名字。
⑦manifest:主配置标签,类似于网页的<html>标签一样。
6.如何启动Activity?
经过上面的两步我们就已经有一个拥有界面元素的Activity了,接下来研究的就是如何将Activity启动起来了。通常情况下我们启动MainActivity时只需要在IDE中将你的应用Run起来之后就可以在虚拟机或者设备上就可以看到了,但是一般来说一个应用总是由多个Activity松耦合来一起组成的,这就需要我们能够从MainActivity或者任意一个Activity去跳转到所需要的Activity去。一般在启动Activity时有三种不同的方式来启动一个Activity:
①显示启动:通俗来讲就是通过代码来直白的启动一个Activity。代码如下:
Intent intent = new Intent(this, SignInActivity.class);
startActivity(intent);
②隐式启动:与显示启动相对立,通俗地说就是不那么直接的启动一个Activity,而是通过在manifest文件中添加inten-filter,再代码中启动这个intent即可。代码如下:
manifest中的配置:
Activity中的启动:
③设置别名启动:这种启动方式其实主要是在Activity标签中设置activity-alias标签,并设置intent-filter,此时运行到模拟器或者真机上之后会生成两个入口图标,一个是原始Activity的入口,另一个是设置别名之后的入口。
7.如何结束Activity?
在启动Activity之后,我们不能一直将其开启,这样子会占用很大一部分的内存资源,所以就需要在恰当的时候讲开启的Activity结束掉。就可以通过调用 Activity 的 finish() 方法来结束该 Activity。您还可以通过调用 finishActivity() 结束您之前启动的另一个 Activity。{但是一般情况下不建议使用此方法直接结束掉Activity,应该在下文介绍的Activity的生命周期中对应的回调方法中进行}
8.Intent和IntentFilter简介
Intent负责对操作的动作、动作涉及数据、附加数据进行描述,Android则根据此Intent的描述,负责找到对应的组件,将 Intent传递给调用的组件,并完成组件的调用。因此,Intent起着一个媒体中介的作用,专门提供组件互相调用的相关信息,实现调用者与被调用者之间的解耦。在应用中,我们可以以两种形式来使用Intent:
直接Intent:指定了component属性的Intent(调用setComponent(ComponentName)或者setClass(Context, Class)来指定)。通过指定具体的组件类,通知应用启动对应的组件。
间接Intent:没有指定component属性的Intent。这些Intent需要包含足够的信息,这样系统才能根据这些信息,在在所有的可用组件中,确定满足此Intent的组件。
对于直接Intent,Android不需要去做解析,因为目标组件已经很明确。
Android需要解析的是那些间接Intent,通过解析,将 Intent映射给可以处理此Intent的Activity、IntentReceiver或Service。Intent解析机制主要是通过查找已注 册在AndroidManifest.xml中的所有IntentFilter及其中定义的Intent,最终找到匹配的Intent。
Intent Filter 描述了一个组件愿意接收什么样的 Intent 对象,Android 将其抽象为 android.content.IntentFilter 类。在 Android 的 AndroidManifest.xml 配置文件中可以通过 <intent-filter >节点为一个 Activity 指定其 Intent Filter,以便告诉系统该 Activity 可以响应什么类型的 Intent。
当使用 startActivity(intent) 来启动另外一个 Activity 时,如果直接指定 intent 对象的 Component 属性,那么 Activity Manager 将试图启动其 Component 属性指定的 Activity。否则 Android 将通过 Intent 的其它属性从安装在系统中的所有 Activity 中查找与之最匹配的一个启动,如果没有找到合适的 Activity,应用程序会得到一个系统抛出的异常。
9.如何判断是Activity还是控件?
当我们不确定时候,就要想办法去验证,这里主要是介绍验证的方法。
①使用hierarchyview工具,工具上会显示root的手机正在运行的activity或者控件
②使用grep “关键字” -inrs ./ 命令。查找界面上的关键字定位具体的类。
总之当我们确定显示的代码是什么,是控件还是Activity就很清楚了。
二、Activity生命周期
1.生命周期介绍
生命周期,从字面意思都能看来它对于Activity的影响是最多的,所以对于生命周期的学习是整个Activity的学习中最重要的部分。整体来说Activity的生命周期总共有7个,分别是:onCreate()、onStart()、onResume()、onPause()、onStop()、onDestroy()、onRestart()。具体的生命周期的流程见图2.1。可以看出来除了onRestart()之外其他的生命周期都是两两相对应的。所以在记忆和学习的时候就可以两两相互对应比对着学习。
2.onCreate()
在每个Activity中都会重写这个方法的,它会在Activity第一次被创建的时候调用,应该在此方法中执行所有正常的静态设置—创建视图、将数据绑定到列表等等。系统向此方法传递一个 Bundle 对象,其中包含 Activity 的上一状态,不过前提是捕获了该状态,始终后接 onStart()。
3.onStart()
在 Activity 即将对用户可见之前调用。如果 Activity 转入前台,则后接 onResume(),如果 Activity 转入隐藏状态,则后接 onStop()。
4.onResume()
在 Activity 即将开始与用户进行交互之前调用。 此时,Activity 处于 Activity 堆栈的顶层,并具有用户输入焦点。
始终后接 onPause()。
5.onPause()
当系统即将开始继续另一个 Activity 时调用。 此方法通常用于确认对持久性数据的未保存更改、停止动画以及其他可能消耗 CPU 的内容,诸如此类。 它应该非常迅速地执行所需操作,因为它返回后,下一个 Activity 才能继续执行。
如果 Activity 返回前台,则后接 onResume(),如果 Activity 转入对用户不可见状态,则后接 onStop()。
6.onStop()
Activity 对用户不再可见时调用。如果 Activity 被销毁,或另一个 Activity(一个现有 Activity 或新 Activity)继续执行并将其覆盖,就可能发生这种情况。如果 Activity 恢复与用户的交互,则后接 onRestart(),如果 Activity 被销毁,则后接 onDestroy()。
7.onDestroy()
在 Activity 被销毁前调用。这是 Activity 将收到的最后调用。 当 Activity 结束(有人调用 Activity 上的 finish()),或系统为节省空间而暂时销毁该 Activity 实例时,可能会调用它。 您可以通过 isFinishing() 方法区分这两种情形。
8.onRestart()
在 Activity 已停止并即将再次启动前调用。始终后接onStart()
9.Activity的三种生存期
在本章第一小节时已经说过Activity的生命周期基本是两两对应的,由此就可以将其分为三个阶段:A、完整生存期:即在onCreate()和onDestroy()之间经历的就是完整生存期,一般而言Activity会在onCreate()中完成所有的初始化,在onDestroy()中完成内存释放等操作。B、可见生存期:即在onStart()和onStop()之间经历就是可见生存期,在此期间内整个Activity对于用户是可见的,即便有时无法和用户进行交互。我们可以利用这一特性从而开发出更加节省空间的Activity,例如:在onStart()中加载资源,在onStop()中释放资源,就可以节省Activity在停止过程中不会占用过多的资源。C、前台生存期:即活动在onResume()和onPasue()之间经历的就是前台生存期,Activity总是处于活动状态,此期间的Activity总是和用户进行交互的,平时接触最多的也是这个状态期间的Activity。
10.Activity状态保存
通常情况下在Activity暂停或者停止时,Activity 的状态会得到保留。因为当 Activity 暂停或停止时,Activity 对象仍保留在内存中—有关其成员和当前状态的所有信息仍处于 Activity 状态。 因此,用户在 Activity 内所做的任何更改都会得到保留,这样一来,当 Activity 返回前台(当它“继续”)时,这些更改仍然存在。
但是,当Activity进入暂停或停止状态时,系统内存不足时会优先将此状态的Activity销毁掉,其Activity对象也被销毁,当用户返回Activity时,系统无法保存其完好状态返回此Activity,只能重新创建Activity对象,但是对于用户来说,他可能会认为此Activity无任何变化,在这种情况下就需要通过onSaveInstanceState()方法来保存有关于Activity的状态信息,确保有关 Activity 状态的重要信息得到保留。系统会先调用 onSaveInstanceState(),然后再使 Activity 变得易于销毁。系统会向该方法传递一个 Bundle,可以在其中使用 putString()和 putInt()等方法以名称-值对形式保存有关 Activity 状态的信息。然后,如果系统终止了应用进程,并且用户返回到了此Activity,则系统会重建该Activity,并将Bundle同时传递给onCreate()和 onRestoreInstanceState()。您可以使用上述任一方法从 Bundle 提取您保存的状态并恢复该 Activity 状态。如果没有状态信息需要恢复,则传递给您的 Bundle 是空值(如果是首次创建该 Activity,就会出现这种情况)。图2.2展示了当Activity不可见时,被kill掉重新返回和直接reStart Activity时所需要实现的方法。
图2.2:Activity中断、恢复、kill掉返回时流程
11.横竖屏切换时Activity生命周期的变换
学习生命周期的调用顺序和调用时机最好的方法是将七个回调方法都重写一遍在其中打上log输出在logcat中查看是最好的,所以就先给你们看看横竖屏切换时Activity生命周期log输出。
从中可以看出每次横竖屏切换时,都会重新调用一次完整的生命周期,相当于是重新创建了一次Activity。这样就会导致有时在Activity中的数据由于横竖屏切换的原因而导致数据的丢失问题,用户体验就会降低,但是从图中可以看出每次切换时,在Activity不可见之前总是会调用onSaveInstanceState()这个方法,并且在Activity重新可见之前会调用onRestoreInstanceState()方法,所以在横竖屏切换时可以用onSaveInstanceState()方法来保存一些Activity的状态信息、数据信息等。而用onRestoreInstanceState()方法来恢复Activity的状态信息、数据信息等,以提高用户体验。
也可以通过设置将Activity固定为横屏或竖屏,就不会出现横竖屏切换时生命周期的影响了。可以通过下列两种方式来设置:a. 在主配置清单中直接通过android:screenOrientation属性来设置。b.在onCreate()方法中通过setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);或setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);来设置。
如果必须要有横竖屏的切换时,也可以在manifest文件中的activity标签下加入android:configChanges="orientation|keyboardHidden|screenSize"属性来进行控制。在这个属性中orientation代表重力方向,keyboardHidden表示将输入键盘隐藏,screenSize表示适应整个屏幕。
注:在设置android:configChanges属性时,Google给我们提供了一共13个值来进行我们需要的设置。设置时,这13个值是可以一起进行设置的,只需要在中间用“|”进行隔开即可,如:android:configChanges="mcc|mnc|locale|touchscreen|keyboard|keyboardHidden|navigation|screenLayout|fontScale|uiMode|orientation|screenSize|smallestScreenSize"对于这13个值官网上具体的解释如下:
12.finish()的调用
在前文的介绍中已经提到了finish()这个方法。主要要作用是结束Activity。但是在Activity的生命周期中也有结束Activity的方法,本节主要是讲述在Activity的生命周期的个方法中调用finish方法之后,Activity生命周期的走向。
①onCreate():
log:
②onStart():
log:
③onResume:
log:
④onPause:
log:
⑤onStop:
log:
⑥onDestroy:
log:
⑦onRestart:
log:
⑧onRestoreIntanceState:
log:
⑨onSavedInstanceState:
log:
从整个的log中可以看出生命周期是两两相互对应的,例如在onCreate中执行finish,则只执行onDestroy;在onStart中执行finish,则只执行onStop和onDestroy。从这就可以看出有先必有后,执行了第一个生命周期的方法,必定会执行与之对应的生命周期方法。但是其中onSavedInstanceState和onRestoreIntanceState这两个方法并不是完全相对应的,因为这两个方法是用于保存Activity状态和恢复状态的方法,并且onRestoreIntanceState仅仅会在横竖屏切换时才会执行此方法,这一点需要注意。
onDestroy()方法的作用也是销毁Activity的作用,所以在onDestroy中调用finish方法的时候需要注意的是要首先判断下当前Activity是否已经被destroy掉,否则在某种条件下是会报错的。
三、Activity之间的通信、栈式管理
1.Activity之间传递消息
在第一章已经简单的介绍了startActivityForResult()方法来进行Activity之间消息的相互传递。但是有时候我们需要传递消息的Activity实例可能运行在不同的进程之中,因此在传递消息时,就需要通过Intent对象来进行消息的传递,Android 中通过 Intent 对象来表示一条消息,一个 Intent 对象不仅包含有这个消息的目的地,还可以包含消息的内容。对于这个Intent 对象来说目的地是必须的,而内容是可选的。关于Intent的详细介绍和使用已经在第一章中做过介绍了,此处就不再赘述。
2.Activity的栈式管理
在上文中,我已经提到过了栈这个东西。简单来说呢,栈是一种先进后出的数据结构[关于栈的详细介绍可以去搜索下“数据结构中栈的介绍”去了解下]。在一开始的时候我就已经说过一个应用可以有多个Activity组成,而在Android中,就是通过栈来管理众多的Activity的。也就是说,在某一时刻,只能有一个Activity位于栈顶,即只有一个Activity能够和用户进行交互,只有当这个Activity被暂停或者销毁后,下面的Activity才有可能浮到栈顶,或者有一个新的Activity被创建出来,则旧的Activity就被压栈沉下去了。Activity是Android程序的表现层。程序的每一个显示屏幕就是一个Activity。正在运行的Activity处在栈的最顶 端,它是运行状态的。
从此图中也可以看出Activity在非活动状态时是极易被kill掉的,上图的颜色表示一种被kill掉的可能性的大小,越在栈底的越容易被kill掉,但是具体的情况和设备的内存状况有关。四、Activity的启动模式以及用法
有时候在项目中我们需要根据特殊的需求来为Activity指定合适的启动模式。启动模式一共分为四种,分别是standard、singleTop、singleTask和singleInstance,可以在主配置清单文件中<activity>标签下设定android:launchMode来选择启动模式。默认情况下启动方式为standard。
1.standard
为默认的启动模式,简单来说就是在这种模式下每次启动一个Activity都会创建一个新的实例,入栈并处于栈顶。
2.SingleTop
当启动模式为singleTop时,每当启动Activity时,若发现栈顶已经是此Activity则直接使用此实例,若栈顶不是同一Activity则重新创建实例,并置入栈顶。
3.SingleTask
当启动模式为SingleTask时,每次启动Activity时系统首先会在栈中检查是否存在着该Activity的实例,若存在则直接使用此实例,并将此Activity之上的所有Activity出栈,若不存在,则创建新的实例。
4.SingleInstance
SingleInstance这种启动模式是最为复杂的模式,以SingleInstance模式启动Activity时,会启用一个新的栈来管理这个活动,这样的方式就会使得之外的任一程序来访问此Activity时,都会共用同一个栈来进行处理,也就能够实现共享Activity实例的功能。
注:在SingleInstance模式下实现共享的前提是所有的Activity都没有被销毁。
====================================================================================================================================
中间有一部分内容是从别的博客中参考的,之前写的时候没有及时注明出处。我的失误。有的log的图片我不知道怎么上传。回头等我会用这个了再更新下这个文档。共勉。
作者:Joseph_wkq 发表于2016/10/10 9:18:29 原文链接
阅读:25 评论:0 查看评论