Android应用程序的四大组件分别是Activity、Service、BroadcastReceiver和ContentProvider。本文将主要对Activity做一个较全面的总结,Service和BroadcaseReceiver也将在随后的博文中分别进行介绍。有关ContentProvider的使用方法可以参阅博文《Android学习笔记37:使用Content Providers方式共享数据》。
1.Activity注册
Activity是Android中最常见的组件,每个Activity都相当于一个屏幕,为用户提供了进行交互的可视化界面。应用程序可以根据需要包含一个或多个Activity,这些Activity都继承自android.app包下的Activity类,并且这些Activity之间的运行是相互独立的。
应用程序中使用到的所有Activity都需要在AndroidManifest.xml文件中进行注册。注册Activity时,需要使用<Activity></Activity>标签,该标签位于<application></application>中,具体位置如下:
1 <manifest xmlns:android="http://schemas.android.com/apk/res/android" 2 package="com.example.android_datastorage_sqlite" 3 android:versionCode="1" 4 android:versionName="1.0" > 5 6 <application 7 android:icon="@drawable/ic_launcher" 8 android:label="@string/app_name" 9 android:theme="@style/AppTheme"> 10 11 <activity 12 android:name=".MainActivity" 13 android:label="@string/title_activity_main" > 14 <intent-filter> 15 <action android:name="android.intent.action.MAIN" /> 16 <category android:name="android.intent.category.LAUNCHER" /> 17 </intent-filter> 18 </activity> 19 20 </application> 21 22 </manifest>
1.1 <Activity>标签的属性值
在<Activity></Activity>标签中,有以下一些常用的属性值可以设置:
(1)android:name,该属性值用于表示自定义的Activity子类名称,其值可以是子类的全称类名“com.example.android_datastorage_sqlite.MainActivity”,也可以以“.”开头省略掉应用程序的包名,后面直接加上子类的类名(即“.MainActivity”)。
(2)android:process,该属性值用于表示Activity组件应该运行在哪个进程中,一般情况下不用设置该属性,所有的组件均运行在同一个进程中。如果该值以“:”开头将会为该Activity创建一个私有的新进程;如果该值以小写字母开头将会创建一个全局的新进程。
(3)android:permission,该属性值用于指定启动该Activity所必须具有的权限。
(4)android:screenOrientation,该属性值用于设置屏幕方向,可以选择的设置值有:unspecified(默认值,由系统来选择方向)、landecape(横向显示,宽度比高度大)、portrait(纵向显示,高度比宽度大)、user(使用用户当前首选的方向)、behind(使用与Activity堆栈中该Activity之下的那个Activity相同的方向)、sensor(显示的方向由设备的方向传感器来决定)、nosensor(屏幕的显示方向不会参照物理方向传感器)。
(5)android:launchMode,该属性值用于设置Activity的启动模式,可以选择的设置值有:standard、singleTop、singleTask、singleInstance。
(6)android:theme,该属性值用于设置Activity的主题模式。
1.2 <Activity>标签的内嵌标签
在<Activity></Activity>标签中,可以内嵌以下两个标签:
(1)<intent-filter></intent-filter>,该标签指明了Activity组件的过滤规则。
(2)<meda-data>,该标签指明了额外的提供给Activity组件的数据值。
1.3不同包下的Activity注册方法
当应用程序中包含多个Activity,并且这些Activity不在同一个包中时,注册Activity时需要做一点小小的改动。
比如,在图1所示的工程中包含了3个Activity,且这3个Activity处于不同的包中。
图1 工程目录结构示意图
那么,在注册AddDialogActivity和DeleteDialogActivity时,使用上述代码中和注册MainActivity一样的方法是行不通的。因为上述代码指定了package="com.example.android_datastorage_sqlite",所以可以使用android:name=".MainActivity"的方式注册MainActivity。但是很显然,AddDialogActivity和DeleteDialogActivity并不在包"com.example.android_datastorage_sqlite"中,所以我们应当使用android:name=".userOerate.AddDialogActivity"和android:name=".userOerate.DeleteDialogActivity"的方式来注册AddDialogActivity和DeleteDialogActivity组件。
2.Activity生命周期
Activity的生命周期包含3个阶段,7个方法。具体可以参阅博文《Android学习笔记04:Activity及Activity生命周期》。这里便不再赘述了。
3.Activity内容的两种声明方式
Activity显示内容有两种声明方式,一种是通过xml布局文件来声明,一种是将屏幕设置为某个继承自View类的对象。
3.1通过xml布局文件声明
Activity的xml布局文件位于工程的res目录下的layout目录中,一个布局文件就相当于一个View容器,其中可以添加Android系统内置的View(比如Button、TextView等),也可以添加自定义的继承自View类的子类对象,甚至是View容器。
同时,在xml布局文件还可以指定View对象在View容器中的排列方式(比如线性布局LinearLayout等)。
通过xml布局文件将不同的View对象整合在一起确实非常方便,但是留给开发人员的自主性却不够。尤其是当进行游戏编程时,往往Android系统中自带的View对象并不能完全满足设计需求。在这种情况下,一般需要通过继承和扩展View类来开发自己想要的用户界面。
3.2通过View类的子类对象声明
使用View类的子类对象作为Activity所要显示的内容,可以通过以下的两个步骤来实现:
(1)编写一个继承自View类的MyView类,在MyView类中需要实现onDraw(Canvas canvas)方法。在该方法中通过操作Canvas对象,完成界面的绘制工作。
(2)实现了界面的绘制之后,就可以在Activity中通过setContentView(View view)方法加载MyView对象,从而让绘制的界面显示出来。
以上两个步骤的具体实现方法,可以参阅博文《Android学习笔记09:Paint及Canvas的简单应用》。
4.Activity之间的通信
Intent对象是组件之间通信的载体,组件之间进行通信就是一个个Intent对象在不断的传递。Intent对象不仅可以运行在相同的组件之间(如Activity之间的通信),也可以运行在不同的组件之间(如Activity与Service之间的通信)。
对于Activity组件,Intent主要通过调用Context.startActivity()、Context.startActivityForResult()方法实现传递,其结果是启动一个新的Activity或者使当前的Activity开始新的任务。
关于如何使用以上两个方法实现Activity之间的通信,可以参阅博文《Android学习笔记33:Intent介绍及Intent在Activity中的使用方法》。
5.保存Activity临时状态数据
在Activity中提供了onSaveInstanceState()方法,用于当前Activity在系统未经你许可的情况下(比如处于暂停或停止状态的Activity,在系统资源极度匮乏时,有可能被杀死)被销毁时,对当前Activity中的临时状态数据进行保存。
使用onSaveInstanceState(Bundle outState)方法,可以在Activity被杀掉之前将Activity的临时状态数据以键值对的形式存放到Bundle对象中,从而能够在再次调用OnCreate()方法时恢复Activity状态。
如下的代码使用onSaveInstanceState()方法对EditText对象中的输入内容进行了保存,并在OnCreate()方法中对EditText对象中的数据进行了恢复。
1 EditText mEditText; //EditText对象,用于输入内容 2 private static final String KEY1 = "editTextValues"; //用于保存EditText对象内容的键 3 private static final String TAG = "MainActivity"; //Log输出过滤器 4 5 /* 6 * Function : onCreate方法,Activity创建时调用 7 * Author : 博客园-依旧淡然 8 */ 9 public void onCreate(Bundle savedInstanceState) { 10 super.onCreate(savedInstanceState); 11 setContentView(R.layout.activity_main); 12 mEditText = (EditText)this.findViewById(R.id.edittext); 13 14 //如果Bundle对象中保存有所需对象的内容,则恢复该对象内容 15 if((savedInstanceState != null) && savedInstanceState.containsKey(KEY1)) { 16 mEditText.setText(savedInstanceState.getString(KEY1)); 17 } 18 Log.i(TAG, "-->onCreate()"); 19 } 20 21 /* 22 * Function : onSaveInstanceState方法,销毁Activity时调用 23 * Author : 博客园-依旧淡然 24 */ 25 protected void onSaveInstanceState(Bundle outState) { 26 super.onSaveInstanceState(outState); 27 String editTextValues = mEditText.getText().toString(); //获取EditText对象内容 28 outState.putString(KEY1, editTextValues); //以键值对的形式保存EditText对象内容 29 Log.i(TAG, "-->onSaveInstanceState()"); 30 }
需要注意的一点是,onSaveInstanceState()方法并不是总会被调用的,只有系统为了节省内存资源而强制销毁Activity时才会调用,所以应当仅仅通过重写onSaveInstanceState()方法来保存一些临时数据,而不是持久数据。要保存持久数据,应该使用onPause()方法。
6.Activity的主题模式
上面已经提到,通过设置<Activity></Activity>标签下的android:theme属性值,可以设置Activity的主题模式。
在Android系统中预设了以下一些主题模式:
(1)android:theme="@android:style/Theme.Dialog",将一个Activity显示为对话框模式
(2)android:theme="@android:style/Theme.NoTitleBar",不显示应用程序标题栏
(3)android:theme="@android:style/Theme.NoTitleBar.Fullscreen",不显示应用程序标题栏,且全屏显示该Activity
(4)android:theme="@android:style/Theme.Light",将Activity的背景设置为白色
(5)android:theme="@android:style/Theme.Light.NoTitleBar",将Activity的背景设置为白色,且不显示应用程序标题栏
(6)android:theme="@android:style/Theme.Light.NoTitleBar.Fullscreen",将Activity的背景设置为白色,不显示应用程序标题栏,且全屏显示该Activity
(7)android:theme="@android:style/Theme.Black",将Activity的背景设置为黑色
(8)android:theme="@android:style/Theme.Black.NoTitleBar",将Activity的背景设置为黑色,且不显示应用程序标题栏
(9)android:theme="@android:style/Theme.Black.NoTitleBar.Fullscreen",将Activity的背景设置为黑色,不显示应用程序标题栏,且全屏显示该Activity
(10)android:theme="@android:style/Theme.Wallpaper",以系统桌面做为Activity的背景
(11)android:theme="@android:style/Theme.Wallpaper.NoTitleBar",以系统桌面做为Activity的背景,且不显示应用程序标题栏
(12)android:theme="@android:style/Theme.Wallpaper.NoTitleBar.Fullscreen",以系统桌面做为Activity的背景,不显示应用程序标题栏,且全屏显示该Activity
比如,我们可以通过设置android:theme="@android:style/Theme.Dialog",将一个Activity仿造成对话框Dialog的样子,如图2所示。
图2 对话框模式的Activity