官术网_书友最值得收藏!

2.3 手機自帶數據庫——SQLite

上一節介紹了如何使用Preferences存儲簡單數據,而復雜的數據就需要存儲到文件或數據庫中了。Android自帶了一款輕量級的關系數據庫—SQLite,其具有體積小,功能強大等特點,成為嵌入式設備首選的數據庫系統。本節將帶領讀者走進SQLite的世界,學習如何應用SQLite數據庫進行數據的增、刪、改、查等基本操作。

2.3.1 初識SQLite

SQLite是一款滿足ACID特性的具有完備功能的關系數據庫系統,由于其設計目標為輕量級、開源、支持嵌入式使用,因此,目前已經在嵌入式設備領域被廣泛采用。其運行需要的系統資源非常少,在嵌入式設備中可能只需要幾百KB的內存就夠了。

SQLite對主流編程語言的支持也非常全面,如C#、PHP、Java等,同時還支持ODBC接口。另外,SQLite的性能也是一流的,在一般應用情況下,其處理速度比MySQL、PostgreSQL這兩款著名的開源數據庫管理系統都快。

提示

SQLite的最新版本為3.8.11.1,發布時間是2015年7月29日。其官方網站為:http://www.sqlite.org或者http://www.sqlite.com.cn,讀者可以在該網站上獲取SQLite的源代碼和相關文檔。

雖然SQLite占用的資源非常少,但是其功能、特性與服務器級數據庫相比卻絲毫不差,這也是SQLite能夠受到Android系統青睞的主要原因,其部分特性如下所列。

? 最大可以支持2TB的數據庫文件。

? 占用資源少,一般占用250KB左右。

? API非常簡單,易于使用。

? 沒有任何額外的依賴,是獨立的。

? 源代碼完全開放,可以用于任何用途。

Android系統中很多的用戶數據都存儲在SQLite數據庫中,如聯系人信息、通話記錄、短信等,由此可見SQLite對于Android的重要性。

提示

讀者要想很好地使用SQLite數據庫,必須熟練掌握SQL語言。這是由于SQL已經事實上成為關系數據庫操作的標準語言,市面上的關系數據庫幾乎無一例外都支持SQL。因此,在數據庫領域,有這樣一句話“學好SQL,走遍天下都不怕”。

2.3.2 SQLite數據庫的基本操作

一般學習數據庫相關課程的時候,首先介紹的就是數據庫的一些基本操作,如數據的增、刪、改、查等。按照慣例,本書也首先簡單介紹SQLite數據庫的創建、關閉及數據的增加、刪除、修改、查詢等基本操作,具體如下所列。

提示

想在Android下通過Java編程對SQLite數據庫進行操作,就必須要用到android.database.sqlite包下的SQLiteDatabase類,該類提供了對SQLite數據庫進行基本操作的所有重要方法。

? 創建數據庫。

創建數據庫需要用到的是openDatabase方法,此方法簽名為“public static SQLiteDatabase openDatabase(String path, SQLiteDatabase.CursorFactory factory, int flags)”。其中path為數據庫所在的路徑;factory為游標工廠;flags為標志,可以用來控制數據庫的訪問模式。

? 關閉數據庫。

關閉數據庫需要用到的是close方法,此方法簽名為“public void close()”。在實際開發中數據庫使用完畢后,一定不要忘記使用該方法關閉數據庫,以防止資源的浪費。

? 插入數據。

插入數據可以使用insert方法,此方法簽名為“public long insert(String table, String nullColumnHack, ContentValues values)”。其中table為待插入的表名,nullColumnHack通常設置為null, values為待插入的數據。

? 更新數據。

更新數據可以使用update方法,其簽名為“public int update(String table, ContentValues values, String whereClause, String[] whereArgs)”。其中table為待更新的表名;values為待更新內容;whereClause為where子句的內容,用來進行記錄篩選;whereArgs為where子句的參數。

? 刪除數據。

