内容提供器Content Provider用于不同程序之间进行数据共享:允许其它程序访问操作提供内容提供器的程序,同时保证提供被访问的数据的安全性。

内容提供器Content Provider一般有两种用法:

  • 使用其他现有提供内容提供器的程序来读取和操作该程序的数据;
  • 自身程序创建内容提供器供其他程序读取和操作自身程序的数据,即对外提供接口。

比如常用的电话簿、短信、媒体库等都提供了内容提供器供其他程序读取和操作数据。

内容提供器Content Provider基本用法

通过Context中的getContentResolver()获取ContentResolver类的实例来访问其他程序的内容提供器的数据,进行相关CRUD操作。相关方法与SQLiteDatabase基本相同,只不过方法参数有些不同。

  • insert():添加数据;
  • update():更新数据;
  • delete():删除数据;
  • query():查询数据。

ContentResolverCRUD操作不接受表名,而是使用Uri参数(内容URI)。内容URI给内容提供器提供了唯一标志符,它是有两部分组成:authoritypath

  • authority:用于区分应用程序,一般为了更好的区分,都会采用程序包名再加上provider来命名。比如程序的包名com.demo.app,那么该程序的authority可以命名为com.demo.app.provider
  • path:用于区分同一程序中不同表,通常都会添加在authority后面。比如程序存在2张表table1table2,对应的path就可以分别命名为/table1/table2
  • 内容URI:是将上面的authoritypath进行组合,然后再在头部加上协议声明。比如content://com.demo.app.provider/table1content://com.demo.app.provider/table2

query()操作

通过内容URI的内容,就可以定位是哪个程序中的哪张数据库表。接着将内容URI解析为Uri对象作为参数传入ContentResolver对象中。

Uri uri = Uri.parse("content://com.demo.app.provider/table1");
Cursor cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs, sortOrder);

这些参数和SQLiteDatabase中的query()方法参数类似,下面是简单的对比:

query()参数对应SQL部分说明
urifrom table指定查询某个应用程序的表
projectionselect column1, column2, ...查询指定列名
selectionwhere column = valuewhere的约束条件
selectionArgs where约束条件的占位符提供的具体值
order byorder by column1, column2指定查询结果的排列方式

查询返回Cursor对象,就可以将数据从Cursor对象逐个读取出来。

Uri uri = Uri.parse("content://com.demo.app.provider/table1");
Cursor cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs, sortOrder);

if (null != cursor) {
    while (cursor.moveToNext()) {
        String column1 = cursor.getString(cursor.getColumnIndex("column1"));
        int column2 = cursor.getString(cursor.getColumnIndex("column2"));
    }
}

if(null != cursor) {
    cursor.close();
}

insert()操作

Uri uri = Uri.parse("content://com.demo.app.provider/table1");

ContentValues values = new ContentValues();
values.put("column1", "text");
values.put("column2", 1);

context.getContentResolver().insert(uri, values);

update()操作

Uri uri = Uri.parse("content://com.demo.app.provider/table1");

ContentValues values = new ContentValues();
values.put("column1", "new_value");

context.getContentResolver().update(uri, values, "column1 = ? and column2 = ?", new String[] {"text1", "1"});

delete()操作

Uri uri = Uri.parse("content://com.demo.app.provider/table1");

context.getContentResolver().delete(uri, "column1 = ?", new String[] {"1"});

读取通讯录

public class MainActivity extends AppCompatActivity {
    ArrayAdapter<String> adapter;
    List<String> contactsList = new ArrayList<>();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        ListView contactsView = (ListView) findViewById(R.id.contacts_view);
        adapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, contactsList);
        contactsView.setAdapter(adapter);
        if(ContextCompat.checkSelfPermission(this, Manifest.permission.READ_CONTACTS) != PackageManager.PERMISSION_GRANTED) {
            ActivityCompat.requestPermissions(this, new String [] {Manifest.permission.READ_CONTACTS}, 1);
        } else {
            readContacts();
        }
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        switch (requestCode) {
            case 1:
                if(grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                    readContacts();
                } else {
                    Toast.makeText(this, "You denied the permission!", Toast.LENGTH_LONG).show();
                }
                break;
                default:break;
        }
    }

    private void readContacts() {
        Cursor cursor = null;
        cursor = getContentResolver().query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, null, null, null, null);
        if (null != cursor) {
            while (cursor.moveToNext()) {
                String displayName = cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME));
                String number = cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER));
                contactsList.add(displayName + "---" + number);
            }
            adapter.notifyDataSetChanged();
        }
        if(null != cursor) {
            cursor.close();
        }
    }
}

