了解了Android应用程序的目录结构和其中每个文件的功能,要进行应用开发,还需要对Android应用构造进行深入的分析。Android应用程序由以下4个模块构造而成:
Activity
Intent
Content Provider
Service
当然,也不是每个Android应用程序都必须由这4部分组成,它可以根据开发者需求来进行组合,比如上面建立的HelloAndroid项目就只使用了Activity这一个模块。但是,任何一个应用程序都必须在AndroidManfest.xml文件中声明使用到的这些模块。
1. Activity
Activity是最基本的模块,我们在HelloAndroid项目中已经使用过。我们称之为"活动",在应用程序中,一个活动(Activity)通常就是一个单独的屏幕。每一个活动都被实现为一个独立的类,并且从活动基类中继承而来,活动类将会显示由视图控件组成的用户接口,并对事件作出响应。例如上HelloAndroid项目中的HelloAndroid.java即继承了活动(Activity)类。大多数的应用都是由多个Activity显示组成,例如,对一个文本信息应用而言,第一个屏幕用来显示发送消息的联系人列表,第二个屏幕用来写文本消息和选择收件人,第三个屏幕查看消息历史或者消息设置操作等。
这里的每一个屏幕就是一个活动,很容易实现从一个屏幕到一个新的屏幕,并且完成新的活动。当一个新的屏幕打开后,前一个屏幕将会暂停,并保存在历史栈中。用户可以返回到历史栈中的前一个屏幕,当屏幕不再使用时,还可以从历史栈中删除。
简单理解,Activity代表一个用户所能看到的屏幕,主要用于处理应用程序的整体性工作,例如,监听系统事件(按键事件、触摸屏事件等),为用户显示指定的View,启动其他Activity等。所有应用的Activity都继承于android.app.Activity类,该类是Android提供的基层类,其他的Activity继承该父类后,通过父类的方法来实现各种功能,这种设计在其他领域也较为常见。
2. Intent
Android用Intent这个特殊类实现在Activity与Activity之间的切换。Intent类用于描述应用的功能。在Intent的描述结构中,有两个最重要的部分:动作和动作对应的数据。典型的动作类型有:MAIN、VIEW、PICK、EDIT等,而动作对应的数据则以URI的形式表示。例如,要查看一个人的联系方式,需要创建一个动作类型为VIEW的intent,以及一个表示这个人的URI。
通过解析各种intent,从一个屏幕导航到另一个屏幕是很简单的。当向前导航时,activity将会调用startActivity(IntentmyIntent)方法。然后,系统会在所有已安装的应用程序中定义的IntentFilter中查找,找到最匹配myIntent的Intent对应的activity。新的activity接收到myIntent的通知后,开始运行。当startActivity方法被调用时,将触发解析myIntent的动作,该机制提供了两个关键好处:
Activities能够重复利用从其他组件中以Intent的形式产生的请求。
Activities可以在任何时候被具有相同IntentFilter的新的Activity取代。
下面我们举例来说明两个Activity之间的切换。运行效果:当应用程序启动时显示布局main.xml,如图3-2所示,当我们点击"切换"按钮时,屏幕显示布局main2.xml,如图3-3所示,再点击"切换"按钮,又回到如图3-2所示。就这样通过Intent完成了两个Activity之间的切换。
图3-2 Activity01 |
图3-3 Activity02 |
具体实现参见本书所附代码:第三章\Examples_03_01
代码清单3-9 Activity01.java
1. package com.yarin.android.Examples_03_01;
2.
3. import android.app.Activity;
4. import android.content.Intent;
5. import android.os.Bundle;
6. import android.view.View;
7. import android.widget.Button;
8.
9. /**
10. * 在Examples_02_01项目中一共使用了2两个Activity,
11. * 每使用一个Activity都必须在AndroidManifest.xml中
12. * 进行声明。
13. */
14. public class Activity01 extends Activity
15. {
16. public void onCreate(Bundle savedInstanceState)
17. {
18. super.onCreate(savedInstanceState);
19. /* 设置显示main.xml布局 */
20. setContentView(R.layout.main);
21. /* findViewById(R.id.button1)取得布局main.xml中的button1 */
22. button
23. /* 监听button的事件信息 */
24. button.setOnClickListener(new Button.OnClickListener() {
25. public void onClick(View v)
26. {
27. /* 新建一个Intent对象 */
28. intent = new
29. /* 指定intent要启动的类 */
30. intent.setClass(Activity01.this, Activity02.class);
31. /* 启动一个新的Activity */
32. startActivity(intent);
33. /* 关闭当前的Activity */
34. Activity01.this.finish();
35. }
36. });
37. }
38. }
代码清单3-10 Activity02.java
1. package com.yarin.android.Examples_03_01;
2.
3. import android.app.Activity;
4. import android.content.Intent;
5. import android.os.Bundle;
6. import android.view.View;
7. import android.widget.Button;
8.
9.
10. public class Activity02 extends Activity
11. {
12. public void onCreate(Bundle savedInstanceState)
13. {
14. super.onCreate(savedInstanceState);
15. /* 设置显示main2.xml布局 */
16. setContentView(R.layout.main2);
17. /* findViewById(R.id.button2)取得布局main.xml中的button2 */
18. button
19. /* 监听button的事件信息 */
20. button.setOnClickListener(new Button.OnClickListener() {
21. public void onClick(View v)
22. {
23. /* 新建一个Intent对象 */
24. intent = new
25. /* 指定intent要启动的类 */
26. intent.setClass(Activity02.this, Activity01.class);
27. /* 启动一个新的Activity */
28. startActivity(intent);
29. /* 关闭当前的Activity */
30. Activity02.this.finish();
31. }
32. });
33. }
34. } 代码清单3-11 main.xml
1. <?xml version="1.0" encoding="utf-8"?>
2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
3. android:orientation="vertical"
4. android:layout_width="fill_parent"
5. android:layout_height="fill_parent"
6. >
7. <TextView
8. android:layout_width="fill_parent"
9. android:layout_height="wrap_content"
10. android:text="@string/hello"
11. />
12. <Button
13. android:id="@+id/button1"
14. android:layout_width="100px"
15. android:layout_height="wrap_content"
16. android:layout_x="100px"
17. android:layout_y="80px"
18. android:text="切换"
19. >
20. </Button>
21. </LinearLayout>
代码清单3-12 main2.xml
1. <?xml version="1.0" encoding="utf-8"?>
2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
3. android:orientation="vertical"
4. android:layout_width="fill_parent"
5. android:layout_height="fill_parent"
6. >
7. <TextView
8. android:layout_width="fill_parent"
9. android:layout_height="wrap_content"
10. android:text="@string/hello2"
11. >
12. </TextView>
13. <Button
14. android:id="@+id/button2"
15. android:layout_width="100px"
16. android:layout_height="wrap_content"
17. android:layout_x="100px"
18. android:layout_y="80px"
19. android:text="切换"
20. >
21. </Button>
22. </LinearLayout>
如代码清单3-9所示,我们需要在AndroidManifest.xml中声明使用的Activity02,如下代码清单3-13所示。
代码清单3-13 AndroidManifest.xml
1. <?xml version="1.0" encoding="utf-8"?>
2. <manifest xmlns:android="http://schemas.android.com/apk/res/android"
3. package="com.yarin.android.Examples_03_01"
4. android:versionCode="1"
5. android:versionName="1.0">
6. <application android:icon="@drawable/icon" android:label="@string/app_name">
7. <activity android:name=".Activity01"
8. android:label="@string/app_name">
9. <intent-filter>
10. <action android:name="android.intent.action.MAIN" />
11. <category android:name="android.intent.category.LAUNCHER" />
12. </intent-filter>
13. </activity>
14. <activity android:name="Activity02"></activity>
15. </application>
16. <uses-sdk android:minSdkVersion="5" />
17. </manifest>
如果希望Android应用能够对外部事件(如当电话呼入时,或者数据网络可用时,或者到了晚上时)做出响应,可以使用IntentReceiver。虽然IntentReceiver在感兴趣的事件发生时会使用NotificationManager通知用户,但它并不能生成UI。IntentReceiver在AndroidManifest.xml中注册,但也可以在代码中使用Context.registerReceiver()进行注册。当intentreceiver被触发时,应用不必对请求调用intentreceiver,系统会在需要时启动应用。各种应用还可以通过使用Context.broadcastIntent()将它们自己的intentreceiver广播给其他应用。
3. Content Provider
Android应用能够将它们的数据保存到文件和SQLite数据库中,甚至是任何有效的设备中。当你想将你的应用数据与其他的应用共享时,Content Provider就可以发挥作用了。因为Content Provider类实现了一组标准的方法,能够让其他的应用保存或读取此内容提供器处理的各种数据类型。
数据是应用的核心。在Android中,默认使用鼎鼎大名的SQLite作为系统数据库。但是在Android中,使用方法有点小小的不一样。在Android中,每一个应用都运行在各自的进程中,当一个应用需要访问其他应用的数据时,也就是数据需要在不同的虚拟机之间传递,这样的情况操作起来可能有些困难(正常情况下,你不能读取其他应用的db文件),ContentProvider正是用来解决在不同的应用包之间共享数据的工具。
在Android中,content provider是一个特殊的存储数据的类型,它提供了一套标准的接口用来获取和操作数据。并且,Android自身也提供了现成的content provider:Contacts、Browser、CallLog、Settings、MediaStore。应用可以通过唯一的ContentResolver interface来使用具体的某个content provider,然后你就可以用ContentResolver提供的方法来使用你需要的content provider了。其中,contentResolver提供的方法包括query()、insert()、update()等。要使用这些方法,还会涉及URI。你可以将它理解成string形式的contentProvider的完全路径。
下面我们通过一个例子来学习ContentProvider的使用,该例子主要实现通过ContentProvider获得电话本中的数据,然后显示到一个TextView中,在运行程序之前我们先看看电话本中存储的电话号码,如图3-4所示,然后再运行程序看看我们获得的数据,如图3-5所示,来看看我们通过ContentProvider获得的数据是否正确。
图3-4 电话本数据 |
图3-5 通过ContentProvider获得电话本数据 |
代码清单3-14:Activity01.java
1. package com.yarin.android.Examples_03_02;
2.
3. import android.app.Activity;
4. import android.content.ContentResolver;
5. import android.database.Cursor;
6. import android.os.Bundle;
7. import android.provider.ContactsContract;
8. import android.provider.ContactsContract.PhoneLookup;
9. import android.widget.TextView;
10.
11. public class Activity01 extends Activity
12. {
13. public void onCreate(Bundle savedInstanceState)
14. {
15. tv = new
16. string = "";
17. super.onCreate(savedInstanceState);
18. //得到ContentResolver对象
19. cr = getContentResolver();
20. //取得电话本中开始一项的光标
21. cursor = cr.query(ContactsContract. Contacts.CONTENT_URI, null, null, null, null);
22. //向下移动一下光标
23. while(cursor.moveToNext())
24. {
25. //取得联系人名字
26. nameFieldColumnIndex = cursor. getColumnIndex(PhoneLookup.DISPLAY_NAME);
27. contact = cursor.getString(nameFieldColumnIndex);
28. //取得电话号码
29. numberFieldColumnIndex = cursor. getColumnIndex(PhoneLookup.NUMBER);
30. number = cursor.getString(numberFieldColumnIndex);
31.
32. string += (contact+":"+number+"\n");
33. }
34. cursor.close();
35. //设置TextView显示的内容
36. tv.setText(string);
37. //显示到屏幕
38. setContentView(tv);
39. }
40. }
前面强调过,要使用这些模块,需要在AndroidManifest.xml声明,如代码清单3-15所示。
代码清单3-15 AndroidManifest.xml
1. <?xml version="1.0" encoding="utf-8"?>
2. <manifest xmlns:android="http://schemas.android.com/apk/res/android"
3. package="com.yarin.android.Examples_03_02"
4. android:versionCode="1"
5. android:versionName="1.0">
6. <uses-permission
7. android:name="android.permission.READ_CONTACTS">
8. </uses-permission>
9. <application android:icon="@drawable/icon" android:label="@string/app_name">
10. <activity android:name=".Activity01"
11. android:label="@string/app_name">
12. <intent-filter>
13. <action android:name="android.intent.action.MAIN" />
14. <category android:name="android.intent.category.LAUNCHER" />
15. </intent-filter>
16. </activity>
17. </application>
18. <uses-sdk android:minSdkVersion="5" />
19. </manifest>
4. Service
Service即"服务"的意思,既然是服务,那么Service将是一个生命周期长而且没有用户界面的程序。比如一个正在从播放列表中播放歌曲的媒体播放器,在这个媒体播放器应用中,应该会有多个activity,让使用者可以选择歌曲并播放歌曲。然而,音乐重放这个功能并没有对应的activity,因为使用者会认为在导航到其他屏幕时音乐应该还在播放。在这个例子中,媒体播放器这个activity会使用Context.startService()来启动一个service,从而可以在后台保持音乐的播放。同时,系统也将保持这个service一直执行,直到这个service运行结束。另外,我们还可以通过使用Context.bindService()方法连接到一个service上(如果这个service当前还没有处于启动状态,则将启动它)。当连接到一个service之后,还可用service提供的接口与它进行通讯。以媒体播放器为例,我们还可以执行暂停、重播等操作。
下面我们通过一个例子来学习service的使用,该例子通过service来播放一首MP3,如图3-6所示。当用户点击"开始"按钮,音乐开始播放;点击"停止"按钮,停止音乐播放。当然,这里需要在资源文件中添加一首MP3歌曲,如图3-7所示。
图3-6 使用Service播放音乐 |
图3-7 test.mp3 |
代码清单3-16 Activity01.java
1. package com.yarin.android.Examples_03_03;
2.
3. import android.app.Activity;
4. import android.content.Intent;
5. import android.os.Bundle;
6. import android.view.View;
7. import android.view.View.OnClickListener;
8. import android.widget.Button;
9.
10. public class Activity01 extends Activity
11. {
12. public void onCreate(Bundle savedInstanceState)
13. {
14. super.onCreate(savedInstanceState);
15. setContentView(R.layout.main);
16.
17. //从main.xml布局中获得Button对象
18. button_start
19. button_stop
20. //设置按钮(Button)监听
21. button_start.setOnClickListener(start);
22. button_stop.setOnClickListener(stop);
23.
24. }
25.
26. //开始按钮
27. start = new
28. {
29. public void onClick(View v)
30. {
31. //开启Service
32. startService(new Intent("com.yarin.Android.MUSIC"));
33. }
34. };
35. //停止按钮
36. stop = new
37. {
38. public void onClick(View v)
39. {
40. //停止Service
41. stopService(new Intent("com.yarin.Android.MUSIC"));
42. }
43. };
44. }
使用Server来播放音乐如代码清单3-17所示。
代码清单3-17 MusicService.java
1. package com.yarin.android.Examples_03_03;
2.
3. import android.app.Service;
4. import android.content.Intent;
5. import android.media.MediaPlayer;
6. import android.os.IBinder;
7.
8. public class MusicService extends Service
9. {
10. //MediaPlayer对象
11. private MediaPlayer player;
12.
13. public IBinder onBind(Intent arg0)
14. {
15. return null;
16. }
17.
18. public void onStart(Intent intent, int startId)
19. {
20. super.onStart(intent, startId);
21. //这里可以理解为装载音乐文件
22. player = MediaPlayer.create(this, R.raw.test);
23. //开始播放
24. player.start();
25. }
26.
27. public void onDestroy()
28. {
29. super.onDestroy();
30. //停止音乐--停止Service
31. player.stop();
32. }
33. }
界面布局XML文件如代码清单3-18所示。
代码清单3-18 main.xml
1. <?xml version="1.0" encoding="utf-8"?>
2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
3. android:orientation="vertical"
4. android:layout_width="fill_parent"
5. android:layout_height="fill_parent"
6. >
7. <TextView
8. android:layout_width="fill_parent"
9. android:layout_height="wrap_content"
10. android:text="@string/hello"
11. />
12. <Button
13. android:id="@+id/start"
14. android:layout_width="fill_parent"
15. android:layout_height="wrap_content"
16. android:text="开始播放"/>
17. <Button
18. android:id="@+id/stop"
19. android:layout_width="fill_parent"
20. android:layout_height="wrap_content"
21. android:text="停止播放"
22. />
23. </LinearLayout>
我们使用Service时同样需要在"AndroidManifest.xml"声明,声明方式如代码清单3-19所示。
代码清单3-19 AndroidManifest.xml
1. <?xml version="1.0" encoding="utf-8"?>
2. <manifest xmlns:android="http://schemas.android.com/apk/res/android"
3. package="com.yarin.Android.Examples_03_03"
4. android:versionCode="1"
5. android:versionName="1.0">
6. <application android:icon="@drawable/icon" android:label="@string/app_name">
7. <activity android:name=".Activity01"
8. android:label="@string/app_name">
9. <intent-filter>
10. <action android:name="android.intent.action.MAIN" />
11. <category android:name="android. intent.category.LAUNCHER" />
12. </intent-filter>
13. </activity>
14. <service android:name=".MusicService">
15. <intent-filter>
16. <action android:name="com.yarin.Android.MUSIC" />
17. <category android:name="android. intent.category.default" />
18. </intent-filter>
19. </service>
20. </application>
21. <uses-sdk android:minSdkVersion="5" />
22. </manifest>