刪除數據可以使用delete方法,其簽名為“public int delete(String table, String whereClause, String[] whereArgs)”。其中table為要操作的表名;whereClause為where子句的內容,用來進行記錄篩選;whereArgs為where子句的參數。

? 查詢數據。

查詢數據可以使用query方法,其方法簽名為“public Cursor query(String table, String[] columns, String selection, String[] selectionArgs, String groupBy, String having, String orderBy)”。其中table為要查詢的表,columns為要查詢的列,selection為過濾記錄的子句,selectionArgs為過濾的參數值,groupBy為分組子句,having為過濾分組的子句,orderBy為記錄排序子句。

提示

Android中被重載了的query方法有多個變體。這里由于篇幅所限,不再贅述,有需要的讀者可以自行查閱API或其他相關資料。

? 執行非查詢SQL。

對于不太熟悉SQL語言的初學者而言,插入、更新、刪除數據可以用前面介紹的insert、update、delete方法。但對于熟練掌握SQL的開發人員而言,使用execSQL方法直接執行相應的SQL語句十分方便。

此方法簽名為“public void execSQL(String sql)”或“public void execSQL(String sql, Object[] bindArgs)”。其中sql為需要執行的SQL語句,bindArgs為帶參數SQL語句的參數值數組。

提示

需要注意的是,此方法僅支持執行非查詢的SQL語句,如CREATE TABLE、DELETE、 INSERT、UPDATE等,不能用于執行SELECT語句。

? 執行查詢SQL。

對于熟練掌握SQL的開發人員而言,會覺得前面介紹的query方法使用過于繁瑣。Android的設計人員也考慮到了這個問題,提供了支持執行SQL查詢語句的rawQuery方法,其方法簽名為“public Cursor rawQuery(String sql, String[] selectionArgs)”。其中sql為要執行的SQL查詢語句(可以帶參數), selectionArgs為查詢參數的值。

提示

SQLiteDatabase類中用于數據庫操作的方法還有很多,本書只是介紹了其中一些常用的,若讀者有需要可以查閱API或其他相關資料進一步學習。

2.3.3 SQLite數據庫的簡單案例

上一小節介紹了SQLite數據庫的基本操作方法,本小節將詳細介紹一個使用SQLite數據庫的簡單案例,以使讀者可以更加快速地掌握SQLite數據庫的使用方法,從而在開發中進行合理地使用。本案例運行效果分別如圖2-10、圖2-11和圖2-12所示。

▲圖2-10 創建數據庫

▲圖2-11 插入記錄

▲圖2-12 查詢記錄

介紹完本案例的運行效果后,接下來將開發本案例中唯一的一個類—Sample2_4_Activity,其代碼如下。