AndroidManifest.xml中添加权限声明:

<uses-permission android:name="android.permission.READ_CONTACTS" />

创建自己的内容提供器

创建自己的内容提供器可以通过继承ContentProvider类来实现。ContentProvider类有6个抽象方法:

  • onCreate():初始化内容提供器是调用。通常进行数据库创建、升级等操作,返回true表示内容提供器初始化成功,返回false则表示失败。
  • query():内容提供器提供的查询数据接口。uri参数确定查询那张表,projection参数用于确定查询返回结果的列,selectionselectionArgs参数用于约束条件,sortOrder参数对结果进行排序。查询结果存放在Cursor对象中。
  • insert():内容提供器提供的添加数据接口。uri参数确定添加记录的表,values参数为添加的记录的数据。返回一个用于表示这条新纪录的URI。
  • update():内容提供器提供的更新数据接口,uri参数确定更新记录的表,values参数为更新记录的数据,selectionselectionArgs参数用于约束条件。
  • delete():内容提供器提供的删除数据接口。uri参数确定更新记录的表,selectionselectionArgs参数用于约束条件。
  • getType():根据传入的内容URI返回相应的MIME类型。
public class DemoContentProvider extends ContentProvider {
    @Override
    public boolean onCreate() {
        return false;
    }
    @Override
    public Cursor query(Uri uri,  String[] projection,  String selection,  String[] selectionArgs,  String sortOrder) {
        return null;
    }
    @Override
    public Uri insert(Uri uri,  ContentValues values) {
        return null;
    }
    @Override
    public int update(Uri uri,  ContentValues values,  String selection,  String[] selectionArgs) {
        return 0;
    }
    @Override
    public int delete(Uri uri,  String selection,  String[] selectionArgs) {
        return 0;
    }
    @Override
    public String getType(Uri uri) {
        return null;
    }
}

内容URI的格式

内容URI的格式主要有2种,

  • content://com.demo.app.provider/table1,表示该应用的table1表中的数据(以路径结尾,则期望匹配该表中的所有数据)。
  • content://com.demo.app.provider/table1/1,表示该应用的table1表中id为1的数据(以id结尾,则期望该表中拥有id的数据)。

上面的URI格式使用通配符来表示:

  • *:匹配任意长度的任意字符,如content://com.demo.app.provider/*匹配应用的任意表。
  • #:匹配任意长度的数字,如content://com.demo.app.provider/table1/#匹配应用中的表table1的任意一行数据。

UriMatcher

有了内容URI的正则表达式匹配规则,就可以借助UriMatcher类方便的匹配内容URI。

addURI()方法:该方法接收3个参数,分别是authoritypath、自定义码。
match()方法:根据addURI()设置的内容,该方法接收将传递进来的Uri对象参数返回匹配对应的自定义码。利用自定义码,就可以确定该内容URI期望访问哪张表中数据。

ContentProvider类的getType()方法和MIME

getType()方法用于获取Uri对象对应的MIME类型。其中一个内容URI对应的MIME字符串主要有3部分组成:

  1. 必须以vnd开头;
  2. 如果内容URI以路径结尾,则该部分内容为android.cursor.dir/;如果内容URI以id结尾,则该部分内容为android.cursor.item/
  3. 最后部分为vnd.{authority}.{path}

根据上面的约定,
内容URIcontent://com.demo.app.provider/table1对应MIME类型vnd.android.cursor.dir/vnd.com.demo.app.provider.table1
内容content://com.demo.app.provider/table1/1对应的MIME类型vnd.android.cursor.item/vnd.com.demo.app.provider.table1

创建自己的内容提供器

DatabaseProvider内容提供器:

