Android内容提供器——创建自己的内容提供器

男儿欲遂平生志,六经勤向窗前读。这篇文章主要讲述Android内容提供器——创建自己的内容提供器相关的知识,希望能为你提供帮助。
创建自己的内容提供器很简单,只需要新建一个类继承ContentProvider类,并重写其中的6个抽象方法即可。(需要全部重写)
 
一 URI的写法
一个标准的内容URI写法有两种:
1.路径结尾,表示访问表中所有内容:

content://com.example.app.provider/table1// * 可以代表任意长度的任意字符,所以也可以写成下面这样 content://com.example.app.provider/*

2.id结尾,表示访问表中特定id的内容:
content://com.example.app.provider/table1/1// # 可以代表任意长度的数字,所以也可以写成下面这样 content://com.example.app.provider/table1/#

 
二 借助UriMatcher类
在UriMatcher类中提供了addURI()方法,传入三个参数,分别是authority,path,和一个自定义代码。
在UriMatcher类中提供了match()方法,传入一个Uri对象,返回值就是addUri中该uri对应的自定义代码。利用这个自定义代码,我们就可以判断出调用期望访问的数据。
Android内容提供器——创建自己的内容提供器

文章图片

代码如下:
1 public class MyProvider extends ContentProvider { 2 3 4public static final int TABLE1_DIR = 0; //访问表1的所有数据 5public static final int TABLE1_ITEM = 1; //访问表1的单条数据 6public static final int TABLE2_DIR = 2; 7public static final int TABLE2_ITEM = 3; 8 9private static UriMatcher uriMatcher; 10 11/*静态代码块,通过adduri方法,将期望匹配的uri格式传入,并定义返回的自定义代码*/ 12static { 13uriMatcher = new UriMatcher(UriMatcher.NO_MATCH); 14uriMatcher.addURI("com.example.app.provider","table1",TABLE1_DIR); 15uriMatcher.addURI("com.example.app.provider","table1/#",TABLE1_ITEM); 16uriMatcher.addURI("com.example.app.provider","table2",TABLE2_DIR); 17uriMatcher.addURI("com.example.app.provider","table2/#",TABLE2_ITEM); 18} 19 20 21@Override 22public boolean onCreate() { 23return false; 24} 25 26@Nullable 27@Override 28public Cursor query(@NonNull Uri uri, @Nullable String[] strings, @Nullable String s, @Nullable String[] strings1, @Nullable String s1) { 29switch (uriMatcher.match(uri)){ 30case TABLE1_DIR: 31//查询table1表中的所有数据 32break; 33case TABLE1_ITEM: 34//查询table1表中的单条数据 35break; 36case TABLE2_DIR: 37//查询table2表中的所有数据 38break; 39case TABLE2_ITEM: 40//查询table2表中的单条数据 41break; 42default: 43break; 44} 45return null; 46} 47··· 48 )

除此之外,还有重写getType()方法,用于获得uri对象对应的mime类型,一个内容URI对应的MIME字符串又三部分组成:
//如果是路径结尾 vnd. + android.cursor.dir/ + vnd.< authority> .< paht> //如: vnd.android.cursor.dir/vnd.com.example.app.provider.table1//如果是id结尾 vnd. + android.cursor.item/ + vnd.< authority> .< paht> //如 vnd.android.cursor.item/vnd.com.example.app.provider.table1

代码如下:
1@Override 2public String getType(@NonNull Uri uri) { 3switch (uriMatcher.match(uri)){ 4case TABLE1_DIR: 5return "vnd.android.cursor.dir/vnd.com.example.app.provider.table1"; 6case TABLE1_ITEM: 7return "vnd.android.cursor.item/vnd.com.example.app.provider.table1"; 8case TABLE2_DIR: 9return "vnd.android.cursor.dir/vnd.com.example.app.provider.table2"; 10case TABLE2_ITEM: 11return "vnd.android.cursor.item/vnd.com.example.app.provider.table2"; 12default: 13break; 14} 15return null; 16}

 
三 实例使用内容提供器
构造一个数据库的内容提供器如下:
1 public class MyContentProvider extends ContentProvider { 2public MyContentProvider() { 3} 4 5public static final int BOOK_DIR = 0; 6public static final int BOOK_ITEM = 1; 7public static final int CATEGORY_DIR = 2; 8public static final int CATEGORY_ITEM = 3; 9 10public static final String AUTHORITY = "com.example.databasetest,provider"; 11private static UriMatcher uriMatcher; 12private MyDatabaseHelper dbHelper; 13 14static { 15uriMatcher = new UriMatcher(UriMatcher.NO_MATCH); 16uriMatcher.addURI(AUTHORITY, "book", BOOK_DIR); 17uriMatcher.addURI(AUTHORITY, "book/#", BOOK_ITEM); 18uriMatcher.addURI(AUTHORITY, "category", CATEGORY_DIR); 19uriMatcher.addURI(AUTHORITY, "category/#", CATEGORY_ITEM); 20} 21 22@Override 23public boolean onCreate() { 24// TODO: Implement this to initialize your content provider on startup. 25dbHelper = new MyDatabaseHelper(getContext(), "BookStore,db", null, 2); 26return true; 27} 28 29@Override 30public Cursor query(Uri uri, String[] projection, String selection, 31String[] selectionArgs, String sortOrder) { 32// TODO: Implement this to handle query requests from clients. 33Cursor cursor = null; 34SQLiteDatabase db = dbHelper.getReadableDatabase(); 35switch (uriMatcher.match(uri)) { 36case BOOK_DIR: 37cursor = db.query("Book", projection, selection, selectionArgs, null, null, sortOrder); 38break; 39case BOOK_ITEM: 40//getPathSegments将path部分以/为界分开,并将其放入字符串数据中,所以0对应path路径,1对应具体id 41String bookId = uri.getPathSegments().get(1); 42cursor = db.query("Book", projection, "id = ?", new String[]{bookId}, null, null, sortOrder); 43break; 44case CATEGORY_DIR: 45cursor = db.query("Category", projection, selection, selectionArgs, null, null, sortOrder); 46break; 47case CATEGORY_ITEM: 48String categoryId = uri.getPathSegments().get(1); 49cursor = db.query("Category", projection, "id = ?", new String[]{categoryId}, null, null, sortOrder); 50break; 51default: 52break; 53} 54return cursor; 55} 56 57@Override 58public Uri insert(Uri uri, ContentValues values) { 59// TODO: Implement this to handle requests to insert a new row. 60SQLiteDatabase db = dbHelper.getWritableDatabase(); 61Uri uriReturn = null; 62switch (uriMatcher.match(uri)) { 63case BOOK_DIR: 64case BOOK_ITEM: 65long newBookId = db.insert("Book", null, values); 66uriReturn = Uri.parse("content://" + AUTHORITY + "/book/" + newBookId); 67break; 68case CATEGORY_DIR: 69case CATEGORY_ITEM: 70long newCategoryId = db.insert("Category", null, values); 71uriReturn = Uri.parse("content://" + AUTHORITY + "/category/" + newCategoryId); 72break; 73default: 74break; 75} 76return uriReturn; 77} 78 79@Override 80public int update(Uri uri, ContentValues values, String selection, 81String[] selectionArgs) { 82// TODO: Implement this to handle requests to update one or more rows. 83SQLiteDatabase db = dbHelper.getWritableDatabase(); 84int updatedRows = 0; 85switch (uriMatcher.match(uri)) { 86case BOOK_DIR: 87updatedRows = db.update("Book", values, selection, selectionArgs); 88break; 89case BOOK_ITEM: 90String bookId = uri.getPathSegments().get(1); 91updatedRows = db.update("Book", values, "id=?", new String[]{bookId}); 92break; 93case CATEGORY_DIR: 94updatedRows = db.update("Category", values, selection, selectionArgs); 95break; 96case CATEGORY_ITEM: 97String CategoryId = uri.getPathSegments().get(1); 98updatedRows = db.update("Category", values, "id=?", new String[]{CategoryId}); 99break; 100default: 101break; 102} 103return updatedRows; 104} 105 106@Override 107public int delete(Uri uri, String selection, String[] selectionArgs) { 108// Implement this to handle requests to delete one or more rows. 109SQLiteDatabase db = dbHelper.getWritableDatabase(); 110int deletedRows = 0; 111switch (uriMatcher.match(uri)) { 112case BOOK_DIR: 113deletedRows = db.delete("Book", selection, selectionArgs); 114break; 115case BOOK_ITEM: 116String deletedBookId = uri.getPathSegments().get(1); 117deletedRows = db.delete("Book", "id=?", new String[]{deletedBookId}); 118break; 119case CATEGORY_DIR: 120deletedRows = db.delete("Category", selection, selectionArgs); 121break; 122case CATEGORY_ITEM: 123String deletedCategoryId = uri.getPathSegments().get(1); 124deletedRows = db.delete("Category", "id=?", new String[]{deletedCategoryId}); 125break; 126default: 127break; 128} 129return deletedRows; 130} 131 132@Override 133public String getType(Uri uri) { 134// TODO: Implement this to handle requests for the MIME type of the data 135// at the given URI.\\ 136switch (uriMatcher.match(uri)) { 137case BOOK_DIR: 138return "vnd.android.cursor.dir/vnd.com.example.app.provider.Book"; 139case BOOK_ITEM: 140return "vnd.android.cursor.item/vnd.com.example.app.provider.Book"; 141case CATEGORY_DIR: 142return "vnd.android.cursor.dir/vnd.com.example.app.provider.category"; 143case CATEGORY_ITEM: 144return "vnd.android.cursor.item/vnd.com.example.app.provider.category"; 145default: 146break; 147} 148return null; 149 150} 151 }

代码整体可以分为几部分,首先是新建常量,用于访问book和category里中的数据,在静态代码块里对UriMatcher进行了addUri的配置。
在onCreate()方法中,创建了一个MyDatabaseHelper的实例,返回true表示初始化成功。
查询query()方法中,先获取到了SQLiteDatabase的实例,传入uri参数知道用户想访问的数据,然后在调用sqlitedatabase的query()方法进行查询,并将查询的cursor参数返回。如果是访问单条的数据,使用getPathSegments将path部分以“/”为界分开,并将其放入字符串数据中,所以0对应path路径,1对应具体id。得到id通过selection和selectionArgs来进行约束查询。最后返回cursor值。
新增insert()方法中,先获取到了SQLiteDatabase的实例,传入uri参数知道用户想往那张表加数据,然后在调用SQliteDatabase的insert()方法进行添加,返回的是一个能够表示这个新增数据的uri,这个内容uri是以新增数据的id结尾。所以还需要调用uri.parse()方法将其进行解析成uri对象。
更新update()方法中,先获取到了SQLiteDatabase的实例,传入uri参数知道用户想访问的数据,然后在调用sqlitedatabase的update()方法进行更新,受影响的行数作为返回值返回。
删除delete()方法中,先获取到了SQLiteDatabase的实例,传入uri参数知道用户想访问的数据,然后在调用sqlitedatabase的delete()方法进行删除,被删除的行数作为返回值返回。
最后是getType()方法,和之前的getType方法完全相同。
 
另外,内容提供器需要在AndroidManifest.xml中注册,一般as已经帮我们进行注册好了。
 
构建另外一个应用来使用我们的databasetest的内容提供器,新建一个应用,页面为四个按钮,布局如下:
1 < LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 2android:layout_width="match_parent" 3android:layout_height="match_parent" 4android:orientation="vertical"> 5 6< Button 7android:id="@+id/add_data" 8android:layout_width="match_parent" 9android:layout_height="wrap_content" 10android:text="add to book"/> 11 12< Button 13android:id="@+id/query_data" 14android:text="query from book" 15android:layout_width="match_parent" 16android:layout_height="wrap_content" /> 17< Button 18android:id="@+id/update_data" 19android:text="update book" 20android:layout_width="match_parent" 21android:layout_height="wrap_content" /> 22< Button 23android:id="@+id/delete_data" 24android:text="delete from book" 25android:layout_width="match_parent" 26android:layout_height="wrap_content" /> 27 < /LinearLayout>

【Android内容提供器——创建自己的内容提供器】然后在主函数中为四个按钮注册点击事件,并赋予相应的逻辑。
1 public class MainActivity extends AppCompatActivity { 2 3private String newId; 4 5static final String path = "content://com.example.databasetest.provider/book"; 6 7@Override 8protected void onCreate(Bundle savedInstanceState) { 9super.onCreate(savedInstanceState); 10setContentView(R.layout.activity_main); 11 12Button addData = https://www.songbingjia.com/android/(Button) findViewById(R.id.add_data); 13addData.setOnClickListener(new View.OnClickListener() { 14@Override 15public void onClick(View view) { 16Uri uri = Uri.parse(path); 17ContentValues values = new ContentValues(); 18values.put("name", "a clash"); 19values.put("author", "Mask"); 20values.put("pages", 1002); 21values.put("price", 12.22); 22Uri newUri = getContentResolver().insert(uri, values); 23newId = newUri.getPathSegments().get(1); 24} 25}); 26 27Button queryData = https://www.songbingjia.com/android/(Button) findViewById(R.id.query_data); 28queryData.setOnClickListener(new View.OnClickListener() { 29@Override 30public void onClick(View view) { 31Cursor cursor = null; 32Uri uri = Uri.parse(path); 33cursor = getContentResolver().query(uri, null, null, null, null); 34if (cursor != null) { 35while (cursor.moveToNext()) { 36String name = cursor.getString(cursor.getColumnIndex("name")); 37String author = cursor.getString(cursor.getColumnIndex("author")); 38int pages = cursor.getInt(cursor.getColumnIndex("pages")); 39double price = cursor.getDouble(cursor.getColumnIndex("price")); 40Log.d("MainActivity", "book name is " + name); 41Log.d("MainActivity", "book author is " + author); 42Log.d("MainActivity", "book pages is " + pages); 43Log.d("MainActivity", "book price is " + price); 44}

    推荐阅读