Android四大組件之ContentProvider

來源:互聯網
上載者:User

Android四大組件之ContentProvider
Android四大組件之ContentProviderContentProvider

安卓應用程式預設是無法擷取到其他程式的資料,這是安卓安全學的基石(沙箱原理)。但是經常我們需要給其他應用分享資料,內容提供者就是一個這種可以分享資料給其他應用的介面。
可以簡單的理解為,內容提供者就是一個可以在不同應用程式間共用資料的組件,相當於一個中間人,一個程式把資料暴露給這個中間人,另一個則通過這個中間人擷取相應的資料.

下面的這張圖片能更直觀的顯示:

ContentProvider中的getContext和AndroidTestCast中的getContext方法一樣,都是一個類比的上下文,必須在該類初始化之後才會調用setContext方法將context設定成自己的成員變數中記錄,
所以對於擷取getContext的時候只能放在方法內,不能放到成員位置,因為在成員上時是null,而在方法內調用時該類就會已經初始化完了

ContentProvider中的query()後不能關閉資料庫,因為其他的應用在調用該query方法時需要繼續使用該傳回值Cursor,所以不能關閉資料庫,因為資料庫關閉之後Cursor就不能用了,
Cursor中儲存的資料其實是資料庫的一個引用,如果資料庫關了Cursor就不能找到裡面的資料了,Cursor.close()只是釋放·Cursor·用到的資源。說到這裡就多數一句
According to Dianne Hackborn (Android framework engineer) there is no need to close the database in a content provider.以為內容提供者是因為進程啟動時便載入,之後就一直存在,當進程銷毀
釋放資源時會去關閉資料庫。

如果資料是SQLiteDatabase,表中必須有一個_id的列,用來表示每條記錄的唯一性。

