简单起见,咱们仍是在上一章中 DatabaseTest 项目的基础上继续开发,经过内容提供器 来给它加入外部访问接口。android
打开 DatabaseTest 项目,首先将 MyDatabaseHelper 中使用 Toast 弹出建立数据库成功的提示去除掉,由于跨程序访问时咱们不能直接使用 Toast。而后添加 一个 DatabaseProvider 类,代码以下所示:数据库
public class DatabaseProvider extends ContentProvider {app
public static final int BOOK_DIR = 0; ide
public static final int BOOK_ITEM = 1; 布局
public static final int CATEGORY_DIR = 2; 3d
public static final int CATEGORY_ITEM = 3;日志
public static final String AUTHORITY = "com.example.databasetest.provider";orm
private static UriMatcher uriMatcher;xml
private MyDatabaseHelper dbHelper; 对象
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);
}
@Override
public boolean onCreate() {
dbHelper = new MyDatabaseHelper(getContext(), "BookStore.db", null, 2);
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 uriReturn = null;
switch (uriMatcher.match(uri)) {
case BOOK_DIR:
case BOOK_ITEM:
long newBookId = db.insert("Book", null, values);
uriReturn = Uri.parse("content://" + AUTHORITY + "/book/" +newBookId);
break;
case CATEGORY_DIR:
case CATEGORY_ITEM:
long newCategoryId = db.insert("Category", null, values);
uriReturn = Uri.parse("content://" + AUTHORITY + "/category/" +newCategoryId);
break;
default:
break;
}
return uriReturn;
}
@Override
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
// 更新数据
SQLiteDatabase db = dbHelper.getWritableDatabase();
int updatedRows = 0;
switch (uriMatcher.match(uri)) {
case BOOK_DIR:
updatedRows = db.update("Book", values, selection, selectionArgs);
break;
case BOOK_ITEM:
String bookId = uri.getPathSegments().get(1);
updatedRows = db.update("Book", values, "id = ?", new String[]{ bookId });
break;
case CATEGORY_DIR:
updatedRows = db.update("Category", values, selection, selectionArgs);
break;
case CATEGORY_ITEM:
String categoryId = uri.getPathSegments().get(1);
updatedRows = db.update("Category", values, "id = ?", new String[]{ categoryId });
break;
default:
break;
}
return updatedRows;
}
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
// 删除数据
SQLiteDatabase db = dbHelper.getWritableDatabase();
int deletedRows = 0;
switch (uriMatcher.match(uri)) {
case BOOK_DIR:
deletedRows = db.delete("Book", selection, selectionArgs);
break;
case BOOK_ITEM:
String bookId = uri.getPathSegments().get(1);
deletedRows = db.delete("Book", "id = ?", new String[] { bookId });
break;
case CATEGORY_DIR:
deletedRows = db.delete("Category", selection, selectionArgs);
break;
case CATEGORY_ITEM:
String categoryId = uri.getPathSegments().get(1);
deletedRows = db.delete("Category", "id = ?", new String[]{ categoryId });
break;
default:
break;
}
return deletedRows;
}
@Override
public String getType(Uri uri) {
switch (uriMatcher.match(uri)) {
case BOOK_DIR:
return "vnd.android.cursor.dir/vnd.com.example.databasetest. provider.book";
case BOOK_ITEM:
return "vnd.android.cursor.item/vnd.com.example.databasetest. provider.book";
case CATEGORY_DIR:
return "vnd.android.cursor.dir/vnd.com.example.databasetest. provider.category";
case CATEGORY_ITEM:
return "vnd.android.cursor.item/vnd.com.example.databasetest. provider.category";
}
return null;
}
}
代码虽然很长,不过不用担忧,这些内容都很是容易理解,由于使用到的所有都是上一 小节中咱们学到的知识。首先在类的一开始,一样是定义了四个常量,分别用于表示访问 Book 表中的全部数据、访问 Book 表中的单条数据、访问 Category 表中的全部数据和访问 Category 表中的单条数据。而后在静态代码块里对 UriMatcher 进行了初始化操做,将指望匹 配的几种 URI 格式添加了进去。
接下来就是每一个抽象方法的具体实现了,先来看下 onCreate()方法,这个方法的代码很 短,就是建立了一个 MyDatabaseHelper 的实例,而后返回 true 表示内容提供器初始化成功, 这时数据库就已经完成了建立或升级操做。
接着看一下 query()方法,在这个方法中先获取到了 SQLiteDatabase 的实例,而后根据 传入的 Uri 参数判断出用户想要访问哪张表,再调用 SQLiteDatabase 的 query()进行查询,并 将 Cursor 对象返回就行了。注意当访问单条数据的时候有一个细节,这里调用了 Uri 对象的 getPathSegments()方法,它会将内容 URI 权限以后的部分以“/”符号进行分割,并把分割后 的结果放入到一个字符串列表中,那这个列表的第 0 个位置存放的就是路径,第 1 个位置存 放的就是 id 了。获得了 id 以后,再经过 selection 和 selectionArgs 参数进行约束,就实现了 查询单条数据的功能。
再日后就是 insert()方法,一样它也是先获取到了 SQLiteDatabase 的实例,而后根据传入 的 Uri 参数判断出用户想要往哪张表里添加数据,再调用 SQLiteDatabase 的 insert()方法进行添加就能够了。注意 insert()方法要求返回一个可以表示这条新增数据的 URI,因此咱们还须要调用 Uri.parse()方法来将一个内容 URI 解析成 Uri 对象,固然这个内容 URI 是以新增数据 的 id 结尾的。
接下来就是 update()方法了,相信这个方法中的代码已经彻底难不倒你了。也是先获取 SQLiteDatabase 的实例,而后根据传入的 Uri 参数判断出用户想要更新哪张表里的数据,再 调用 SQLiteDatabase 的 update()方法进行更新就行了,受影响的行数将做为返回值返回。
下面是 delete()方法,是否是感受越到后面越轻松了?由于你已经渐入佳境,真正地找 到窍门了。这里仍然是先获取到 SQLiteDatabase 的实例,而后根据传入的 Uri 参数判断出用 户想要删除哪张表里的数据,再调用 SQLiteDatabase 的 delete()方法进行删除就行了,被删 除的行数将做为返回值返回。
最后是 getType()方法,这个方法中的代码彻底是按照上一节中介绍的格式规则编写的, 相信已经没有什么解释的必要了。
这样咱们就将内容提供器中的代码所有编写完了,不过离实现跨程序数据共享的功能还 差了一小步,由于还须要将内容提供器在 AndroidManifest.xml 文件中注册才能够,以下所示:
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.databasetest"
android:versionCode="1" android:versionName="1.0" >
……
<application android:allowBackup="true" android:icon="@drawable/ic_launcher" android:label="@string/app_name" android:theme="@style/AppTheme" >
……
<provider android:name="com.example.databasetest.DatabaseProvider" android:authorities="com.example.databasetest.provider" >
</provider>
</application>
</manifest>
能够看到,这里咱们使用了<provider>标签来对 DatabaseProvider 这个内容提供器进行注 册,在 android:name 属性中指定了该类的全名,又在 android:authorities 属性中指定了该内容 提供器的权限。
如今 DatabaseTest 这个项目就已经拥有了跨程序共享数据的功能了,咱们赶快来尝试一下。首先须要将 DatabaseTest 程序从模拟器中删除掉,以防止上一章中产生的遗留数据对我
们形成干扰。而后运行一下项目,将 DatabaseTest 程序从新安装在模拟器上了。接着关闭掉 DatabaseTest 这个项目,并建立一个新项目 ProviderTest ,咱们就将经过这个程序去访问 DatabaseTest 中的数据。
仍是先来编写一下布局文件吧,修改 activity_main.xml 中的代码,以下所示:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"
android:orientation="vertical" >
<Button android:id="@+id/add_data" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="Add To Book" />
<Button android:id="@+id/query_data" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="Query From Book" />
<Button android:id="@+id/update_data" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="Update Book" />
<Button android:id="@+id/delete_data" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="Delete From Book" />
</LinearLayout>
布局文件很简单,里面放置了四个按钮,分别用于添加、查询、修改和删除数据的。然 后修改 MainActivity 中的代码,以下所示:
public class MainActivity extends Activity {
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);
addData.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
// 添加数据
Uri uri = Uri.parse("content://com.example.databasetest. provider/book");
ContentValues values = new ContentValues();
values.put("name", "A Clash of Kings");
values.put("author", "George Martin");
values.put("pages", 1040);
values.put("price", 22.85);
Uri newUri = getContentResolver().insert(uri, values);
newId = newUri.getPathSegments().get(1);
}
});
Button queryData = (Button) findViewById(R.id.query_data);
queryData.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
// 查询数据
Uri uri = Uri.parse("content://com.example.databasetest. provider/book");
Cursor cursor = getContentResolver().query(uri, null, null,null, null);
if (cursor != null) {
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("MainActivity", "book name is " + name);
Log.d("MainActivity", "book author is " + author);
Log.d("MainActivity", "book pages is " + pages);
Log.d("MainActivity", "book price is " + price);
}
}
}
cursor.close();
}
Button updateData = (Button) findViewById(R.id.update_data);
updateData.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
// 更新数据
Uri uri = Uri.parse("content://com.example.databasetest. provider/book/" + newId);
ContentValues values = new ContentValues();
values.put("name", "A Storm of Swords");
values.put("pages", 1216);
values.put("price", 24.05);
getContentResolver().update(uri, values, null, null);
}
});
Button deleteData = (Button) findViewById(R.id.delete_data);
deleteData.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
// 删除数据
Uri uri = Uri.parse("content://com.example.databasetest. provider/book/" + newId);
getContentResolver().delete(uri, null, null);
}
});
}
}
能够看到,咱们分别在这四个按钮的点击事件里面处理了增删改查的逻辑。添加数据的时候,首先调用了 Uri.parse()方法将一个内容 URI 解析成 Uri 对象,而后把要添加的数据都 存放到 ContentValues 对象中,接着调用 ContentResolver 的 insert()方法执行添加操做就能够 了。注意 insert()方法会返回一个 Uri 对象,这个对象中包含了新增数据的 id,咱们经过 getPathSegments()方法将这个 id 取出,稍后会用到它。
查询数据的时候,一样是调用了 Uri.parse()方法将一个内容 URI 解析成 Uri 对象,而后 调用 ContentResolver 的 query()方法去查询数据,查询的结果固然仍是存放在 Cursor 对象中 的。以后对 Cursor 进行遍历,从中取出查询结果,并一一打印出来。
更新数据的时候,也是先将内容 URI 解析成 Uri 对象,而后把想要更新的数据存放到 ContentValues 对象中,再调用 ContentResolver 的 update()方法执行更新操做就能够了。注意 这里咱们为了避免想让 Book 表中其余的行受到影响,在调用 Uri.parse()方法时,给内容 URI 的尾部增长了一个 id,而这个 id 正是添加数据时所返回的。这就表示咱们只但愿更新刚刚 添加的那条数据,Book 表中的其余行都不会受影响。
删除数据的时候,也是使用一样的方法解析了一个以 id 结尾的内容 URI,而后调用 ContentResolver 的 delete()方法执行删除操做就能够了。因为咱们在内容 URI 里指定了一个 id,所以只会删掉拥有相应 id 的那行数据,Book 表中的其余数据都不会受影响。
如今运行一下 ProviderTest 项目,会显示如图 7.4 所示的界面。
图 7.4
点击一下 Add To Book 按钮,此时数据就应该已经添加到 DatabaseTest 程序的数据库中了,咱们能够经过点击 Query From Book 按钮来检查一下,打印日志如图 7.5 所示。
图 7.5
而后点击一下 Update Book 按钮来更新数据,再点击一下 Query From Book 按钮进行检 查,结果如图 7.6 所示。
图 7.6
最后点击 Delete From Book 按钮删除数据,此时再点击 Query From Book 按钮就查询不 到数据了。
由此能够看出,咱们的跨程序共享数据功能已经成功实现了!如今不只是 ProviderTest 程序,任何一个程序均可以轻松访问 DatabaseTest 中的数据,并且咱们还丝绝不用担忧隐私 数据泄漏的问题。