一 . 四大组件:

Android 四大组件分别为 activity、service、content provider、broadcast receiver。

一、android 四大组件详解

1、activity

(1)一个 Activity 通常就是一个单独的屏幕(窗口)。

(2)Activity 之间通过 Intent 进行通信。

(3)android 应用中每一个 Activity 都必须要在 AndroidManifest.xml 配置文件中声明,否则系统将不识别也不执行该 Activity。

2、service

(1)service 用于在后台完成用户指定的操作。service 分为两种:

(a)started(启动):当应用程序组件(如 activity)调用 startService() 方法启动服务时,服务处于 started 状态。

(b)bound(绑定):当应用程序组件调用 bindService() 方法绑定到服务时,服务处于 bound 状态。

(2)startService() 与 bindService() 区别:

(a)started service(启动服务)是由其他组件调用 startService() 方法启动的,这导致服务的 onStartCommand() 方法被调用。当服务是 started 状态时,其生命周期与启动它的组件无关,并且可以在后台无限期运行,即使启动服务的组件已经被销毁。因此,服务需要在完成任务后调用 stopSelf() 方法停止,或者由其他组件调用 stopService() 方法停止。

(b) 使用 bindService() 方法启用服务,调用者与服务绑定在了一起,调用者一旦退出,服务也就终止,大有 “不求同时生,必须同时死” 的特点。

(3) 开发人员需要在应用程序配置文件中声明全部的 service,使用 标签。

(4)Service 通常位于后台运行,它一般不需要与用户交互,因此 Service 组件没有图形用户界面。Service 组件需要继承 Service 基类。Service 组件通常用于为其他组件提供后台服务或监控其他组件的运行状态。

3、content provider

(1)android 平台提供了 Content Provider 使一个应用程序的指定数据集提供给其他应用程序。其他应用可以通过 ContentResolver 类从该内容提供者中获取或存入数据。

(2)只有需要在多个应用程序间共享数据是才需要内容提供者。例如,通讯录数据被多个应用程序使用,且必须存储在一个内容提供者中。它的好处是统一数据访问方式。

(3)ContentProvider 实现数据共享。ContentProvider 用于保存和获取数据,并使其对所有应用程序可见。这是不同应用程序间共享数据的唯一方式,因为 android 没有提供所有应用共同访问的公共存储区。

(4)开发人员不会直接使用 ContentProvider 类的对象,大多数是通过 ContentResolver 对象实现对 ContentProvider 的操作。

(5)ContentProvider 使用 URI 来唯一标识其数据集,这里的 URI 以 content:// 作为前缀,表示该数据由 ContentProvider 来管理。

4、broadcast receiver

(1)你的应用可以使用它对外部事件进行过滤,只对感兴趣的外部事件 (如当电话呼入时,或者数据网络可用时) 进行接收并做出响应。广播接收器没有用户界面。然而,它们可以启动一个 activity 或 serice 来响应它们收到的信息,或者用 NotificationManager 来通知用户。通知可以用很多种方式来吸引用户的注意力,例如闪动背灯、震动、播放声音等。一般来说是在状态栏上放一个持久的图标,用户可以打开它并获取消息。

(2)广播接收者的注册有两种方法,分别是程序动态注册和 AndroidManifest 文件中进行静态注册。

(3)动态注册广播接收器特点是当用来注册的 Activity 关掉后,广播也就失效了。静态注册无需担忧广播接收器是否被关闭,只要设备是开启状态,广播接收器也是打开着的。也就是说哪怕 app 本身未启动,该 app 订阅的广播在触发时也会对它起作用。

二、android 四大组件总结:

(1)4 大组件的注册

4 大基本组件都需要注册才能使用,每个 Activity、service、Content Provider 都需要在 AndroidManifest 文件中进行配置。AndroidManifest 文件中未进行声明的 activity、服务以及内容提供者将不为系统所见,从而也就不可用。而 broadcast receiver 广播接收者的注册分静态注册(在 AndroidManifest 文件中进行配置)和通过代码动态创建并以调用 Context.registerReceiver() 的方式注册至系统。需要注意的是在 AndroidManifest 文件中进行配置的广播接收者会随系统的启动而一直处于活跃状态,只要接收到感兴趣的广播就会触发(即使程序未运行)。

(2)4 大组件的激活

内容提供者的激活:当接收到 ContentResolver 发出的请求后,内容提供者被激活。而其它三种组件 activity、服务和广播接收器被一种叫做 intent 的异步消息所激活。

(3)4 大组件的关闭

内容提供者仅在响应 ContentResolver 提出请求的时候激活。而一个广播接收器仅在响应广播信息的时候激活。所以,没有必要去显式的关闭这些组件。Activity 关闭:可以通过调用它的 finish() 方法来关闭一个 activity。服务关闭:对于通过 startService() 方法启动的服务要调用 Context.stopService() 方法关闭服务,使用 bindService() 方法启动的服务要调用 Contex.unbindService() 方法关闭服务。

(4)android 中的任务(activity 栈)