代碼位置:見隨書中源代碼/第2章/Sample2_4/src/com/bn/pp4目錄下的Sample2_4_Activity.java。

      1       package  com.bn.pp4;
      2     ……//此處省略了部分類的引入代碼,讀者可自行查看隨書的源代碼
      3       public  class  Sample2_4_Activity  extends  Activity  {
      4           SQLiteDatabase sld; // 聲明SQLiteDatabase引用
      5               @Override
      6           public void onCreate(Bundle savedInstanceState) {    // onCreate方法
      7                       super.onCreate(savedInstanceState);
      8                 setContentView(R.layout.main);                    //跳轉到主界面
      9                       Button  b  =  (Button)  this.findViewById(R.id.Button01);
                                                              //獲取打開/創建數據庫按鈕的引用
      10                b.setOnClickListener(                    //為打開/創建按鈕添加監聽器
      11                     new  OnClickListener()  {
      12                              @Override
      13                              public  void  onClick(View  v)  {
      14                            createOrOpenDatabase();     //調用方法打開或創建數據庫
      15                     }});
      16                b = (Button) this.findViewById(R.id.Button02); //獲取關閉數據庫按鈕的引用
      17                b.setOnClickListener(                    //為關閉按鈕添加監聽器
      18                     new  OnClickListener()  {
      19                              @Override
      20                              public  void  onClick(View  v)  {
      21                            closeDatabase();              //調用方法關閉數據庫
      22                     }});
      23                b = (Button) this.findViewById(R.id.Button03); //獲取添加記錄按鈕的引用
      24                b.setOnClickListener(                    //為添加按鈕添加監聽器
      25                     new  OnClickListener()  {
      26                              @Override
      27                              public  void  onClick(View  v)  {
      28                            insert();                      //調用方法插入記錄
      29                     }});
      30                b = (Button) this.findViewById(R.id.Button04); //獲取刪除記錄按鈕的引用
      31                b.setOnClickListener(                    //為刪除按鈕添加監聽器
      32                     new  OnClickListener()  {
      33                              @Override
      34                              public  void  onClick(View  v)  {
      35                            delete();                      //調用方法刪除記錄
      36                     }});
      37                b = (Button) this.findViewById(R.id.Button05); //獲取查詢記錄按鈕的引用
      38                b.setOnClickListener(                    //為查詢按鈕添加監聽器
      39                     new  OnClickListener()  {
      40                              @Override
      41                              public  void  onClick(View  v)  {
      42                            query();                       //調用方法查詢記錄
      43             }}); }
      44          public void createOrOpenDatabase() {         //創建或打開數據庫的方法
      45                     try  {
      46                              sld  =  SQLiteDatabase.openDatabase(
      47                                  "/data/data/com.bn.pp4/mydb",        //數據庫所在路徑
      48                                  null,                    //游標工廠,默認為null
      49                                              SQLiteDatabase.OPEN_READWRITE  |
      50                                  SQLiteDatabase.CREATE_IF_NECESSARY //模式為讀寫,若不存在則創建
      51                      );                                    //生成創建數據庫的SQL語句
      52                              String  sql  =  "create  table  if  not  exists  student"  +
      53                                              "(sno  char(5), stuname  varchar(20), "  +
      54                                              "sage  integer, sclass  char(5))";
      55                      sld.execSQL(sql);                  //執行SQL語句
      56                      Toast.makeText(getBaseContext(), "成功創建數據庫。",
      57                                              Toast.LENGTH_LONG).show();
      58                     }  catch  (Exception  e)  {
      59                              e.printStackTrace();
      60             }}
      61          public void closeDatabase() {                 //關閉數據庫的方法
      62                     try  {
      63                      sld.close();                        //關閉數據庫
      64                      Toast.makeText(getBaseContext(), "成功關閉數據庫。",
      65                                              Toast.LENGTH_LONG).show();
      66                     }  catch  (Exception  e)  {
      67                              e.printStackTrace();
      68             }}
      69          public void insert() {                         //插入記錄的方法
      70                try  {                                      //生成插入記錄的SQL語句
      71                              String  sql  =  "insert  into  student  values"  +
      72                                              "('001', 'Android',22, '283')";
      73                      sld.execSQL(sql);                  //執行SQL語句
      74                      Toast.makeText(getBaseContext(), "成功插入一條記錄。",
      75                                               Toast.LENGTH_LONG).show();
      76                     }  catch  (Exception  e)  {
      77                              e.printStackTrace();
      78             }}
      79          public void delete() {                         //刪除記錄的方法
      80                try  {                                      //生成刪除所有記錄的SQL語句
      81                              String  sql  =  "delete  from  student; ";
      82                      sld.execSQL(sql);                  //執行SQL語句
      83                      Toast.makeText(getBaseContext(), "成功刪除所有記錄。",
      84                                              Toast.LENGTH_LONG).show();
      85                     }  catch  (Exception  e)  {
      86                              e.printStackTrace();
      87             }}
      88          public void query(){                           //查詢的方法
      89                try {                                      //生成查詢記錄的SQL語句
      90                              String  sql  =  "select  *  from  student  where  sage>? ";
      91                              Cursor  cur  =  sld.rawQuery(sql,  new  String[]  {  "20"  });
                                                              //獲取Cursor對象引用
      92                      while (cur.moveToNext()) {        //若存在記錄
      93                            String sno = cur.getString(0);       //獲取第一列信息
      94                            String sname = cur.getString(1);     //獲取第二列信息
      95                            int sage = cur.getInt(2);             //獲取第三列信息
      96                            String sclass = cur.getString(3);    //獲取第四列信息
      97                                      Toast.makeText(
      98                                                      getBaseContext(),
      99                                        "查詢到的記錄為:'" + sno + "'\t'" + sname
      100                                                    +  "'\t\t'"  +  sage+  "'\t'"  +  sclass  +  "'",
      101                                                    Toast.LENGTH_LONG).show();
      102                            }
      103                     cur.close();                                  //關閉Cursor
      104                    }  catch  (Exception  e)  {
      105                            e.printStackTrace();
      106    }}}

