一 ContentProvider簡介android
ContentProvider是四大組件之一 用來暴露應用裏面的數據給其它應用訪問sql
Uri: 表明了要操做的數據 Uri主要包含了 須要操做的內容提供者 和 內容提供者中須要操做的指定數據數據庫
UriMatcher: 用於匹配Uricookie
ContentUris: 用於操做Uri路徑後面的參數部分app
ContentProvider: 內容提供者 用於對外共享數據async
ContentResolver: 內容解決者 用於外部應用中 操做指定Uri內容提供者裏面數據ide
ContentObserver: 內容觀察者 用於外部應用中 監聽指定Uri內容提供者裏面數據變化this
AsyncQueryHandler: 用於執行內容解決者的耗時操做 多用於查詢spa
二 舉個例子設計
ContentProvider做爲一個應用程序名叫Provider
ContentResolver做爲一個應用程序名叫Resolver
ContentObserver做爲一個應用程序名叫Observer
Provider端
1. 新建一個SQLiteOpenHelper
public class SQLiteUtils extends SQLiteOpenHelper { private static final String DB_NAME = "test.db"; private static final int DB_VERSION = 1; private static SQLiteOpenHelper mInstance; private SQLiteUtils(Context context, String name, SQLiteDatabase.CursorFactory factory, int version) { super(context, name, factory, version); } public static synchronized SQLiteOpenHelper newInstance(Context context) { if (null == mInstance) { mInstance = new SQLiteUtils(context, DB_NAME, null, DB_VERSION); //數據庫的版本號 若是版本號不一樣 執行onUpgrade()方法 } return mInstance; } @Override public void onCreate(SQLiteDatabase db) { // 建立表 String sql = "CREATE TABLE android_account (" + "id INTEGER PRIMARY KEY AUTOINCREMENT, " + "name TEXT NOT NULL, " + "money INTEGER NOT NULL)"; db.execSQL(sql); } @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { // 數據庫升級(在數據庫版本發生變化時會被調用 通常在軟件升級時才需改變版本號) String sql = "ALTER TABLE android_account ADD age INTEGER"; db.execSQL(sql); } }
2. 新建一個ContentProvider
public class MyContentProvider extends ContentProvider { private static final String AUTHORITY = "com.hy.provider.provider.MyContentProvider"; private static final int ANDROID_ACCOUNT = 10010; private static UriMatcher sMatcher = new UriMatcher(UriMatcher.NO_MATCH); static{ // authority(受權) + path(通常是表名) = uri = code(惟一標識) sMatcher.addURI(AUTHORITY, "android_account", ANDROID_ACCOUNT); //content://authority/android_account } private SQLiteOpenHelper mHelper; //數據庫幫助類 @Override public boolean onCreate() { mHelper = SQLiteUtils.newInstance(getContext()); return false; } @Nullable @Override public String getType(@NonNull Uri uri) { return null; } @Nullable @Override public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) { SQLiteDatabase database = mHelper.getWritableDatabase(); switch (sMatcher.match(uri)) { case ANDROID_ACCOUNT: database.insert("android_account", null, values); break; default: // 若是匹配不上 對外拋出異常 throw new IllegalArgumentException("uri do not exist -> " + uri); } // SQLiteDatabase不須要手動關閉 內容提供者會自行維護 getContext().getContentResolver().notifyChange(uri, null); //發出改變通知 return null; } @Override public int delete(@NonNull Uri uri, @Nullable String selection, @Nullable String[] selectionArgs) { SQLiteDatabase database = mHelper.getWritableDatabase(); switch (sMatcher.match(uri)) { case ANDROID_ACCOUNT: database.delete("android_account", selection, selectionArgs); break; default: // 若是匹配不上 對外拋出異常 throw new IllegalArgumentException("uri do not exist -> " + uri); } // SQLiteDatabase不須要手動關閉 內容提供者會自行維護 getContext().getContentResolver().notifyChange(uri, null); //發出改變通知 return 0; } @Override public int update(@NonNull Uri uri, @Nullable ContentValues values, @Nullable String selection, @Nullable String[] selectionArgs) { SQLiteDatabase database = mHelper.getWritableDatabase(); switch (sMatcher.match(uri)) { case ANDROID_ACCOUNT: database.update("android_account", values, selection, selectionArgs); break; default: // 若是匹配不上 對外拋出異常 throw new IllegalArgumentException("uri do not exist -> " + uri); } // SQLiteDatabase不須要手動關閉 內容提供者會自行維護 getContext().getContentResolver().notifyChange(uri, null); //發出改變通知 return 0; } @Nullable @Override public Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable String selection, @Nullable String[] selectionArgs, @Nullable String sortOrder) { SQLiteDatabase database = mHelper.getReadableDatabase(); Cursor cursor; switch (sMatcher.match(uri)) { case ANDROID_ACCOUNT: cursor = database.query("android_account", //表名 若是是多表聯合查詢 能夠用逗號將兩個表名分開 projection, //要查詢出來的列名 selection, //查詢條件子句 在條件子句容許使用佔位符"?" selectionArgs, //selection語句中佔位符的值 null, null, sortOrder); break; default: // 若是匹配不上 對外拋出異常 throw new IllegalArgumentException("uri do not exist -> " + uri); } // Cursor和SQLiteDatabase不須要手動關閉 內容提供者會自行維護 return cursor; } }
3. AndroidManifest.xml application節點裏面配置provider
<!-- authorities: 只是一個惟一標識 若是手機裏面已經存在這樣的受權安裝應用就會失敗 exported: 是否容許其它應用訪問 --> <provider android:name=".provider.MyContentProvider" android:authorities="com.hy.provider.provider.MyContentProvider" android:exported="true" />
Resolver端
public class MainActivity extends AppCompatActivity implements View.OnClickListener { // android:authorities="com.hy.provider.provider.MyContentProvider" Uri mUri = Uri.parse("content://com.hy.provider.provider.MyContentProvider/android_account"); ContentResolver mResolver; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); findViewById(R.id.insert).setOnClickListener(this); findViewById(R.id.delete).setOnClickListener(this); findViewById(R.id.update).setOnClickListener(this); findViewById(R.id.select).setOnClickListener(this); findViewById(R.id.async).setOnClickListener(this); mResolver = getContentResolver(); } @Override public void onClick(View view) { switch (view.getId()) { case R.id.insert: try { ContentValues values = new ContentValues(); values.put("name", "黃禕"); values.put("money", 222); mResolver.insert(mUri, values); } catch (Exception e) { e.printStackTrace(); } break; case R.id.delete: try { mResolver.delete(mUri, "id > ?", new String[]{"0"}); } catch (Exception e) { e.printStackTrace(); } break; case R.id.update: try { ContentValues values = new ContentValues(); values.put("money", 1100); mResolver.update(mUri, values, "id > ?", new String[]{"0"}); } catch (Exception e) { e.printStackTrace(); } break; case R.id.select: try { Cursor cursor = mResolver.query(mUri, new String[]{"id", "name", "money"}, null, null, null); while (cursor.moveToNext()) { int id = cursor.getInt(cursor.getColumnIndex("id")); String name = cursor.getString(cursor.getColumnIndex("name")); int money = cursor.getInt(cursor.getColumnIndex("money")); Log.i("HUANG", "id=" + id + ", name=" + name + ", money=" + money); } cursor.close(); } catch (Exception e) { e.printStackTrace(); } break; case R.id.async: // 查詢的數據若是超級多 直接查詢就可能致使ANR 這是一個耗時的操做 // Android專門設計了一個類 用於執行內容解決者的耗時操做 多用於查詢 new QueryHandler(mResolver).startQuery(0, //查詢的惟一標識 該參數會傳遞給onQueryComplete() null, //用來傳遞數據 該參數會傳遞給onQueryComplete() mUri, new String[]{"id", "name", "money"}, null, null, null); break; } } private class QueryHandler extends AsyncQueryHandler { public QueryHandler(ContentResolver cr) { super(cr); } @Override protected void onQueryComplete(int token, Object cookie, Cursor cursor) { super.onQueryComplete(token, cookie, cursor); while (cursor.moveToNext()) { int id = cursor.getInt(cursor.getColumnIndex("id")); String name = cursor.getString(cursor.getColumnIndex("name")); int money = cursor.getInt(cursor.getColumnIndex("money")); Log.i("HUANG", "id=" + id + ", name=" + name + ", money=" + money); } } } }
Observer端
public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // 註冊內容觀察者監聽 getContentResolver().registerContentObserver(Uri.parse("content://com.hy.provider.provider.MyContentProvider/android_account"), true, //true = 監聽指定Uri和指定Uri的子路徑 new MyContentObserver(new Handler())); } private class MyContentObserver extends ContentObserver { public MyContentObserver(Handler handler) { super(handler); } @Override public void onChange(boolean selfChange) { super.onChange(selfChange); // 當內容提供者發出改變通知 該方法就會調用 Log.i("HUANG", "數據發生改變"); } } }