public class DatabaseProvider extends ContentProvider {
    public static final int BOOK_DIR = 0;
    public static final int BOOK_ITEM = 1;
    public static final int CATEGORY_DIR = 3;
    public static final int CATEGORY_ITEM = 4;
    public static final String AUTHORITY = "com.jyoryo.app.android.study.provider";

    private MyDatabaseHelper dbHelper;
    private static UriMatcher uriMatcher;
    static {
        uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
        uriMatcher.addURI(AUTHORITY, "book", BOOK_DIR);
        uriMatcher.addURI(AUTHORITY, "book/#", BOOK_ITEM);
        uriMatcher.addURI(AUTHORITY, "category", CATEGORY_DIR);
        uriMatcher.addURI(AUTHORITY, "category/#", CATEGORY_ITEM);
    }

    public DatabaseProvider() {
    }

    @Override
    public boolean onCreate() {
        dbHelper = new MyDatabaseHelper(getContext(), "BookStore.db", null, 1);
        return true;
    }

    @Override
    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
        SQLiteDatabase db = dbHelper.getReadableDatabase();
        Cursor cursor = null;
        switch (uriMatcher.match(uri)) {
            case BOOK_DIR:
                cursor = db.query("book", projection, selection, selectionArgs, null, null, sortOrder);
                break;
            case BOOK_ITEM:
                String bookId = uri.getPathSegments().get(1);
                cursor = db.query("book", projection, "id = ?", new String[] {bookId}, null, null, sortOrder);
                break;
            case CATEGORY_DIR:
                cursor = db.query("category", projection, selection, selectionArgs, null, null, sortOrder);
                break;
            case CATEGORY_ITEM:
                String categoryId = uri.getPathSegments().get(1);
                cursor = db.query("category", projection, "id = ?", new String[] {categoryId}, null, null, sortOrder);
                break;
            default:
                break;
        }
        return  cursor;
    }

    @Override
    public Uri insert(Uri uri, ContentValues values) {
        SQLiteDatabase db = dbHelper.getWritableDatabase();
        Uri urlReturn = null;
        switch (uriMatcher.match(uri)) {
            case BOOK_DIR:
            case BOOK_ITEM:
                long newBookId = db.insert("book", null, values);
                urlReturn = Uri.parse("content://" + AUTHORITY + "/book/" + newBookId);
                break;
            case CATEGORY_DIR:
            case CATEGORY_ITEM:
                long newCategoryId = db.insert("category", null, values);
                urlReturn = Uri.parse("content://" + AUTHORITY + "/category/" + newCategoryId);
                break;
            default:
                break;
        }
        return  urlReturn;
    }

    @Override
    public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
        SQLiteDatabase db = dbHelper.getWritableDatabase();
        int updateRows = 0;
        switch (uriMatcher.match(uri)) {
            case BOOK_DIR:
                updateRows = db.update("book", values, selection, selectionArgs);
                break;
            case BOOK_ITEM:
                String bookId = uri.getPathSegments().get(1);
                updateRows = db.update("book", values, "id = ?", new String[] {bookId});
                break;
            case CATEGORY_DIR:
                updateRows = db.update("category", values, selection, selectionArgs);
                break;
            case CATEGORY_ITEM:
                String categoryId = uri.getPathSegments().get(1);
                updateRows = db.update("category", values, "id = ?", new String[] {categoryId});
                break;
            default:
                break;
        }
        return updateRows;
    }

    @Override
    public int delete(Uri uri, String selection, String[] selectionArgs) {
        SQLiteDatabase db = dbHelper.getWritableDatabase();
        int deleteRows = 0;
        switch (uriMatcher.match(uri)) {
            case BOOK_DIR:
                deleteRows = db.delete("book", selection, selectionArgs);
                break;
            case BOOK_ITEM:
                String bookId = uri.getPathSegments().get(1);
                deleteRows = db.delete("book", "id = ?", new String [] {bookId});
                break;
            case CATEGORY_DIR:
                deleteRows = db.delete("category", selection, selectionArgs);
                break;
            case CATEGORY_ITEM:
                String categoryId = uri.getPathSegments().get(1);
                deleteRows = db.delete("category", "id = ?", new String [] {categoryId});
                break;
            default:break ;
        }
        return 0;
    }

    @Override
    public String getType(Uri uri) {
        switch (uriMatcher.match(uri)) {
            case BOOK_DIR:
                return "vnd.android.cursor.dir/vnd.com.jyoryo.app.android.study.provider.book";
            case BOOK_ITEM:
                return "vnd.android.cursor.item/vnd.com.jyoryo.app.android.study.provider.book";
            case CATEGORY_DIR:
                return "vnd.android.cursor.dir/vnd.com.jyoryo.app.android.study.provider.category";
            case CATEGORY_ITEM:
                return "vnd.android.cursor.item/vnd.com.jyoryo.app.android.study.provider.category";
            default:
                return null;
        }
    }
}