? 第9-43行為案例中的各個按鈕添加監聽器,監聽器中調用對應的方法來實現數據庫的打開/創建、關閉、插入、刪除和查詢等操作。

? 第43-60行為創建及打開數據庫的方法,方法中首先獲取了SQLiteDatabase對象的引用,并為其指定數據庫的存儲路徑和讀寫模式,然后用“create table”語句創建了一張名稱為student的表。

? 第69-78行為向數據庫中插入一條記錄的方法,插入的記錄內容為“'001', 'Android',22, '283'”。

? 第79-87行為刪除數據庫中所有記錄的方法。

? 第88-106行為從數據庫中查找符合條件記錄的方法,首先需要獲取Cursor對象的引用,并為其添加查找范圍(具體范圍為年齡大于20)。若查到相應記錄,則將該記錄信息用Toast顯示出來。

2.3.4 使用ContentProvider組件共享數據

前一小節介紹了SQLite數據庫中的一些操作,但有時數據庫中的信息不但創建其的應用程序要使用,還希望能夠分享給其他應用程序使用。這時就需要使用ContentProvider組件了,ContentProvider組件的基本情況如下所列。

? Android平臺中每個應用程序都有自己的用戶ID并在自己的進程中運行,每個進程擁有獨立的運行環境,這樣可以保證程序的完整性。但這也使得應用程序在需要進行資源共享和數據通信時很不方便。為了解決這一問題,Android提供了專門用來在應用程序之間分享數據的ContentProvider組件。

? ContentProvider能將應用程序中特定的數據提供給其他應用程序使用,這些數據可以來自應用程序私有文件夾下的私有數據文件,也可以來自應用程序自己私有的SQLite數據庫。當然,數據的來源還有很多其他選擇,ContentProvider組件本身并沒有做出限制,讀者可以充分發揮想象的空間。

? 使用ContentProvider組件共享數據的基本方式是繼承ContentProvider類并重寫其中的相應方法,具體情況在后面的案例中進行介紹。

? 別的應用程序想分享數據時需要使用ContentResolver,通過ContentResolver對象將需要分享數據的請求發送給ContentProvider組件,而不能直接調用ContentProvider組件。

下面使用ContentProvider組件將上一小節的案例進行升級,使得此案例具有分享數據給其他應用程序的能力,其具體開發步驟如下。

(1)在案例Sample2_4的com/bn/pp4包下創建MyContentProvider類,該類繼承自ContentProvider類,并要實現其中所有的抽象方法,具體代碼如下。