(a)任务其实就是 activity 的栈,它由一个或多个 Activity 组成,共同完成一个完整的用户体验。栈底的是启动整个任务的 Activity,栈顶的是当前运行的用户可以交互的 Activity,当一个 activity 启动另外一个的时候,新的 activity 就被压入栈,并成为当前运行的 activity。而前一个 activity 仍保持在栈之中。当用户按下 BACK 键的时候,当前 activity 出栈,而前一个恢复为当前运行的 activity。栈中保存的其实是对象,栈中的 Activity 永远不会重排,只会压入或弹出。

(b)任务中的所有 activity 是作为一个整体进行移动的。整个的任务(即 activity 栈)可以移到前台,或退至后台。

(c)Android 系统是一个多任务 (Multi-Task) 的操作系统,可以在用手机听音乐的同时,也执行其他多个程序。每多执行一个应用程序,就会多耗费一些系统内存,当同时执行的程序过多,或是关闭的程序没有正确释放掉内存,系统就会觉得越来越慢,甚至不稳定。为了解决这个问题,Android 引入了一个新的机制,即生命周期(Life Cycle)。

二 . 六大布局:

Android 六大界面布局方式:

声明 Android 程序布局有两种方式:

  1. 使用 XML 文件描述界面布局;

  2. 在 Java 代码中通过调用方法进行控制。

我们既可以使用任何一种声明界面布局的方式,也可以同时使用两种方式。

使用 XML 文件声明有以下 3 个特点:

    1. 将程序的表现层和控制层分离;
    1. 在后期修改用户界面时,无须更改程序的源程序;
    1. 可通过 WYSIWYG 可视化工具直接看到所设计的用户界面,有利于加快界面设计的过程。

建议尽量采用 XML 文件声明界面元素布局。在程序运行时动态添加界面布局会大大降低应用响应速度,但依然可以在必要时动态改变屏幕内容。

六大界面布局方式包括: 线性布局 (LinearLayout)、框架布局(FrameLayout)、表格布局(TableLayout)、相对布局(RelativeLayout)、绝对布局(AbsoluteLayout) 和网格布局(GridLayout) 。

1. LinearLayout 线性布局

LinearLayout 容器中的组件一个挨一个排列,通过控制 android:orientation 属性,可控制各组件是横向排列还是纵向排列。

LinearLayout 的常用 XML 属性及相关方法

XML 属性相关方法说明
android:gravitysetGravity(int)设置布局管理器内组件的对齐方式
android:orientationsetOrientation(int)设置布局管理器内组件的排列方式,可以设置为 horizontal、vertical 两个值之一

其中,gravity 属性支持 top, left, right, center_vertical, fill_vertical, center_horizontal, fill_horizontal, center, fill, clip_vertical, clip_horizontal。也可以同时指定多种对齐方式的组合。

LinearLayout 子元素支持的常用 XML 属性及方法

XML 属性说明
android:layout_gravity指定该子元素在 LinearLayout 中的对齐方式
android:layout_weight指定子元素在 LinearLayout 中所占的权重

2. TableLayout 表格布局

TableLayout 继承自 Linearout,本质上仍然是线性布局管理器。表格布局采用行、列的形式来管理 UI 组件,并不需要明确地声明包含多少行、多少列,而是通过添加 TableRow、其他组件来控制表格的行数和列数。

每向 TableLayout 中添加一个 TableRow 就代表一行;

每向 TableRow 中添加一个一个子组件就表示一列;

如果直接向 TableLayout 添加组件,那么该组件将直接占用一行;

在表格布局中,可以为单元格设置如下三种行为方式:

  • Shrinkable:该列的所有单元格的宽度可以被收缩,以保证该表格能适应父容器的宽度;
  • Strentchable:该列所有单元格的宽度可以被拉伸,以保证组件能完全填满表格空余空间;
  • Collapsed:如果该列被设置为 Collapsed,那么该列的所有单元格会被隐藏;

TableLayout 的常用 XML 属性及方法

XML 属性相关方法说明
android:collapseColumnssetColumns(int, boolean)设置需要被隐藏的列的序号,多个序号间用逗号分隔
android:shrinkColumnssetShrinkAllColumns(boolean)设置需要被收缩的列的序号
android:stretchColumnssetStretchAllColumns(boolean)设置允许被拉伸的列的序号

3. FrameLayout 帧布局

FrameLayout 直接继承自 ViewGroup 组件。帧布局为每个加入其中的组件创建一个空白的区域 (称为一帧),每个子组件占据一帧,这些帧会根据 gravity 属性执行自动对齐。

FrameLayout 的常用 XM 了属性及方法

XML 属性相关方法说明
android:foregroundsetForeground(Drawable)设置该帧布局容器的前景图像
android:foregroundGravitysetForeGroundGraity(int)定义绘制前景图像的 gravity 属性

4. RelativeLayout 相对布局

RelativeLayout 的 XML 属性及相关方法说明

XML 属性相关方法说明
android:gravitysetGravity(int) 
android:ignoreGravitysetIgnoreGravity(int)设置哪个组件不受 gravity 属性的影响

