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 中的数据,而且我们还丝毫不用担心隐私
数据泄漏的问题。

时间: 2024-10-19 13:14:55

android: 实现跨程序数据共享的相关文章

android——实现跨程序访问数据

使用之前的SQLite存储的应用程序.首先需要在这个应用程序中创建内容提供器,右击com.example.administrator.exp7包→New→Other→Content Provider,会弹出这样的对话框, 其中Class name 是内容提供器的名字,Authorities是包名com.example.administrator.exp7,Exported表示是否允许外部程序访问内容提供器,Enabled表示是否启用这个内容提供器. 然后修改MyContentProvider中的

Android 跨程序共享数据,探究内容提供器

内容提供器(Content Provider)主要用于在不同的应用程序之间实现数据共享的功能,它提供了一套完整的机制,允许一个程序访问另一个程序中的数据,同时还能保证被访问数据的安全性.目前,使用内容提供器是 Android 实现跨程序共享数据的标准方式. 不同于文件存储和 SharePreferences 存储中的两种全局可读写操作模式,内容提供器可以选择只对哪一部分数据进行共享,从而保证我们程序中的隐私数据不会有泄露的风险. 访问其他程序中的数据: 对于每一个应用程序来说,如果想要访问内容提

内容提供者(Content Provider)--跨程序共享数据

内容提供者简介 访问其他应用中的数据 ContentResolver 的基本用法 实例读取系统联系人 创建自己的内容提供器 创建内容提供器的步骤 创建新类继承ContentProvider并且重写其中6个方法 内容URI参数 使用UriMatcher实现匹配内容URI的功能 getType函数介绍 实现跨程序数据共享 内容提供者 访问自己创建的内容提供者数据 遇到的问题 内容提供者简介 使用场景: 比如:电话薄,短信,媒体库 简介 主要用于在不同的应用程序之间实现数据共享的功能,它提供了一套完整

Android学习笔记(二十一)——实战:程序数据共享

//此系列博文是<第一行Android代码>的学习笔记,如有错漏,欢迎指正! 我们继续在Database项目的基础上继续开发,通过内容提供器来给它加入外部访问接口.首先将 MyDatabaseHelper 中使用 Toast弹出创建数据库成功的提示去除掉,因为跨程序访问时我们不能直接使用 Toast. 一.添加一个 DatabaseProvider 类: 1 public class DatabaseProvider extends ContentProvider { 2 public sta

Android入门(十四)内容提供器-实现跨程序共享实例

原文链接:http://www.orlion.ga/661/ 打开SQLite博文中创建的 DatabaseDemo项目,首先将 MyDatabaseHelper中使用 Toast弹出创建数据库成功的提示去除掉,因为跨程序访问时我们不能直接使用 Toast.然后添加一个 DatabaseProvider类,代码如下所示: package ga.orlion.databasedemo; import android.content.ContentProvider; import android.c

调试 Android* x86 应用程序的方法以及要使用的工具

作者:Xiaodong Wang 1.简介 众所周知,Android* 开发人员头顶许多称呼:设计员.程序猿等,并且通常会不可避免地被称为故障检修工.代码中的错误无法避免,因此无论您是否一开始就造成错误,了解调试工具以及如何迅速而有效地跟踪并解决错误都很重要.鉴于此,如今的 Android 开发人员必须掌握有效的调试技巧.本文提供了 Android 应用程序调试工具的简单教学,用于帮助 Android SDK 以及相关工具的新手迅速入门,并在 Android x86 平台上更有效地解决故障. 2

Android企业级应用程序开发完整训练:精通Android商业级开发最佳实践的24堂课

从企业级商业实战的角度入手,24小时内通过23个动手实战案例,循序渐进的对Android商业级别的应用程序开发要点各个击破,依托于在多年的Android(6款完整的硬件产品和超过20款应用软件)开发和企业级培训经验(超过150期的次Android的企业内训和公开课),旨在在实务的基础之上帮助你完成任何复杂程序的高质量Android应用程序开发,让Android开发跟上想象的速度.最后,通过ActivityManagerService揭秘Android应用程序一切行为背后的核心根源,让你从此开发应

如何确定android中的程序入口?

1.如何确定android中的程序入口? 需要在AndroidMainifest.xml文件中添加这个Activity的配置信息,同时将DemoActivity配置中的intent-filter移动到LauncherActivity配置中. <activity android:name=".DemoActivity" android:label="@string/app_name" > </activity> <activity and

Android 修改应用程序字体

在网上搜索了相关资料,研究了两种算是比较快速的改变程序字体的方法,好,先来介绍着两种方法. 首先第一种方法是重写控件(以Textview为例): 1.Android在写程序的时候谷歌早已将所有字体都默认好了具体是什么字体,自己也没去研究,所以假如说要让程序的字体变成自己想要的字体也是件不容易的事情,首先你要先下载字体库(后缀为ttf),英文的字体库还不算大,而一般来说中文的字体库就很大,所以可以更加程序里面出现的文字,对字体库进行裁剪.要下载字体库的可以上网下载,网上一堆的是,csdn里面的比较