MyDatabaseHelper数据库帮助类:

public class MyDatabaseHelper extends SQLiteOpenHelper {
    public static final String CREATE_BOOK = "CREATE TABLE `book` ("
            + " `id` INTEGER PRIMARY KEY AUTOINCREMENT,"
            + " `author` TEXT,"
            + " `price` REAL,"
            + " `pages` INTEGER,"
            + " `name` TEXT"
            + ")";
    public static final String CREATE_CATEGORY = "CREATE TABLE `category` ("
            + " `id` INTEGER PRIMARY KEY AUTOINCREMENT,"
            + " `name` TEXT,"
            + " `code` INTEGER)";
    private Context mContext;

    public MyDatabaseHelper(Context context, String name, SQLiteDatabase.CursorFactory factory, int version) {
        super(context, name, factory, version);
        mContext = context;
    }

    @Override
    public void onCreate(SQLiteDatabase db) {
        db.execSQL(CREATE_BOOK);
        db.execSQL(CREATE_CATEGORY);
    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        db.execSQL("drop table if exists book");
        db.execSQL("drop table if exists category");
        onCreate(db);
    }
}

使用内容提供器:

public class MainActivity extends AppCompatActivity {
    private static final String TAG = "MainActivity";
    private String newId;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Button addData = (Button) findViewById(R.id.add_data),
                queryData = (Button) findViewById(R.id.query_data),
                updateData = (Button) findViewById(R.id.update_data),
                deleteData = (Button) findViewById(R.id.delete_data);
                
        addData.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Uri uri = Uri.parse("content://com.jyoryo.app.android.study.provider/book");
                ContentValues values = new ContentValues();
                values.put("name", "name1");
                values.put("author", "author1");
                values.put("pages", 100);
                values.put("price", 19.99D);
                Uri newUri = getContentResolver().insert(uri, values);
                newId = newUri.getPathSegments().get(1);
            }
        });
        
        queryData.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Uri uri = Uri.parse("content://com.jyoryo.app.android.study.provider/book");
                Cursor cursor = getContentResolver().query(uri, null, null, null, null);
                if(null != cursor) {
                    while (cursor.moveToNext()) {
                        String name = cursor.getString(cursor.getColumnIndex("name"));
                        String author = cursor.getString(cursor.getColumnIndex("author"));
                        int pages = cursor.getInt(cursor.getColumnIndex("pages"));
                        double price = cursor.getDouble(cursor.getColumnIndex("price"));
                        Log.d(TAG, "Book Info---Name:" + name + "; Author:" + author + "; Pages:" + pages + "; Price:" + price);
                    }
                }
            }
        });
        
        updateData.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Uri uri = Uri.parse("content://com.jyoryo.app.android.study.provider/book/" + newId);
                ContentValues values = new ContentValues();
                values.put("name", "new_name1");
                values.put("pages", 6666);
                values.put("price", 66.66D);
                getContentResolver().update(uri, values, null, null);
            }
        });
        
        deleteData.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Uri uri = Uri.parse("content://com.jyoryo.app.android.study.provider/book/" + newId);
                getContentResolver().delete(uri, null, null);
            }
        });
    }
}

AndroidManifest.xml中声明内容提供器:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.jyoryo.app.android.study">

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <provider
            android:name=".DatabaseProvider"
            android:authorities="com.jyoryo.app.android.study.provider"
            android:enabled="true"
            android:exported="true"></provider>

        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

标签: android

添加新评论