为了控制该布局容器的各子组件的布局分布,RelativeLayout 提供了一个内部类:RelativeLayout.LayoutParams。

RelativeLayout.LayoutParams 里只能设为 boolean 的 XML 属性

XML 属性说明
android:layout_centerHorizontal设置该子组件是否位于布局容器的水平居中
android:layout_centerVertical 
android:layout_centerParent 
android:layout_alignParentBottom 
android:layout_alignParentLeft 
android:layout_alignParentRight 
android:layout_alignParentTop

RelativeLayout.LayoutParams 里属性值为其他 UI 组件 ID 的 XML 属性

XML 属性说明
android:layout_toRightOf控制该子组件位于给出 ID 组件的右侧
android:layout_toLeftOf 
android:layout_above 
android:layout_below 
android:layout_alignTop 
android:layout_alignBottom 
android:layout_alignRight 
android:layout_alignLeft

5. Android 4.0 新增的网格布局 GridLayout

GridLayout 是 Android4.0 增加的网格布局控件,与之前的 TableLayout 有些相似,它把整个容器划分为 rows × columns 个网格,每个网格可以放置一个组件。性能及功能都要比 tablelayout 好,比如 GridLayout 布局中的单元格可以跨越多行,而 tablelayout 则不行,此外,其渲染速度也比 tablelayout 要快。

GridLayout 提供了 setRowCount(int) 和 setColumnCount(int) 方法来控制该网格的行和列的数量。

GridLayout 常用的 XML 属性和方法说明

XML 属性相关方法说明
android:alignmentModesetAlignmentMode(int)设置该布局管理器采用的对齐模式
android:columnCountsetColumnCount(int)设置该网格的列数量
android:columnOrderPreservedsetColumnOrderPreserved(boolean)设置该网格容器是否保留序列号
android:roeCountsetRowCount(int)设置该网格的行数量
android:rowOrderPreservedsetRowOrderPreserved(boolean)设置该网格容器是否保留行序号
android:useDefaultMarginssetUseDefaultMargins(boolean)设置该布局管理器是否使用默认的页边距

为了控制 GridLayout 布局容器中各子组件的布局分布,GridLayout 提供了一个内部类:GridLayout.LayoutParams,来控制 Gridlayout 布局容器中子组件的布局分布。

GridLayout.LayoutParams 常用的 XML 属性和方法说明

XML 属性说明
android:layout_column设置该组件在 GridLayout 的第几列
android:layout_columnSpan设置该子组件在 GridLayout 横向上跨几列
android:layout_gravity设置该子组件采用何种方式占据该网格的空间
android:layout_row设置该子组件在 GridLayout 的第几行
android:layout_rowSpan设置该子组件在 GridLayout 纵向上跨几行

6. AbsoluteLayout 绝对布局

即 Android 不提供任何布局控制,而是由开发人员自己通过 X 坐标、Y 坐标来控制组件的位置。每个组件都可指定如下两个 XML 属性:

  • layour_x;
  • layout_y;

绝对布局已经过时,不应使用或少使用。

界面布局类型的选择和性能优化

首先得明确,界面布局类型的嵌套越多越深越复杂,会使布局实例化变慢,使 Activity 的展开时间延长。建议尽量减少布局嵌套,尽量减少创建 View 对象的数量。

1 . 减少布局层次,可考虑用 RelativeLayout 来代替 LinearLayout。通过 Relative 的相对其他元素的位置来布局,可减少块状嵌套;

2 . 另一种减少布局层次的技巧是使用 <merge /> 标签来合并布局;

3 . 重用布局。Android 支持在 XML 中使用 <include /> 标签, <include /> 通过指定 android:layout 属性来指定要包含的另一个 XML 布局。

如:

<include android:id="@+id/id1" android:layout="@layout/mylayout"><include android:id="@+id/id2" android:layout="@layout/mylayout"><include android:id="@+id/id3" android:layout="@layout/mylayout">

三、五大存储:

Android 中,可供选择的存储方式有 SharedPreferences、文件存储、SQLite 数据库方式、内容提供器(Content provider)和网络。

一. SharedPreferences 方式

        Android 提供用来存储一些简单的配置信息的一种机制,例如,一些默认欢迎语、登录的用户名和密码等。其以键值对的方式存储,

使得我们可以很方便的读取和存入.

       1)程序要实现的功能:
       我们在 Name 文本框中输入 wangwu,在 Password 文本框中输入 123456,然后退出这个应用。我们在应用程序列表中找到这个应用,重新启动,可以看到其使用了前面输入的 Name 和 Password

       2) 实现的代码

       布局

        主要实现代码

1.  package com.demo; 

2.  import android.app.Activity; 
3.  import android.content.SharedPreferences; 
4.  import android.os.Bundle; 
5.  import android.widget.EditText; 

