Android開發教學
Android 開發環境配置
Android 架構
Android 應用組件
Android Hello World示例
Android 資源組織和訪問
Android Activity
Android Service
Android廣播接收器
Android內容提供者
Android碎片/片段
Android Intent過濾器
Android UI佈局
Android LinearLayout
Android RelativeLayout
Android TableLayout
Android AbsoluteLayout
Android FrameLayout
Android ListView
Android GridView
Android UI控件
Android TextView
Android EditText
Android AutoCompleteTextView
Android Button
Android ImageButton
Android CheckBox
Android ToggleButton
Android RadioButton
Android RadioGroup
Android事件處理
Android樣式和主題
Android樣式示例
Android主題示例
Android自定義組件
Android拖放
Android通知
Android基於位置服務
Android發送電子郵件
Android發送短信/SMS
Android撥打電話
發佈Android應用
ArrayAdapter
SimpleCursorAdapter
Android ProgressDialog
Android Spinner
使用活動代碼自定義Android組件
使用佈局文件自定義Android組件
Android自定義組件及屬性
Android Alertdialog(警告對話框)
Android Animation(動畫)實例
Android音頻捕獲(錄音)
Android音頻管理器實例
Android AutoCompleteTextView(自動完成)實例
Android最佳實踐
Android Bluetooth(藍牙)實例
Android Camera(攝像頭)
Android Clipboard(複製/剪貼板)
Android自定義字體
Android數據備份
Android Gestures/手勢
Android圖片效果
Android圖片切換
Android內部存儲
Android JetPlayer實例
Android JSON解析器
Android加載Spinner
Android本地化
Android登錄實例
Android MediaPlayer(多媒體播放)

Android內容提供者

內容提供程序(Provider)組件從一個應用到其他請求提供數據。通過 ContentResolver 類的方法這樣的請求處理。內容提供程序使用不同的方式來存儲數據,並且可以將數據存儲在數據庫中,文件中,甚至在網絡上。

每一個 Android 應用程序運行在自己的進程保持一個應用程序數據,在另外一個應用程序中隱藏自己的權限。但有時需要在應用程序之間共享數據。這時內容提供程序是非常有用。

內容提供程序將內容集中在一個地方,讓許多不同的應用訪問。內容提供程序的行非常像數據庫,可以對它進行查詢,編輯等操作,添加或刪除可使用 insert(), update(), delete(), query() 方法。在大多數情況下,這些數據都存儲在SQlite數據庫。

內容提供程序實施 ContentProvider 類的子類,必須實現了一套標準的 API,使其他應用程序來執行事務。

public class MyContentProvider extends ContentProvider { }

內容的URI

要查詢內容提供程序,可以指定 URI 形式如以下格式的查詢字符串:

:///<data_type>/

這裏是URI的各個部分的細節

部分

描述

prefix

始終設置內容爲 ://

authority

規定內容提供商的名稱,例如聯繫人,瀏覽器等。對於第三方內容提供商,這可能是完全合格的名稱,如 com.yiibai.statusprovider

data_type

表示數據,特定提供程序提供的類型。例如,如果得到所有的聯繫人的通訊錄內容提供程序,那麼數據路徑URI是這樣的 content://contacts/people

id

規定要求的特定記錄。例如,如果正在尋找聯繫人編號爲5,在聯繫人內容提供者中,則URI是這樣的 content://contacts/people/5.

創建內容提供者

以下是簡單的步驟用來創建自己的內容提供者的數量。

  • 首先,需要創建一個內容提供者擴展 ContentProvider 基類。

  • 其次,需要定義內容提供者用於訪問內容的 URI 地址。

  • 接下來,需要創建自己的數據庫用於保存內容。通常情況下,Android使用SQLite數據庫,並且框架需要重寫 onCreate() 方法會使用 SQLite開放的 Helper方法來創建或打開提供者數據庫。當啓動應用程序時,每個內容提供者的onCreate()方法調用處理程序在主應用程序。

  • 接下來,必須實現內容提供者查詢來執行不同的數據庫的具體操作。

  • 最後,在activity文件使用標籤註冊內容提供者。

