綁定帳號登入

Android 台灣中文網

打印 上一主題 下一主題

[教程] 高煥堂(2)講解 ContentProvider範例

[複製連結] 查看: 3113|回覆: 0|好評: 0
跳轉到指定樓層
樓主
暗桌之光 | 收聽TA | 只看該作者 回帖獎勵 |倒序瀏覽 |閱讀模式
發表於 2011-8-21 14:22

馬上加入Android 台灣中文網,立即免費下載應用遊戲。

您需要 登錄 才可以下載或查看,沒有帳號?註冊

x
1.  何謂Android的嫡系組件

    Android有4項一等公民(或稱為嫡系親屬),包括:Activity、ContentProvider、IntentReceiver與Service。它們都必須宣告於AndroidManifest.xml檔案裡,如下:
  1. <?xml version="1.0" encoding="utf-8"?>
  2. <manifest xmlns:android="http://schemas.android.com/apk/res/android"
  3.     package="com.misoo.SQ03">
  4.     <uses-permission xmlns:android="http://schemas.android.com/apk/res/android"  
  5.          android:name="android.permission.INTERNET">
  6.     </uses-permission>
  7.     <application android:icon="@drawable/icon" android:label="@string/app_name">
  8.         <provider android:name="DataProvider"
  9.             android:authorities="com.misoo.provider.SQ03">
  10.         </provider>
  11.         <activity android:name=".ac01" 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.         <activity android:name=".DispActivity" android:label="DispActivity">
  18.         </activity>
  19.     </application>
  20. </manifest>
複製代碼
這讓Android知道我們城市裡定義了多少個嫡系組件類別;Android可以在啟動時就將它們執行起來,成為共享的(Shared)服務組件。這些嫡系服務組件間的溝通,通常是透過「意圖」(Intent)對像來請Android轉達給對方,Android則會依據意圖而找出最佳的配對。配對成功,就展開相互的溝通與服務了。

2.   什麼是ContentProvider嫡系組件
---- 以SQLite為例

    在Android裡,SQLite數據庫是最典型的ContentProvider,負責儲存各式各樣的內容。除了數據庫之外,還有許多其它種類的ContentProvider。在這裡並不是要介紹這些ContentProvider,而是要透過SQLite認識ContentProvider接口,然後將舶來Linter組件,配上這種ContentProvider接口,讓它搖身一變成為Android的嫡系組件。


2.1  一般(即非嫡系)SQLite的範例
 
      沒有透過ContentProvider接口來使用SQLite,就是對SQLite的「非嫡系」用法。此時,應用程式透過JDBC接口和SQL語句來與SQLite溝通,以存取數據庫裡的內容。先認識這種傳統用法。此範例將從SQLite讀取數據。首先建立一個程式項目,其含有兩個Java程式文件:ac01.java和DataProvider.java。其中,ac01.java 是典型的Activity類別,負責UI畫面的顯示工作,而DataProvider則負責與SQLite溝通。其詳細程式代碼為:
  1. /* ----- ac01.java 程式代碼 ------*/
  2. package com.misoo.pklx;
  3. import java.util.ArrayList;
  4. import java.util.HashMap;
  5. import java.util.Map;
  6. import android.app.ListActivity;
  7. import android.database.Cursor;
  8. import android.os.Bundle;
  9. import android.view.View;
  10. import android.widget.ListView;
  11. import android.widget.SimpleAdapter;

  12. public class ac01 extends ListActivity {
  13.     private static final String[] PROJECTION = new String[] { "stud_no", "stud_name" };

  14.     @Override protected void onCreate(Bundle savedInstanceState) {
  15.        super.onCreate(savedInstanceState);
  16.         DataProvider dp = new DataProvider(this);
  17.         Cursor cur = dp.query(PROJECTION, null, null, null);
  18.         ArrayList<Map<String, Object>> coll
  19.                 = new ArrayList<Map<String, Object>>();
  20.         Map<String, Object> item;
  21.         cur.moveToFirst();
  22.         while(!cur.isAfterLast()) {
  23.           item = new HashMap<String, Object>();
  24.           item.put("c1", cur.getString(0) + ",  " + cur.getString(1));
  25.           coll.add(item);
  26.           cur.moveToNext();
  27.         }
  28.         dp.close();
  29.         this.setListAdapter(new SimpleAdapter(this, coll,
  30.               android.R.layout.simple_list_item_1, new String[] { "c1" },
  31.               new int[] {android.R.id.text1}));
  32.     }
  33.     @Override
  34.     protected void onListItemClick(ListView l, View v, int position, long id) {
  35.               finish();
  36. }}