6.  public class SharedPreferencesDemo extends Activity { 

7.   public static final String SETTING_INFOS = "SETTING_Infos"; 
8.   public static final String NAME = "NAME"; 
9.   public static final String PASSWORD = "PASSWORD"; 
10.   private EditText field_name; // 接收用户名的组件  
11.   private EditText filed_pass; // 接收密码的组件  

12.   @Override 
13.   public void onCreate(Bundle savedInstanceState) { 
14.   super.onCreate(savedInstanceState); 
15.   setContentView(R.layout.main); 

16.   //Find VIew  
17.   field_name = (EditText) findViewById(R.id.name); // 首先获取用来输入用户名的组件  
18.   filed_pass = (EditText) findViewById(R.id.password); // 同时也需要获取输入密码的组件  

19.   // Restore preferences  
20.   SharedPreferences settings = getSharedPreferences(SETTING_INFOS, 0); // 获取一个 SharedPreferences 对象  
21.   String name = settings.getString(NAME, ""); // 取出保存的 NAME  
22.   String password = settings.getString(PASSWORD, ""); // 取出保存的 PASSWORD  

23.   //Set value  
24.   field_name.setText(name); // 将取出来的用户名赋予 field_name  
25.   filed_pass.setText(password); // 将取出来的密码赋予 filed_pass  
26.   } 

27.   @Override 
28.   protected void onStop(){ 
29.   super.onStop(); 

30.   SharedPreferences settings = getSharedPreferences(SETTING_INFOS, 0); // 首先获取一个 SharedPreferences 对象  
31.   settings.edit() 
32.   .putString(NAME, field_name.getText().toString()) 
33.   .putString(PASSWORD, filed_pass.getText().toString()) 
34.   .commit(); 
35.   } // 将用户名和密码保存进去  

36.  } 

SharedPreferences 保存到哪里去了?

       SharedPreferences 是以 XML 的格式以文件的方式自动保存的,在 DDMS 中的 File Explorer 中展开到 / data/data/<package

name>/shared_prefs 下,以上面这个为例,可以看到一个叫做 SETTING_Infos.xml 的文件

       注意:Preferences 只能在同一个包内使用,不能在不同的包之间使用。

二. 文件存储方式

       在 Android 中,其提供了 openFileInput 和 openFileOuput 方法读取设备上的文件,下面看个例子代码,具体如下所示: 
             String FILE_NAME = “tempfile.tmp”;  // 确定要操作文件的文件名
             FileOutputStream fos = openFileOutput(FILE_NAME, Context.MODE_PRIVATE); // 初始化
             FileInputStream fis = openFileInput(FILE_NAME); // 创建写入流

       上述代码中两个方法只支持读取该应用目录下的文件,读取非其自身目录下的文件将会抛出异常。需要提醒的是,如果调用

FileOutputStream 时指定的文件不存在,Android 会自动创建它。另外,在默认情况下,写入的时候会覆盖原文件内容,如果想把

新写入的内容附加到原文件内容后,则可以指定其模式为 Context.MODE_APPEND。

三. SQLite 数据库方式

      SQLite 是 Android 所带的一个标准的数据库,它支持 SQL 语句,它是一个轻量级的嵌入式数据库。
      1)实现的功能

          在这个例子里边,我们在程序的主界面有一些按钮,通过这些按钮可以对数据库进行标准的增、删、改、查。

      2)实现代码

          所用到的字符文件

  1. SQLite 数据库操作实例
  2. 建立数据库表
  3. 删除数据库表
  4. 插入两条记录
  5. 删除一条记录
  6. 查看数据库表

          布局代码

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/app_name" 
11.   /> 
12.   <Button 
13.   android:text="@string/button1" 
14.   android:id="@+id/button1" 
15.   android:layout_width="wrap_content" 
16.   android:layout_height="wrap_content" 
17.   ></Button> 
18.   <Button 
19.   android:text="@string/button2" 
20.   android:id="@+id/button2" 
21.   android:layout_width="wrap_content" 
22.   android:layout_height="wrap_content" 
23.   ></Button> 
24.   <Button 
25.   android:text="@string/button3" 
26.   android:id="@+id/button3" 
27.   android:layout_width="wrap_content" 
28.   android:layout_height="wrap_content" 
29.   ></Button> 
30.   <Button 
31.   android:text="@string/button4" 
32.   android:id="@+id/button4" 
33.   android:layout_width="wrap_content" 
34.   android:layout_height="wrap_content" 
35.   ></Button> 
36.   <Button 
37.   android:text="@string/button5" 
38.   android:id="@+id/button5" 
39.   android:layout_width="wrap_content" 
40.   android:layout_height="wrap_content" 
41.   ></Button> 

42.  </LinearLayout> 

          主要代码


1.  package com.sqlite; 

2.  import android.app.Activity; 
3.  import android.content.Context; 
4.  import android.database.Cursor; 
5.  import android.database.SQLException; 
6.  import android.database.sqlite.SQLiteDatabase; 
7.  import android.database.sqlite.SQLiteOpenHelper; 
8.  import android.os.Bundle; 
9.  import android.util.Log; 
10.  import android.view.View; 
11.  import android.view.View.OnClickListener; 
12.  import android.widget.Button; 

