android: 实现跨程序数据共享
简单起见,我们还是在上一章中 DatabaseTest 项目的基础上继续开发,通过内容提供器 来给它加入外部访问接口。
打开 DatabaseTest 项目,首先将 MyDatabaseHelper 中使用 Toast 弹出创建数据库成功的提示去除掉,因为跨程序访问时我们不能直接使用 Toast。然后添加 一个 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 = 2;
public static final int CATEGORY_ITEM = 3;
public static final String AUTHORITY = "com.example.databasetest.provider";
private static UriMatcher uriMatcher;
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 中的数据,而且我们还丝毫不用担心隐私
数据泄漏的问题。
android: 实现跨程序数据共享的更多相关文章
- Android学习--跨程序共享数据之内容提供其探究
什么是内容提供器? 跨程序共享数据之内容提供器,这是个什么功能?看到这个名称的时候最能给我们提供信息的应该是“跨程序”这个词了,是的重点就是这个词,这个内容提供器的作用主要是用于在不同的引用程序之间实 ...
- android 多应用程序数据共享 ContentProvider和ContentResolver
android 没有一个可以将所有应用程序数据统一放置的地方,即两个应用程序间的数据不能共享.但ContentProvider与ContentResolver可以解决多应用程序数据共享. 我们都知 ...
- android——实现跨程序访问数据
使用之前的SQLite存储的应用程序.首先需要在这个应用程序中创建内容提供器,右击com.example.administrator.exp7包→New→Other→Content Provider, ...
- Android入门(十四)内容提供器-实现跨程序共享实例
原文链接:http://www.orlion.ga/661/ 打开SQLite博文中创建的 DatabaseDemo项目,首先将 MyDatabaseHelper中使用 Toast弹出创建数据库成功的 ...
- Android学习笔记(二十一)——实战:程序数据共享
//此系列博文是<第一行Android代码>的学习笔记,如有错漏,欢迎指正! 我们继续在Database项目的基础上继续开发,通过内容提供器来给它加入外部访问接口.首先将 MyDataba ...
- pc端和android端应用程序测试有什么区别?(ps面试题)
pc端和android端应用程序测试有什么区别?(ps面试题) [VIP7]大连-凭海临风(215687736) 2014/4/10 8:56:171.测试环境不同PC平台一般都是windows an ...
- 分享50款 Android 移动应用程序图标【下篇】
在这个移动程序流行的时代,持续增长的应用程序经济充满了商业机遇.任何对应用程序设计感兴趣的人,将会喜欢上这里的50个独特的 Android 应用程序图标.这些例子中的图标能够让应用程序的设计更具吸引力 ...
- 分享50款 Android 移动应用程序图标【上篇】
在这个移动程序流行的时代,持续增长的应用程序经济充满了商业机遇.任何对应用程序设计感兴趣的人,将会喜欢上这里的50个独特的 Android 应用程序图标.这些例子中的图标能够让应用程序的设计更具吸引力 ...
- Java和Android Http连接程序:使用java.net.URL 下载服务器图片到客户端
Java和Android Http连接程序:使用java.net.URL 下载服务器图片到客户端 本博客前面博文中利用org.apache.http包中API进行Android客户端HTTP连接的例子 ...
随机推荐
- BIEE 11g学习
biee 11g学习1. 创建资料档案库文件(RPD) 文件数据库 1.1 创建数据源连接 运行Net Manager 用于BIEE的数据库服务 1.2 模型的建立 1.运行o ...
- 用nodej和glub-watcher写的监听go 项目自动编译,很鸡肋
glub 一般都是很轻量的编译. go太重了,改一小个部分,就编译的话,多数是编译失败. 而且很消耗性能,还没想到完美的优化办法. 暂时用个定时器 监听2秒,停止1秒,如此循环,会减少些 “无效”的编 ...
- 线程学习笔记 等待句柄和线程池(摘自https://blog.gkarch.com/threading/part2.html#manualresetevent)
//如果你的应用有很多线程,这些线程大部分时间都在阻塞,那么可以通过调用ThreadPool.RegisterWaitForSingleObject来减少资源消耗.这个方法接受一个委托,它会在向等待句 ...
- SQLServer 脚本测试
最近在做大数据同步的工作.很少数据需要特殊清洗算法,每次测试,都测试全部数据,浪费时间,可以只测试那些特殊数据即可(切记).
- 复利test
因为上节课老师有给我们讲了单元测试的概念及其基本操作,我就对我之前的复利计算器进行了改进和测试.因为从控制台的简单输入输出对单元测试来说不够完善,便参考了其他同学的优秀编程又重新写了一个复利计算器的框 ...
- UIColor 分类 16进制转 RGB
.h #import <UIKit/UIKit.h> @interface UIColor (WJ) + (UIColor *)colorWithWJString:(NSString *) ...
- centos6.5中 nginx-1.6.3 编译安装
参考来源:http://nginx.org/en/docs/configure.html nginx-1.6.3 编译安装:1) ./configure --help 查看编译选项 2) 需要安装一下 ...
- 斯坦福第十四课:降维(Dimensionality Reduction)
14.1 动机一:数据压缩 14.2 动机二:数据可视化 14.3 主成分分析问题 14.4 主成分分析算法 14.5 选择主成分的数量 14.6 重建的压缩表示 14.7 主成分分析法 ...
- Replace Pioneer 续用2
软件介绍(摘自百度百科) Replace Pioneer(中文名:替换先锋)是Mind Pioneer出品的一款共享软件. Replace Pioneer是一款与众不同的专业文本批量替换和处理软 ...
- Spring MVC 3 深入总结
一.前言: 大家好,Spring3 MVC是非常优秀的MVC框架,由其是在3.0版本发布后,现在有越来越多的团队选择了Spring3 MVC了.Spring3 MVC结构简单,应了那句话简单就是美 ...