代碼位置:見隨書中源代碼/第2章/Sample2_4/src/com/bn/pp4目錄下的MyContentProvider.java。

      1       package  com.bn.pp4;
      2     ……//此處省略了部分類的引入代碼,讀者可自行查看隨書的源代碼
      3     public class MyContentProvider extends ContentProvider {  //繼承ContentProvider
      4           private static final UriMatcher um;               //聲明Uri匹配引用
      5               static  {
      6                 um = new UriMatcher(UriMatcher.NO_MATCH);  //創建UriMatcher
      7                 um.addURI("com.bn.pp4.provider.student", "stu", 1); //設置匹配字符串
      8               }
      9           SQLiteDatabase sld;                                 //聲明SQLiteDatabase引用
      10             @Override
      11             public  String  getType(Uri  uri)  {
      12                     return  null;
      13             }
      14          @Override  //調用數據庫的query方法時會自動調用該方法
      15             public  Cursor  query(Uri  uri,  String[]  projection,  String  selection,
      16                              String[]  selectionArgs,  String  sortOrder)  {
      17                switch (um.match(uri)) {                //若匹配成功
      18                case 1:                                    //執行操作,獲取Cursor對象引用
      19                              Cursor  cur  =  sld.query("student",  projection,  selection,
      20                                              selectionArgs,  null,  null,  sortOrder);
      21                      return cur;                         //返回Cursor對象引用
      22                     }
      23                     return  null;
      24             }
      25             @Override
      26          public int delete(Uri arg0, String arg1, String[] arg2) {    //空實現
      27                     return  0;
      28             }
      29             @Override
      30          public Uri insert(Uri uri, ContentValues values) {             //空實現
      31                     return  null;
      32             }
      33             @Override
      34          public boolean onCreate() {                        //創建數據庫時自動調用該方法
      35                     sld  =  SQLiteDatabase.openDatabase(
      36                            "/data/data/com.bn.pp4/mydb",    //數據庫所在路徑
      37                            null,                               //游標工廠,默認為null
      38                                      SQLiteDatabase.OPEN_READWRITE|
      39                            SQLiteDatabase.CREATE_IF_NECESSARY //讀寫、若不存在則創建
      40                     );
      41                     return  false;
      42             }
      43             @Override
      44             public  int  update(Uri  uri,  ContentValues  values,  String  selection,
      45                      String[] selectionArgs) {              //空實現
      46                     return  0;
      47     }}

? 第4-8行為聲明Uri匹配對象,并且設置匹配字符串。此匹配字符串在需要得到分享數據的應用程序中提供給ContentResolver使用,以進行配對。

? 第11-13行重寫了getType方法,本案例中對getType方法沒有要求,因此其返回空值。

? 第15-24行重寫了query方法,在匹配成功后,數據需求方通過ContentResolver調用此方法查詢需要的數據。

? 第26-32行重寫了delete與insert方法,本案例中對這兩個方法沒有要求,因此都設置為返回空值。

? 第34-42行重寫了onCreate方法,其功能為首先獲取SQLiteDatabase對象引用,然后創建或打開數據庫,為信息的分享做好準備。

? 第44-47行重寫了update方法,本案例中對這個方法沒有要求,因此設置為返回空值。

(2)僅僅是完成上面的代碼還是不夠的,在Android程序開發中,有一個很重要的配置文件AndroidManifest.xml。要想使用ContentProvider組件,在完成代碼的開發后,還必須在該配置文件中進行相應的配置,將如下代碼插入到AndroidManifest.xml文件中的application標簽中。

代碼位置:見隨書中源代碼/第2章/Sample2_4目錄下的AndroidManifest.xml。

      1       <provider
      2          android:name="MyContentProvider"              <! --將調用的類名-->
      3          android:authorities="com/bn/pp4.provider.student"  <! --要匹配的Uri字符串-->
      4               android:exported="true"/>

2.3.5 使用ContentResolver獲取分享數據

升級完了Sample2_4使其具有了數據分享能力之后,就可以在別的應用程序中通過ContentResolver匹配到Sample2_4案例中的ContentProvider組件獲取分享的數據了。具體的開發步驟如下所列。

(1)創建項目Sample2_4_From,將項目的包名設定為com.bn.pp4f,并創建一個繼承自Activity的類ContentConsumerActivity,其代碼如下。

