Android ContentProvider

一 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", "數據發生改變");
        }

    }

}
相關文章
相關標籤/搜索