下面是需要覆蓋內容提供程序類的方法的列表:

  • onCreate() 方法被稱爲提供者開始。

  • query() 方法接收來自客戶端的請求。返回的結果作爲一個Cursor對象。

  • insert() 方法插入一條新記錄到內容提供者。

  • delete() 方法從內容提供者刪除記錄。

  • update() 方法從內容提供者更新現有記錄。

  • getType() 此方法在給定的URI返回 MIME 類型的數據。

示例

這個例子將解釋如何創建自己的 ContentProvider。因此按照下面的步驟類似於我們之前創建Hello World範例:

Step

描述

1

使用Eclipse IDE創建Android應用程序,並將它命名爲MyContentProviderunder在包com.example.mycontentprovider下並使用空的Activity。

2

修改主要活動文件MainActivity.java增加兩個新的方法onClickAddName() 和 onClickRetrieveStudents()。

3

創建一個新的名爲StudentsProvider.java的java文件在packagecom.example.mycontentprovider包下,並定義實際提供者和相關方法。

4

使用註冊內容提供者在AndroidManifest.xml文件中的<provider.../>標籤

5

修改res/layout/activity_main.xml文件的默認內容包括一個小的GUI添加學生記錄。

6

在res/values/strings.xml文件中定義所需的常量

7

運行該應用程序啓動Android模擬器和驗證應用程序所做的修改結果。

以下是主活動文件 src/com.example.mycontentprovider/MainActivity.java 修改後的內容。這個文件可以包括每個生命週期方法。我們已經增加了兩個新方法onClickAddName()onClickRetrieveStudents() 來處理用戶與應用程序交互。