複製代碼
指令:
  1. DataProvider dp = new DataProvider(this);
複製代碼
這和一般類別之用法是一樣的。ac01對像指名要誕生一個DataProvider的物件。然後呼叫它,如下指令:
          Cursor cur = dp.query(PROJECTION, null, null, null);

這要求SQLite從數據庫查詢出某些數據。詳細的DataProvider.java程式代碼如下:
  1. /* ----- DataProvider.java 程式代碼 ------*/
  2. package com.misoo.pklx;
  3. import android.content.Context;
  4. import android.database.Cursor;
  5. import android.database.SQLException;
  6. import android.database.sqlite.SQLiteDatabase;
  7. import android.util.Log;

  8. public class DataProvider {
  9.     private static final String DATABASE_NAME = "StudDB";
  10.     private static final String TABLE_NAME = "Student";
  11.     private final int DB_MODE = Context.MODE_PRIVATE;
  12.     private SQLiteDatabase db=null;
  13.          
  14.     public DataProvider(Context ctx) {
  15.        try {  db = ctx.openOrCreateDatabase(DATABASE_NAME, DB_MODE, null);   }
  16.         catch (Exception e) {  Log.e("ERROR", e.toString());   return;   }
  17.        
  18.        try { db.execSQL("drop table "+ TABLE_NAME); }
  19.         catch (Exception e) {  Log.e("ERROR", e.toString());   }
  20.        
  21.        db.execSQL("CREATE TABLE " + TABLE_NAME + " ("  + "stud_no" + " TEXT,"
  22.                             + "stud_name" + " TEXT" + ");");
  23.           String sql_1 = "insert into "+ TABLE_NAME +
  24.         " (stud_no, stud_name) values('S101', 'Lily');";
  25.          String sql_2 = "insert into " + TABLE_NAME +
  26.         " (stud_no, stud_name) values('S102', 'Linda');";
  27.           String sql_3 = "insert into " + TABLE_NAME +
  28.         " (stud_no, stud_name) values('S103', 'Bruce');";
  29.        
  30.            try {  db.execSQL(sql_1);   db.execSQL(sql_2);  db.execSQL(sql_3); }
  31.          catch (SQLException e) {  Log.e("ERROR", e.toString());  return;  }
  32.            }
  33.     public Cursor query(String[] projection, String selection, String[] selectionArgs,
  34.                               String sortOrder) {
  35.               Cursor cur = db.query(TABLE_NAME, projection, null, null, null, null, null);
  36.                return cur;
  37.     }
  38.     public void close(){   db.close();   }
  39. }
複製代碼
這種用法屬於非嫡系的用法:在ac01.java程式代碼裡,其指令:
  1.           DataProvider dp = new DataProvider(this);
複製代碼
明確指定由DataProvider對像來提供服務。反之,嫡系用法則是透過意圖(Intent)來請Android代為配對,進而找出適當的ContentProvider對像來為aco1對像提供服務。