代碼位置:見隨書中源代碼/第2章/ Sample2_4_From/src/com/bn/pp4f目錄下的Content ConsumerActivity.java。

      1     package com.bn.pp4f;                                                //包聲明
      2     import android.app.Activity;                                       //相關類的引入
      3     //……此處省略了部分相關類的引入代碼,讀者可自行查看隨書的源代碼
      4     import android.widget.EditText;                                    //相關類的引入
      5       public  class  ContentConsumerActivity  extends  Activity  {
      6         ContentResolver cr;                      // ContentResolver的引用
      7         @Override                                                         //重寫方法的標志
      8            public  void  onCreate(Bundle  savedInstanceState)  {
      9             super.onCreate(savedInstanceState);             //繼承父類的onCreate方法
      10            setContentView(R.layout.main);                   //跳轉到主界面
      11            cr=this.getContentResolver();                    //獲取ContentResolver的對象
      12            //初始化查詢按鈕
      13            Button b=(Button)this.findViewById(R.id.Button01);      //Button類的引用
      14            b.setOnClickListener(                                       //設置按鈕監聽
      15                  new  OnClickListener(){
      16                      @Override                                 //重寫方法的標志
      17                      public void onClick(View v) {          //重寫onClick方法
      18                            String stuname="Android";        //設置查詢的字符串
      19                                      Cursor  cur=cr.query(
      20                                          Uri.parse("content://com.bn.pp4.provider.student/stu"),
      21                                          new  String[]{"sno", "stuname", "sage", "sclass"},
      22                               "stuname=? ",                    //查詢條件
      23                                          new  String[]{stuname},
      24                                          "sage  ASC"
      25                                      );
      26                              while(cur.moveToNext()){
      27                            String sno=cur.getString(0);               //獲取學號
      28                            String sname=cur.getString(1);             //獲取名稱
      29                            int sage=cur.getInt(2);                     //獲取年齡
      30                            String sclass=cur.getString(3);           //獲取班級
      31                                      appendMessage(sno+"\t"+sname+"\t\t"+sage+"\t"+sclass);
      32                              }
      33                      cur.close();                             //關閉ContentResolver
      34             }}); }
      35        public void appendMessage(String msg){             //向文本區中添加文本
      36                     EditText  et=(EditText)this.findViewById(R.id.EditText02);
                                                                  //獲取EditText的對象
      37                et.append(msg+"\n");                          //添加顯示的字符串
      38     }}

? 第8-25行主要功能為獲取ContentResolver對象的引用,并給按鈕添加監聽器,使得按鈕按下后可以通過ContentResolver匹配到Sample2_4案例中的ContentProvider組件獲取需要的數據。

? 第26-33行功能為將獲取的Sample2_4案例分享的數據顯示到屏幕上的EditText控件中。

? 第35-38行為向EditText控件中添加文本信息的方法。

(2)Sample2_4_From案例開發完成后,運行該案例,其效果如圖2-13和圖2-14所示。

▲圖2-13 運行界面1

▲圖2-14 運行界面2

說明

圖2-13為運行該案例后的界面效果圖,圖2-14為單擊“獲取”按鈕后,通過ContentResolver匹配到Sample2_4案例中的ContentProvider組件獲取數據后的效果圖。

主站蜘蛛池模板: 南通市| 临夏县| 兴安县| 镇安县| 新巴尔虎左旗| 寿阳县| 龙口市| 海阳市| 宜章县| 南涧| 大连市| 上饶市| 华容县| 贡嘎县| 若羌县| 西宁市| 宁城县| 池州市| 莒南县| 峨山| 沂水县| 筠连县| 德钦县| 渭南市| 浠水县| 滨州市| 阿鲁科尔沁旗| 卢龙县| 都兰县| 青神县| 三河市| 准格尔旗| 黔东| 津市市| 新郑市| 奉新县| 虹口区| 财经| 华宁县| 郓城县| 奎屯市|