package com.example.mycontentprovider; import android.net.Uri; import android.os.Bundle; import android.app.Activity; import android.content.ContentValues; import android.content.CursorLoader; import android.database.Cursor; import android.view.Menu; import android.view.View; import android.widget.EditText; import android.widget.Toast; public class MainActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); } @Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.main, menu); return true; } public void onClickAddName(View view) { // Add a new student record ContentValues values = new ContentValues(); values.put(StudentsProvider.NAME, ((EditText)findViewById(R.id.txtName)).getText().toString()); values.put(StudentsProvider.GRADE, ((EditText)findViewById(R.id.txtGrade)).getText().toString()); Uri uri = getContentResolver().insert( StudentsProvider.CONTENT_URI, values); Toast.makeText(getBaseContext(), uri.toString(), Toast.LENGTH_LONG).show(); } public void onClickRetrieveStudents(View view) { // Retrieve student records String URL = "content://com.example.provider.College/students"; Uri students = Uri.parse(URL); Cursor c = managedQuery(students, null, null, null, "name"); if (c.moveToFirst()) { do{ Toast.makeText(this, c.getString(c.getColumnIndex(StudentsProvider._ID)) + ", " + c.getString(c.getColumnIndex( StudentsProvider.NAME)) + ", " + c.getString(c.getColumnIndex( StudentsProvider.GRADE)), Toast.LENGTH_SHORT).show(); } while (c.moveToNext()); } } }

在 com.example.mycontentprovider 包下創建新的 StudentsProvider.java 文件,以下是內容:

package com.example.mycontentprovider; import java.util.HashMap; import android.content.ContentProvider; import android.content.ContentUris; import android.content.ContentValues; import android.content.Context; import android.content.UriMatcher; import android.database.Cursor; import android.database.SQLException; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper; import android.database.sqlite.SQLiteQueryBuilder; import android.net.Uri; import android.text.TextUtils; public class StudentsProvider extends ContentProvider { static final String PROVIDER_NAME = "com.example.provider.College"; static final String URL = "content://" + PROVIDER_NAME + "/students"; static final Uri CONTENT_URI = Uri.parse(URL); static final String _ID = "_id"; static final String NAME = "name"; static final String GRADE = "grade"; private static HashMap<String, String> STUDENTS_PROJECTION_MAP; static final int STUDENTS = 1; static final int STUDENT_ID = 2; static final UriMatcher uriMatcher; static{ uriMatcher = new UriMatcher(UriMatcher.NO_MATCH); uriMatcher.addURI(PROVIDER_NAME, "students", STUDENTS); uriMatcher.addURI(PROVIDER_NAME, "students/#", STUDENT_ID); } /**
* Database specific constant declarations
*/ private SQLiteDatabase db; static final String DATABASE_NAME = "College"; static final String STUDENTS_TABLE_NAME = "students"; static final int DATABASE_VERSION = 1; static final String CREATE_DB_TABLE = " CREATE TABLE " + STUDENTS_TABLE_NAME + " (_id INTEGER PRIMARY KEY AUTOINCREMENT, " + " name TEXT NOT NULL, " + " grade TEXT NOT NULL);"; /**
* Helper class that actually creates and manages
* the provider's underlying data repository.
*/ private static class DatabaseHelper extends SQLiteOpenHelper { DatabaseHelper(Context context){ super(context, DATABASE_NAME, null, DATABASE_VERSION); } @Override public void onCreate(SQLiteDatabase db) { db.execSQL(CREATE_DB_TABLE); } @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { db.execSQL("DROP TABLE IF EXISTS " + STUDENTS_TABLE_NAME); onCreate(db); } } @Override public boolean onCreate() { Context context = getContext(); DatabaseHelper dbHelper = new DatabaseHelper(context); /**
* Create a write able database which will trigger its
* creation if it doesn't already exist.
*/ db = dbHelper.getWritableDatabase(); return (db == null)? false:true; } @Override public Uri insert(Uri uri, ContentValues values) { /**
* Add a new student record
*/ long rowID = db.insert( STUDENTS_TABLE_NAME, "", values); /**
* If record is added successfully
*/ if (rowID > 0) { Uri _uri = ContentUris.withAppendedId(CONTENT_URI, rowID); getContext().getContentResolver().notifyChange(_uri, null); return _uri; } throw new SQLException("Failed to add a record into " + uri); } @Override public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { SQLiteQueryBuilder qb = new SQLiteQueryBuilder(); qb.setTables(STUDENTS_TABLE_NAME); switch (uriMatcher.match(uri)) { case STUDENTS: qb.setProjectionMap(STUDENTS_PROJECTION_MAP); break; case STUDENT_ID: qb.appendWhere( _ID + "=" + uri.getPathSegments().get(1)); break; default: throw new IllegalArgumentException("Unknown URI " + uri); } if (sortOrder == null || sortOrder == ""){ /**
* By default sort on student names
*/ sortOrder = NAME; } Cursor c = qb.query(db, projection, selection, selectionArgs, null, null, sortOrder); /**
* register to watch a content URI for changes
*/ c.setNotificationUri(getContext().getContentResolver(), uri); return c; } @Override public int delete(Uri uri, String selection, String[] selectionArgs) { int count = 0; switch (uriMatcher.match(uri)){ case STUDENTS: count = db.delete(STUDENTS_TABLE_NAME, selection, selectionArgs); break; case STUDENT_ID: String id = uri.getPathSegments().get(1); count = db.delete( STUDENTS_TABLE_NAME, _ID + " = " + id + (!TextUtils.isEmpty(selection) ? " AND (" + selection + ')' : ""), selectionArgs); break; default: throw new IllegalArgumentException("Unknown URI " + uri); } getContext().getContentResolver().notifyChange(uri, null); return count; } @Override public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { int count = 0; switch (uriMatcher.match(uri)){ case STUDENTS: count = db.update(STUDENTS_TABLE_NAME, values, selection, selectionArgs); break; case STUDENT_ID: count = db.update(STUDENTS_TABLE_NAME, values, _ID + " = " + uri.getPathSegments().get(1) + (!TextUtils.isEmpty(selection) ? " AND (" + selection + ')' : ""), selectionArgs); break; default: throw new IllegalArgumentException("Unknown URI " + uri ); } getContext().getContentResolver().notifyChange(uri, null); return count; } @Override public String getType(Uri uri) { switch (uriMatcher.match(uri)){ /**
* Get all student records
*/ case STUDENTS: return "vnd.android.cursor.dir/vnd.example.students"; /**
* Get a particular student
*/ case STUDENT_ID: return "vnd.android.cursor.item/vnd.example.students"; default: throw new IllegalArgumentException("Unsupported URI: " + uri); } } }

以下將 AndroidManifest.xml 文件的內容修改。在這裏添加了<provider.../>標籤,包括內容提供者:

<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.mycontentprovider" android:versionCode="1" android:versionName="1.0" > <uses-sdk android:minSdkVersion="8" android:targetSdkVersion="17" /> <application android:allowBackup="true" android:icon="@drawable/ic_launcher" android:label="@string/app_name" android:theme="@style/AppTheme" > <activity android:name="com.example.mycontentprovider.MainActivity" android:label="@string/app_name" > <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> <provider android:name="StudentsProvider" android:authorities="com.example.provider.College">

以下將 res/layout/activity_main.xml 文件的內容包括一個按鈕來自定義廣播意圖: 

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent" android:orientation="vertical" > <TextView android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="Name" /> <EditText android:id="@+id/txtName" android:layout_height="wrap_content" android:layout_width="fill_parent" /> <TextView android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="Grade" /> <EditText android:id="@+id/txtGrade" android:layout_height="wrap_content" android:layout_width="fill_parent" /> <Button android:text="Add Name" android:id="@+id/btnAdd" android:layout_width="fill_parent" android:layout_height="wrap_content" android:onClick="onClickAddName" /> <Button android:text="Retrieve Students" android:id="@+id/btnRetrieve" android:layout_width="fill_parent" android:layout_height="wrap_content" android:onClick="onClickRetrieveStudents" />

確保 res/values/strings.xml 文件有以下內容:

<string name="app_name">MyContentProvider <string name="action_settings">Settings <string name="hello_world">Hello world! ;

現在運行剛剛創建的應用程序 MyContentProvider。假設設置AVD並且已經做好了環境設置。要從Eclipse運行的應用程序,從工具欄打開一個項目的活動文件,並單擊「Run」  Eclipse  圖標。Eclipse AVD 安裝的應用程序後並啓動它,如果設置和應用程序沒有問題,它會啓動顯示仿真器窗口(要耐心,可能有點慢,由你計算機的速度決定):

Android

現在輸入學生姓名和年級,最後單擊「Add Name」按鈕,將增加學生記錄到數據庫中,ContentProvider URI也一起添加到數據庫中,底部顯示的記錄數,並會閃爍一條消息。此操作使得我們的insert()方法的使用。讓我們重複這個過程,我們的內容提供者在數據庫中添加一些更多的學生信息。

Add

正在添加記錄在數據庫中,現在其時間讓ContentProvider來給我們這些記錄回,所以讓我們的單擊檢索學生按鈕,這將獲取並顯示所有記錄這是按照我們執行query()方法。

Fetch

可以編寫活動 MainActivity.java文件提供的回調函數,然後修改用戶界面,更新和刪除操作按鈕添加和讀操作以同樣的方式,因爲我們已經做了更新和刪除操作。

通過這種方式,可以使用現有的內容提供者,如地址簿,或不錯的面向數據庫應用開發,可以使用內容提供者的概念,可以執行所有的數據庫操作,如排序讀,寫,更新和刪除上面的例子中解釋。