13.  /* 
14.   * 什么是 SQLiteDatabase? 
15.   * 一个 SQLiteDatabase 的实例代表了一个 SQLite 的数据库,通过 SQLiteDatabase 实例的一些方法,我们可以执行 SQL 语句, 
16.   * 对数据库进行增、删、查、改的操作。需要注意的是,数据库对于一个应用来说是私有的,并且在一个应用当中,数据库的名字也是惟一的。 
17.   */ 

18.  /* 
19.   * 什么是 SQLiteOpenHelper ? 
20.   * 这个类主要生成一个数据库,并对数据库的版本进行管理。 
21.   * 当在程序当中调用这个类的方法 getWritableDatabase() 或者 getReadableDatabase() 方法的时候,如果当时没有数据,那么 Android 系统就会自动生成一个数据库。 
22.   * SQLiteOpenHelper 是一个抽象类,我们通常需要继承它,并且实现里边的 3 个函数, 
23.   *     onCreate(SQLiteDatabase):在数据库第一次生成的时候会调用这个方法,一般我们在这个方法里边生成数据库表。 
24.   *     onUpgrade(SQLiteDatabase, int, int):当数据库需要升级的时候,Android 系统会主动的调用这个方法。一般我们在这个方法里边删除数据表,并建立新的数据表,当然是否还需要做其他的操作,完全取决于应用的需求。 
25.   *     onOpen(SQLiteDatabase):这是当打开数据库时的回调函数,一般也不会用到。 
26.   */ 