2.2  嫡系SQLite的範例

    剛才的範例裡,我們直接使用DataProvider類別的接口來與SQLite溝通。本節的範例,將替DataProvider配上ContentProvider接口,讓ac01對像能透過ContentProvider新接口來溝通。此範例也是從SQLite數據庫讀取3筆數據;請仔細看看其程式代碼的微妙差異:
  1. /* ----- ac01.java 程式代碼 ------*/
  2. package com.misoo.pkrr;
  3. import java.util.ArrayList;
  4. import java.util.HashMap;
  5. import java.util.Map;
  6. import android.app.ListActivity;
  7. import android.content.Intent;
  8. import android.database.Cursor;
  9. import android.net.Uri;
  10. import android.os.Bundle;
  11. import android.view.View;
  12. import android.widget.ListView;
  13. import android.widget.SimpleAdapter;

  14. public class ac01 extends ListActivity {
  15.         public static int g_variable;
  16.         public static final String AUTHORITY = "com.misoo.provider.rx09-02";
  17.         public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY
  18.                         + "/Student");
  19.         private static final String[] PROJECTION
  20.               = new String[]{ "stud_no", "stud_name"};
  21.         @Override        protected void onCreate(Bundle savedInstanceState) {
  22.                 super.onCreate(savedInstanceState);
  23.                 Intent intent = getIntent();
  24.                 if (intent.getData() == null)  intent.setData(CONTENT_URI);
  25.                 Cursor cur = getContentResolver().query(getIntent().getData(),
  26.                                 PROJECTION, null, null, null);
  27.                 ArrayList<Map<String, Object>> coll = new ArrayList<Map<String, Object>>();
  28.                 Map<String, Object> item;
  29.                 cur.moveToFirst();
  30.                 while (!cur.isAfterLast()) {
  31.                         item = new HashMap<String, Object>();
  32.                         item.put("c1", cur.getString(0) + ",  " + cur.getString(1));
  33.                         coll.add(item);
  34.                         cur.moveToNext();
  35.                 }
  36.                 this.setListAdapter(new SimpleAdapter(this, coll,
  37.                                 android.R.layout.simple_list_item_1, new String[] { "c1" },
  38.                                 new int[] { android.R.id.text1 }));
  39.         }
  40.         @Override
  41.         protected void onListItemClick(ListView l, View v, int position, long id) { finish();}
  42. }
複製代碼
指令:
  1.               Cursor cur = getContentResolver().query(getIntent().getData(),
  2.                                    PROJECTION, null, null, null);
複製代碼
要求Android代為尋找適合的ContentProvider來提供服務,並不刻意指定由DataProvider對像來擔任。只要合乎ConentProvider接口,且符合意圖條件的對象皆可以來為ac01對像提供服務。於是,ac01程式代碼就不再直接呼叫DataProvider類別的函數了,而是呼叫ContentProvider接口所提供的函數。再來仔細看看DataProvider類別與ContentProvider接口的搭配情形:
  1. /* ----- DataProvider.java 程式代碼 ------*/
  2. package com.misoo.pkrr;
  3. import android.content.ContentProvider;
  4. import android.content.ContentValues;
  5. import android.content.Context;
  6. import android.database.Cursor;
  7. import android.database.SQLException;
  8. import android.database.sqlite.SQLiteDatabase;
  9. import android.database.sqlite.SQLiteOpenHelper;
  10. import android.net.Uri;
  11. import android.util.Log;

  12. public class DataProvider extends ContentProvider {
  13.         private static final String DATABASE_NAME = "StudNewDB";
  14.         private static final int DATABASE_VERSION = 2;
  15.         private static final String TABLE_NAME = "StudTable";
  16.         private static class DatabaseHelper extends SQLiteOpenHelper {
  17.                 DatabaseHelper(Context context) {
  18.                         super(context, DATABASE_NAME, null, DATABASE_VERSION);  }
  19.                 @Override        public void onCreate(SQLiteDatabase db) {
  20.                         db.execSQL("CREATE TABLE " + TABLE_NAME + " (" + "stud_no"
  21.                                         + " TEXT," + "stud_name" + " TEXT" + ");");
  22.                         String sql_1 = "insert into " + TABLE_NAME
  23.                                         + " (stud_no, stud_name) values('S1001', 'Pam');";
  24.                         String sql_2 = "insert into " + TABLE_NAME
  25.                                         + " (stud_no, stud_name) values('S1002', 'Steve');";
  26.                         String sql_3 = "insert into " + TABLE_NAME
  27.                                         + " (stud_no, stud_name) values('S1003', 'John');";
  28.                         try { db.execSQL(sql_1);  db.execSQL(sql_2);  db.execSQL(sql_3);        }
  29.          catch (SQLException e) { Log.e("ERROR", e.toString());        }
  30.                 }
  31.                 @Override
  32.                 public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {}
  33.         }
  34.         // ---------------------------------------------------------------------------------
  35.         private DatabaseHelper mOpenHelper;
  36.         @Override        public boolean onCreate() {
  37.                 mOpenHelper = new DatabaseHelper(getContext());  return true;  }
  38.         @Override        public Cursor query(Uri uri, String[] projection, String selection,
  39.                         String[] selectionArgs, String sortOrder) {
  40.                 SQLiteDatabase db = mOpenHelper.getReadableDatabase();
  41.                 Cursor c = db.query(TABLE_NAME, projection, null, null, null, null,        null);
  42.                 return c;
  43.         }
  44.         @Override        public String getType(Uri uri) {        return null;  }
  45.         @Override        public Uri insert(Uri uri, ContentValues initialValues) { return uri;        }
  46.         @Override        public int delete(Uri uri, String where, String[] whereArgs) { return 0; }
  47.         @Override        public int update(Uri uri, ContentValues values, String where,
  48.                                 String[] whereArgs)
  49.         {  return 0;  }
  50. }
