Android ContentProvider

一 ContentProvider简介

ContentProvider是四大组件之一 用来暴露应用里面的数据给其它应用访问

Uri: 代表了要操作的数据 Uri主要包含了 需要操作的内容提供者 和 内容提供者中需要操作的指定数据

UriMatcher: 用于匹配Uri

ContentUris: 用于操作Uri路径后面的参数部分

ContentProvider: 内容提供者 用于对外共享数据

ContentResolver: 内容解决者 用于外部应用中 操作指定Uri内容提供者里面数据

ContentObserver: 内容观察者 用于外部应用中 监听指定Uri内容提供者里面数据变化

AsyncQueryHandler: 用于执行内容解决者的耗时操作 多用于查询

 

二 举个例子

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", "数据发生改变");
        }

    }

}

 

posted @ 2018-09-07 15:53  梦三  阅读(2688)  评论(0编辑  收藏  举报