27.  public class SQLiteDemo extends Activity { 

28.   OnClickListener listener1 = null; 
29.   OnClickListener listener2 = null; 
30.   OnClickListener listener3 = null; 
31.   OnClickListener listener4 = null; 
32.   OnClickListener listener5 = null; 

33.   Button button1; 
34.   Button button2; 
35.   Button button3; 
36.   Button button4; 
37.   Button button5; 

38.   DatabaseHelper mOpenHelper; 

39.   private static final String DATABASE_NAME = "dbForTest.db"; 
40.   private static final int DATABASE_VERSION = 1; 
41.   private static final String TABLE_NAME = "diary"; 
42.   private static final String TITLE = "title"; 
43.   private static final String BODY = "body"; 

44.   // 建立一个内部类, 主要生成一个数据库  
45.   private static class DatabaseHelper extends SQLiteOpenHelper { 

46.   DatabaseHelper(Context context) { 
47.   super(context, DATABASE_NAME, null, DATABASE_VERSION); 
48.   } 

49.   // 在数据库第一次生成的时候会调用这个方法,一般我们在这个方法里边生成数据库表。  
50.   @Override 
51.   public void onCreate(SQLiteDatabase db) { 

52.   String sql = "CREATE TABLE" + TABLE_NAME + "(" + TITLE 
53.   + "text not null," + BODY + "text not null" + ");"; 
54.   Log.i("haiyang:createDB=", sql); 
55.   db.execSQL(sql); 
56.   } 

57.   @Override 
58.   public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { 
59.   } 
60.   } 

61.   @Override 
62.   public void onCreate(Bundle savedInstanceState) { 
63.   super.onCreate(savedInstanceState); 
64.   setContentView(R.layout.main); 
65.   prepareListener(); 
66.   initLayout(); 
67.   mOpenHelper = new DatabaseHelper(this); 

68.   } 

69.   private void initLayout() { 
70.   button1 = (Button) findViewById(R.id.button1); 
71.   button1.setOnClickListener(listener1); 

72.   button2 = (Button) findViewById(R.id.button2); 
73.   button2.setOnClickListener(listener2); 

74.   button3 = (Button) findViewById(R.id.button3); 
75.   button3.setOnClickListener(listener3); 

76.   button4 = (Button) findViewById(R.id.button4); 
77.   button4.setOnClickListener(listener4); 

78.   button5 = (Button) findViewById(R.id.button5); 
79.   button5.setOnClickListener(listener5); 

80.   } 

81.   private void prepareListener() { 
82.   listener1 = new OnClickListener() { 
83.   public void onClick(View v) { 
84.   CreateTable(); 
85.   } 
86.   }; 
87.   listener2 = new OnClickListener() { 
88.   public void onClick(View v) { 
89.   dropTable(); 
90.   } 
91.   }; 
92.   listener3 = new OnClickListener() { 
93.   public void onClick(View v) { 
94.   insertItem(); 
95.   } 
96.   }; 
97.   listener4 = new OnClickListener() { 
98.   public void onClick(View v) { 
99.   deleteItem(); 
100.   } 
101.   }; 
102.   listener5 = new OnClickListener() { 
103.   public void onClick(View v) { 
104.   showItems(); 
105.   } 
106.   }; 
107.   } 

108.   /* 
109.   * 重新建立数据表 
110.   */ 
111.   private void CreateTable() { 
112.   //mOpenHelper.getWritableDatabase() 语句负责得到一个可写的 SQLite 数据库,如果这个数据库还没有建立,  
113.   // 那么 mOpenHelper 辅助类负责建立这个数据库。如果数据库已经建立,那么直接返回一个可写的数据库。  
114.   SQLiteDatabase db = mOpenHelper.getWritableDatabase(); 
115.   String sql = "CREATE TABLE" + TABLE_NAME + "(" + TITLE 
116.   + "text not null," + BODY + "text not null" + ");"; 
117.   Log.i("haiyang:createDB=", sql); 

118.   try { 
119.   db.execSQL("DROP TABLE IF EXISTS diary"); 
120.   db.execSQL(sql); 
121.   setTitle("数据表成功重建"); 
122.   } catch (SQLException e) { 
123.   setTitle("数据表重建错误"); 
124.   } 
125.   } 

126.   /* 
127.   * 删除数据表 
128.   */ 
129.   private void dropTable() { 
130.   //mOpenHelper.getWritableDatabase() 语句负责得到一个可写的 SQLite 数据库,如果这个数据库还没有建立,  
131.   // 那么 mOpenHelper 辅助类负责建立这个数据库。如果数据库已经建立,那么直接返回一个可写的数据库。  
132.   SQLiteDatabase db = mOpenHelper.getWritableDatabase(); 
133.   String sql = "drop table" + TABLE_NAME; 
134.   try { 
135.   db.execSQL(sql); 
136.   setTitle("数据表成功删除:" + sql); 
137.   } catch (SQLException e) { 
138.   setTitle("数据表删除错误"); 
139.   } 
140.   } 

141.   /* 
142.   * 插入两条数据 
143.   */ 
144.   private void insertItem() { 
145.   //mOpenHelper.getWritableDatabase() 语句负责得到一个可写的 SQLite 数据库,如果这个数据库还没有建立,  
146.   // 那么 mOpenHelper 辅助类负责建立这个数据库。如果数据库已经建立,那么直接返回一个可写的数据库。  
147.   SQLiteDatabase db = mOpenHelper.getWritableDatabase(); 
148.   String sql1 = "insert into" + TABLE_NAME + "(" + TITLE + "," + BODY 
149.   + ") values('haiyang','android 的发展真是迅速啊');"; 
150.   String sql2 = "insert into" + TABLE_NAME + "(" + TITLE + "," + BODY 
151.   + ") values('icesky','android 的发展真是迅速啊');"; 
152.   try { 
153.   // Log.i()会将参数内容打印到日志当中,并且打印级别是 Info 级别  
154.   // Android 支持 5 种打印级别,分别是 Verbose、Debug、Info、Warning、Error,当然我们在程序当中一般用到的是 Info 级别  
155.   Log.i("haiyang:sql1=", sql1); 
156.   Log.i("haiyang:sql2=", sql2); 
157.   db.execSQL(sql1); 
158.   db.execSQL(sql2); 
159.   setTitle("插入两条数据成功"); 
160.   } catch (SQLException e) { 
161.   setTitle("插入两条数据失败"); 
162.   } 
163.   } 

164.   /* 
165.   * 删除其中的一条数据 
166.   */ 
167.   private void deleteItem() { 
168.   try { 
169.   //mOpenHelper.getWritableDatabase() 语句负责得到一个可写的 SQLite 数据库,如果这个数据库还没有建立,  
170.   // 那么 mOpenHelper 辅助类负责建立这个数据库。如果数据库已经建立,那么直接返回一个可写的数据库。  
171.   SQLiteDatabase db = mOpenHelper.getWritableDatabase(); 
172.   // 第一个参数是数据库表名,在这里是 TABLE_NAME,也就是 diary。  
173.   // 第二个参数,相当于 SQL 语句当中的 where 部分,也就是描述了删除的条件。  
174.   // 如果在第二个参数当中有 “?” 符号,那么第三个参数中的字符串会依次替换在第二个参数当中出现的 “?” 符号。  
175.   db.delete(TABLE_NAME, "title ='haiyang'", null); 
176.   setTitle("删除 title 为 haiyang 的一条记录"); 
177.   } catch (SQLException e) { 

178.   } 

179.   } 

180.   /* 
181.   * 在屏幕的 title 区域显示当前数据表当中的数据的条数。 
182.   */ 
183.   /* 
184.   * Cursor cur = db.query(TABLE_NAME, col, null, null, null, null, null) 语句将查询到的数据放到一个 Cursor 当中。 
185.   这个 Cursor 里边封装了这个数据表 TABLE_NAME 当中的所有条列。 
186.   query() 方法相当的有用,在这里我们简单地讲一下。 
187.   第一个参数是数据库里边表的名字,比如在我们这个例子,表的名字就是 TABLE_NAME,也就是 "diary"。 
188.   第二个字段是我们想要返回数据包含的列的信息。在这个例子当中我们想要得到的列有 title、body。我们把这两个列的名字放到字符串数组里边来。 
189.   第三个参数为 selection,相当于 SQL 语句的 where 部分,如果想返回所有的数据,那么就直接置为 null。 
190.   第四个参数为 selectionArgs。在 selection 部分,你有可能用到 “?”,那么在 selectionArgs 定义的字符串会代替 selection 中的 “?”。 
191.   第五个参数为 groupBy。定义查询出来的数据是否分组,如果为 null 则说明不用分组。 
192.   第六个参数为 having ,相当于 SQL 语句当中的 having 部分。 
193.   第七个参数为 orderBy,来描述我们期望的返回值是否需要排序,如果设置为 null 则说明不需要排序。 
194.   */ 

195.   private void showItems() { 

196.   SQLiteDatabase db = mOpenHelper.getReadableDatabase(); 
197.   String col[] = { TITLE, BODY}; 
198.   // 查询数据  
199.   Cursor cur = db.query(TABLE_NAME, col, null, null, null, null, null); 
200.   Integer num = cur.getCount(); 
201.   setTitle(Integer.toString(num) + "条记录"); 
202.   } 
203.  }