繼承ContentProvider,並實現相應的方法。
public class NoteProvider extends ContentProvider {    private static final int NOTES = 1;    private static final int NOTE_ID = 2;    public static final String AUTHORITY = com.charon.demo.provider.noteprovider;    public static final String TABLE_NAME = note;    // 定義一個名為`CONTENT_URI`必須為其指定一個唯一的字串值,最好的方案是以類的全名稱    public static final Uri CONTENT_URI = Uri.parse(content:// + AUTHORITY + / + TABLE_NAME);    // 聲明一個路徑的檢查者,參數為Uri不匹配時的傳回值    // 雖然是中間人,但也不能誰要資料我們都給,所以要檢查下,只有符合我們要求的人,我們才會給他資料。    private static UriMatcher sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);    private NoteSQLiteOpenHelper mSQLiteOpenHelper;    private SQLiteDatabase mSQLiteDatabase;    static {        // 建立匹配規則,例如發現路徑為ccom.charon.demo.provider.noteprovider/note/1表示要操作note表中id為1的記錄        sUriMatcher.addURI(AUTHORITY, TABLE_NAME, NOTES);        sUriMatcher.addURI(AUTHORITY, TABLE_NAME + /#, NOTE_ID);    }    /**     * Implement this to initialize your content provider on startup.     * This method is called for all registered content providers on the     * application main thread at application launch time.  It must not perform     * lengthy operations, or application startup will be delayed.     * 

*

You should defer nontrivial initialization (such as opening, * upgrading, and scanning databases) until the content provider is used * (via {@link #query}, {@link #insert}, etc). Deferred initialization * keeps application startup fast, avoids unnecessary work if the provider * turns out not to be needed, and stops database errors (such as a full * disk) from halting application launch. *

*

If you use SQLite, {@link android.database.sqlite.SQLiteOpenHelper} * is a helpful utility class that makes it easy to manage databases, * and will automatically defer opening until first use. If you do use * SQLiteOpenHelper, make sure to avoid calling * {@link android.database.sqlite.SQLiteOpenHelper#getReadableDatabase} or * {@link android.database.sqlite.SQLiteOpenHelper#getWritableDatabase} * from this method. (Instead, override * {@link android.database.sqlite.SQLiteOpenHelper#onOpen} to initialize the * database when it is first opened.) * * @return true if the provider was successfully loaded, false otherwise */ @Override public boolean onCreate() { mSQLiteOpenHelper = new NoteSQLiteOpenHelper(getContext()); return true; } /** * 內容提供者暴露的查詢的方法. */ @Override public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { mSQLiteDatabase = mSQLiteOpenHelper.getReadableDatabase(); Cursor cursor; // 1.重要的事情 ,檢查 uri的路徑. switch (sUriMatcher.match(uri)) { case NOTES: break; case NOTE_ID: String id = uri.getLastPathSegment(); if (TextUtils.isEmpty(selection)) { selection = selection + _id = + id; } else { selection = selection + and + _id = + id; } break; default: throw new IllegalArgumentException(UnKnown Uri + uri); break; } cursor = mSQLiteDatabase.query(TABLE_NAME, projection, selection, selectionArgs, null, null, sortOrder); if (cursor != null) { cursor.setNotificationUri(getContext().getContentResolver(), uri); } return cursor; } /** * Implement this to handle requests for the MIME type of the data at the * given URI. The returned MIME type should start with * vnd.android.cursor.item for a single record, * or vnd.android.cursor.dir/ for multiple items. * This method can be called from multiple threads, as described in * Processes * and Threads. *

*

Note that there are no permissions needed for an application to * access this information; if your content provider requires read and/or * write permissions, or is not exported, all applications can still call * this method regardless of their access permissions. This allows them * to retrieve the MIME type for a URI when dispatching intents. * * @param uri the URI to query. * @return a MIME type string, or {@code null} if there is no type. */ @Override public String getType(Uri uri) { // 注釋說的很清楚了,下面是常用的格式 // 單個記錄的IMEI類型 vnd.android.cursor.item/vnd.. // 多個記錄的IMEI類型 vnd.android.cursor.dir/vnd.. switch (sUriMatcher.match(uri)) { case NOTE_ID: // 如果uri為 content://com.charon.demo.noteprovider/note/1 return vnd.android.cursor.item/vnd.charon.note; case NOTES: return vnd.android.cursor.dir/vnd.charon.note; default: return null; } // 這個MIME類型的作用是要匹配AndroidManifest.xml檔案標籤下標籤的子標籤的屬性android:mimeType。 // 如果不一致,則會導致對應的Activity無法啟動。 } @Override public Uri insert(Uri uri, ContentValues values) { mSQLiteDatabase = mSQLiteOpenHelper.getWritableDatabase(); switch (sUriMatcher.match(uri)) { case NOTES: break; case NOTE_ID: break; default: throw new IllegalArgumentException(UnKnown Uri + uri); break; } long rowId = mSQLiteDatabase.insert(TABLE_NAME, null, values); if (rowId > 0) { Uri noteUri = ContentUris.withAppendedId(CONTENT_URI, rowId); getContext().getContentResolver().notifyChange(noteUri, null); return noteUri; } return null; } @Override public int delete(Uri uri, String selection, String[] selectionArgs) { mSQLiteDatabase = mSQLiteOpenHelper.getWritableDatabase(); switch (sUriMatcher.match(uri)) { case NOTES: break; case NOTE_ID: String id = uri.getLastPathSegment(); if (TextUtils.isEmpty(selection)) { selection = selection + _id = + id; } else { selection = selection + and + _id = + id; } break; default: throw new IllegalArgumentException(UnKnown Uri + uri); break; } int count = mSQLiteDatabase.delete(TABLE_NAME, selection, selectionArgs); getContext().getContentResolver().notifyChange(uri, null); return count; } @Override public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { switch (sUriMatcher.match(uri)) { case NOTES: break; case NOTE_ID: break; default: throw new IllegalArgumentException(UnKnown Uri + uri); break; } mSQLiteDatabase = mSQLiteOpenHelper.getWritableDatabase(); int update = mSQLiteDatabase.update(TABLE_NAME, values, selection, selectionArgs); return update; }

在資訊清單檔中進行註冊,並且指定其authorities
使用內容提供者擷取資料,使用ContentResolver去操作ContentProvider, ContentResolver用於管理ContentProvider執行個體,並且可實現找到指定的ContentProvider並擷取裡面的資料
java
public void query(View view){
//得到內容提供者的解析器 中間人
ContentResolver resolver = getContentResolver();
Cursor cursor = resolver.query(NoteProvider.CONTENT_URI, null, null, null, null);
while(cursor.moveToNext()){
String name = cursor.getString(cursor.getColumnIndex(name));
int id = cursor.getInt(cursor.getColumnIndex(id));
float money = cursor.getFloat(cursor.getColumnIndex(money));
System.out.println(id=+id+,name=+name+,money=+money);
}
cursor.close();
}
public void insert(View view){
ContentResolver resolver = getContentResolver();
ContentValues values = new ContentValues();
values.put(name, 買洗頭膏);
values.put(money, 22.58f);
resolver.insert(NoteProvider.CONTENT_URI, values);
}
public void update(View view){
ContentResolver resolver = getContentResolver();
ContentValues values = new ContentValues();
values.put(name, 買洗頭膏);
values.put(money, 42.58f);
resolver.update(NoteProvider.CONTENT_URI, values, name=?, new String[]{買洗頭膏});
}
public void delete(View view){
ContentResolver resolver = getContentResolver();
resolver.delete(NoteProvider.CONTENT_URI, name=?, new String[]{買洗頭膏});
}

內容觀察者

內容觀察者的原理:
How a content provider actually stores its data under the covers is up to its designer. But all content providers implement a common interface for querying the provider and
returning results — as well as for adding, altering, and deleting data.
It's an interface that clients use indirectly, most generally through ContentResolver objects.
You get a ContentResolver by calling getContentResolver() from within the implementation of an Activity or other application component:
You can then use the ContentResolver's methods to interact with whatever content providers you're interested in.

一方使用內容觀察者去觀察變化

    protected void onCreate(Bundle savedInstanceState) {    super.onCreate(savedInstanceState);    setContentView(R.layout.activity_main);    ContentResolver resolver = getContentResolver();    resolver.registerContentObserver(NoteProvider.CONTENT_URI, true, new NoteObserver(new Handler()));}private class NoteObserver extends ContentObserver {    public NoteObserver(Handler handler) {        super(handler);    }    //當觀察到資料發生變化的時候  會執行onchange方法.    @Override    public void onChange(boolean selfChange) {        super.onChange(selfChange);        Log.i(TAG,發現有新的簡訊產生了...);        //1.利用內容提供者  中間人 擷取使用者的簡訊資料.        ContentResolver resolver  = getContentResolver();        // .. 重新查詢        cursor = ...;        cursor.close();    }}

一方在發生變化的時候去發送改變的訊息
對於一些系統的內容提供者內部都實現了該步驟,如果是自己寫程式想要暴露就必須要加上該代碼。

getContext().getContentResolver().notifyChange(uri, null);
 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.