複製代碼
類別定義:
  1.            public class DataProvider extends ContentProvider {
  2.         // …..…..
  3.         }
複製代碼
DataProvider類別繼承ContentProvider父類別,也繼承了它的接口定義。ContentProvider接口定義了多個函數,主要包括:
        l           query()函數---- 它查詢出合乎某條件的數據。
        l           insert()函數---- 它將存入一筆新資料。
        l           delete()函數---- 它刪除合乎某條件的資料。
        l           update()函數---- 更新某些筆數據的內容。

     在這個DataProvider類別裡,撰寫了query()函數內的指令,來實現query()接口,這個query()函數實際呼叫SQLite數據庫的功能。也就是說,ac01等應用程式透過ContentProvider接口間接呼叫到DataProvider的query()函數,然後此query()函數才使用SQLite的服務。
     由於此範例的DataProvider已經是ContentProvider嫡系身份了,必須由Android來啟動它,而不是有ac01等應用程式來直接啟動它,所以必須在AndroidManifest.xml文檔裡給Android一些指示,如下:
  1. /* ----- AndroidManifest.xml 文檔 ------*/
  2. <?xml version="1.0" encoding="utf-8"?>
  3. <manifest xmlns:android="http://schemas.android.com/apk/res/android"
  4.       package="com.misoo.pkrr"
  5.       android:versionCode="1"
  6.       android:versionName="1.0.0">
  7.     <application android:icon="@drawable/icon" android:label="@string/app_name">
  8.         <activity android:name=".ac01"
  9.                   android:label="@string/app_name">
  10.             <intent-filter>
  11.                 <action android:name="android.intent.action.MAIN" />
  12.                 <category android:name="android.intent.category.LAUNCHER" />
  13.             </intent-filter>
  14.         </activity>
  15.         <provider android:name="DataProvider"
  16.             android:authorities="com.misoo.provider.rx09-02">
  17.         </provider>
  18.     </application>
  19. </manifest>
複製代碼
這特別說明DataProvider是一個ContentProvider,於是Android就會來啟動它。
「用Android 就來APK.TW」,快來加入粉絲吧!
Android 台灣中文網(APK.TW)

評分

參與人數 1幫助 +1 收起 理由
idvtw + 1

查看全部評分

收藏收藏 分享分享 分享專題
用Android 就來Android 台灣中文網(https://apk.tw)
您需要登錄後才可以回帖 登錄 | 註冊

本版積分規則