四. 内容提供器(Content provider)方式

在 Android 的设计 “哲学” 里是鼓励开发者使用内部类的,这样不但使用方便,而且执行效率也高。

1. 什么是 ContentProvider 
      数据在 Android 当中是私有的,当然这些数据包括文件数据和数据库数据以及一些其他类型的数据。难道两个程序之间就没有办法对于数据进行交换?解决这个问题主要靠 ContentProvider。
 一个 Content Provider 类实现了一组标准的方法接口,从而能够让其他的应用保存或读取此 Content Provider 的各种数据类型。也就是说,一个程序可以通过实现一个 Content Provider 的抽象接口将自己的数据暴露出去。外界根本看不到,也不用看到这个应用暴露的数据在应用当中是如何存储的,或者是用数据库存储还是用文件存储,还是通过网上获得,这些一切都不重要,重要的是外界可以通过这一套标准及统一的接口和程序里的数据打交道,可以读取程序的数据,也可以删除程序的数据,当然,中间也会涉及一些权限的问题。
      下边列举一些较常见的接口,这些接口如下所示。
      query(Uri uri, String[] projection, String selection, String[] selectionArgs,String sortOrder):通过 Uri 进行查询,返回一个 Cursor。 
      insert(Uri url, ContentValues values):将一组数据插入到 Uri 指定的地方。 
      update(Uri uri, ContentValues values, String where, String[] selectionArgs):更新 Uri 指定位置的数据。 
      delete(Uri url, String where, String[] selectionArgs):删除指定 Uri 并且符合一定条件的数据。

2. 什么是 ContentResolver       外界的程序通过 ContentResolver 接口可以访问 ContentProvider 提供的数据,在 Activity 当中通过 getContentResolver() 可以得到当前应用的 ContentResolver 实例。
      ContentResolver 提供的接口和 ContentProvider 中需要实现的接口对应,主要有以下几个。 
      query(Uri uri, String[] projection, String selection, String[] selectionArgs,String sortOrder):通过 Uri 进行查询,返回一个 Cursor。
      insert(Uri url, ContentValues values):将一组数据插入到 Uri 指定的地方。
      update(Uri uri, ContentValues values, String where, String[] selectionArgs):更新 Uri 指定位置的数据。
      delete(Uri url, String where, String[] selectionArgs):删除指定 Uri 并且符合一定条件的数据。

3.ContentProvider 和 ContentResolver 中用到的 Uri 
      在 ContentProvider 和 ContentResolver 当中用到了 Uri 的形式通常有两种,一种是指定全部数据,另一种是指定某个 ID 的数据。
     我们看下面的例子。 
          content://contacts/people/  这个 Uri 指定的就是全部的联系人数据。
          content://contacts/people/1 这个 Uri 指定的是 ID 为 1 的联系人的数据。

在上边两个类中用到的 Uri 一般由 3 部分组成。
         第一部分是:“content://” 。
         第二部分是要获得数据的一个字符串片段。 
        最后就是 ID(如果没有指定 ID,那么表示返回全部)。

由于 URI 通常比较长,而且有时候容易出错,且难以理解。所以,在 Android 当中定义了一些辅助类,并且定义了一些常量来代替这些长字符串的使用,例如下边的代码: 
     Contacts.People.CONTENT_URI (联系人的 URI)。
     1)实现的功能

在这个例子里边,首先在系统的联系人应用当中插入一些联系人信息,然后把这些联系人的名字和电话再显示出来

2)实现方法

package com.contentProvider; 

3.  import android.app.ListActivity; 
4.  import android.database.Cursor; 
5.  import android.os.Bundle; 
6.  import android.provider.Contacts.Phones; 
7.  import android.widget.ListAdapter; 
8.  import android.widget.SimpleCursorAdapter; 

9.  public class ContentProviderDemo extends ListActivity { 

10.   protected void onCreate(Bundle savedInstanceState) { 
11.   super.onCreate(savedInstanceState); 
12.   //getContentResolver() 方法得到应用的 ContentResolver 实例。  
13.   // query(Phones.CONTENT_URI, null, null, null, null)。它是 ContentResolver 里的方法,负责查询所有联系人,并返回一个 Cursor。这个方法参数比较多,每个参数的具体含义如下。  
14.   //·  第一个参数为 Uri,在这个例子里边这个 Uri 是联系人的 Uri。  
15.   //·  第二个参数是一个字符串的数组,数组里边的每一个字符串都是数据表中某一列的名字,它指定返回数据表中那些列的值。  
16.   //·  第三个参数相当于 SQL 语句的 where 部分,描述哪些值是我们需要的。  
17.   //·  第四个参数是一个字符串数组,它里边的值依次代替在第三个参数中出现的 “?” 符号。  
18.   //·  第五个参数指定了排序的方式。  
19.   Cursor c = getContentResolver().query(Phones.CONTENT_URI, null, null, null, null); 
20.   startManagingCursor(c); // 让系统来管理生成的 Cursor。  
21.   ListAdapter adapter = new SimpleCursorAdapter( 
22.   this, 
23.   android.R.layout.simple_list_item_2, 
24.   c, 
25.   new String[] { Phones.NAME, Phones.NUMBER}, 
26.   new int[] { android.R.id.text1, android.R.id.text2}); 
27.   setListAdapter(adapter); // 将 ListView 和 SimpleCursorAdapter 进行绑定。  
28.   } 

29.  } 

五. 网络存储方式

1.例子介绍
         通过邮政编码查询该地区的天气预报,以 POST 发送的方式发送请求到 webservicex.net 站点,访问 WebService.webservicex.net 站点上提供查询天气预报的服务,具体信息请参考其 WSDL 文档,网址为:http://www.webservicex.net/WeatherForecast.asmx?WSDL
输入:美国某个城市的邮政编码。 
输出:该邮政编码对应城市的天气预报。
2.实现步骤如下
(1)如果需要访问外部网络,则需要在 AndroidManifest.xml 文件中加入如下代码申请权限许可: 

 

 
(2)以 HTTP POST 的方式发送(注意:SERVER_URL 并不是指 WSDL 的 URL,而是服务本身的 URL)。实现的代码如下所示:

private static final String SERVER_URL = "[http://www.webservicex.net/WeatherForecast](http://www.webservicex.net/WeatherForecast). asmx/GetWeatherByZipCode"; // 定义需要获取的内容来源地址  
HttpPost request = new HttpPost(SERVER_URL); // 根据内容来源地址创建一个 Http 请求  
// 添加一个变量   
List <NameValuePair> params = new ArrayList <NameValuePair>();   
// 设置一个华盛顿区号  
params.add(new BasicNameValuePair("ZipCode", "200120"));  // 添加必须的参数  
request.setEntity(new UrlEncodedFormEntity(params, HTTP.UTF_8)); // 设置参数的编码  
try {   
HttpResponse httpResponse = new DefaultHttpClient().execute(request); // 发送请求并获取反馈  
// 解析返回的内容  
if(httpResponse.getStatusLine().getStatusCode() != 404)    
{   
  String result = EntityUtils.toString(httpResponse.getEntity());   
  Log.d(LOG_TAG, result);   
}   
} catch (Exception e) {   
Log.e(LOG_TAG, e.getMessage());   
}   

 代码解释:
如上代码使用 Http 从 webservicex 获取 ZipCode 为 “200120”(美国 WASHINGTON D.C)的内容,其返回的内容如下:

<WeatherForecasts xmlns:xsd="[http://www.w3.org/2001/XMLSchema](http://www.w3.org/2001/XMLSchema)"xmlns:xsi="http: //www.w3.org/2001/XMLSchema-instance"xmlns="[http://www.webservicex.net](http://www.webservicex.net/)">   
  <Latitude>38.97571</Latitude>   
  <Longitude>77.02825</Longitude>   
  <AllocationFactor>0.024849</AllocationFactor>   
  <FipsCode>11</FipsCode>   
  <PlaceName>WASHINGTON</PlaceName>   
  <StateCode>DC</StateCode>   
  <Details>   
    <WeatherData>   
      <Day>Saturday, April 25, 2009</Day>   
      <WeatherImage>http://forecast.weather.gov/images/wtf/sct.jpg</WeatherImage>   
      <MaxTemperatureF>88</MaxTemperatureF>   
      <MinTemperatureF>57</MinTemperatureF>   
      <MaxTemperatureC>31</MaxTemperatureC>   
      <MinTemperatureC>14</MinTemperatureC>   
    </WeatherData>   
    <WeatherData>   
      <Day>Sunday, April 26, 2009</Day>   
      <WeatherImage>http://forecast.weather.gov/images/wtf/few.jpg</WeatherImage>   
      <MaxTemperatureF>89</MaxTemperatureF>   
      <MinTemperatureF>60</MinTemperatureF>   
      <MaxTemperatureC>32</MaxTemperatureC>   
      <MinTemperatureC>16</MinTemperatureC>   
    </WeatherData>  
…  
  </Details>   
</WeatherForecasts>