前面学习的数据持久化技术包括文件存储、SharedPreferences存储以及数据库存储技术保存的数据都只能被当前应用程序所访问。虽然文件存储和SharedPreferences存储中提供了MODE_WORLD_READABLE和MODE_WORLD_WRITEABLE这两种操作模式,可以实现不同应用程序间的数据共享,但是这两种模式在Android4.2版本中就已经被废弃了。目前,Android系统推荐使用一种更加安全可靠的内容提供器技术。
内容提供器(Content Provider)主要用于在不同的应用程序之间实现数据共享的功能,它提供了一套完整的机制,允许一个程序访问另一个程序中的数据,同时还能保证被访问的安全性。不同于文件存储和SharedPreferences存储中的两种全局可读写操作模式,内容提供器可以选择只对一部分数据进行共享,从而保证我们程序中的隐私数据不被泄露。
内容提供器的用法一般有两种:
当一个应用程序通过内容提供器对其数据提供了外部访问接口,任何其他的应用程序就都可以对这部分数据进行访问。Android系统中自带的电话簿、短信、媒体库等程序都提供了类似的访问接口,这就让第三方应用程序可以充分地利用这部分数据来实现更好的功能。
对每一个应用程序而言,如果想要访问内容提供器中的共享的数据,就一定要借助ContentResolver类,具体的步骤如下:
具体的应用示例如下:
1 //查询
2 Cursor cursor = getContentResolver().query(
3 uri,
4 projection,
5 selection,
6 selectionArgs,
7 sortOrder);
8 if (cursor != null) {
9 while (cursor.moveToNext()) {
10 String column1 = cursor.getString(cursor.getColumnIndex("column1"));
11 int column2 = cursor.getInt(cursor.getColumnIndex("column2"));
12 }
13 cursor.close();
14 }
15
16 //添加
17 ContentValues values = new ContentValues();
18 values.put("column1", "text");
19 values.put("column2", 1);
20 getContentResolver().insert(uri, values);
21
22 //更新
23 ContentValues values = new ContentValues();
24 values.put("column1", "");
25 getContentResolver().update(uri, values, "column1 = ? and column2 = ?", new
26 String[] {"text", "1"});
27
28 //删除
29 getContentResolver().delete(uri, "column2 = ?", new String[] { "1" });
下面我们尝试读取手机中的联系人的姓名和电话:
首先我们在布局中简单地设置一个按钮和一个ListView,点击按钮后读取联系人,ListView用于显示
1 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
2 android:layout_width="match_parent"
3 android:layout_height="match_parent"
4 android:orientation="vertical" >
5
6 <Button
7 android:id="@+id/ReadContract"
8 android:layout_width="wrap_content"
9 android:layout_height="wrap_content"
10 android:text="@string/readContractButtonName"/>
11
12 <ListView
13 android:id="@+id/contract"
14 android:layout_width="wrap_content"
15 android:layout_height="wrap_content" >
16
17 </ListView>
18
19 </LinearLayout>
然后,我们在Activity的onCreate()方法为按钮添加点击事件,点击之后调用我们自己写的一个读取联系人的方法readContrast(),在该方法中我们获取系统联系人的存放的Uri,然后通过获取的ContentResolver对象的query()方法进行查询,将查询结果添加到List中,然后显示出了
1 public class MainActivity extends Activity {
2
3 private Button readContract ;
4 private ListView contractView ;
5 ArrayAdapter<String> adapter ;
6 List<String> contractsList = new ArrayList<String>() ;
7
8 @Override
9 protected void onCreate(Bundle savedInstanceState) {
10 super.onCreate(savedInstanceState);
11 setContentView(R.layout.activity_main);
12
13 readContract = (Button) findViewById(R.id.ReadContract) ;
14 contractView = (ListView) findViewById(R.id.contract) ;
15 adapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, contractsList) ;
16
17 readContract.setOnClickListener(new OnClickListener() {
18 @Override
19 public void onClick(View v) {
20 //contractView.setAdapter(adapter)必须在点击事件监控中设置,否则没有显示
21 contractView.setAdapter(adapter);
22 readContract() ;
23 }
24 });
25 }
26
27 private void readContract() {
28 Cursor cursor = null ;
29 Uri uri = ContactsContract.CommonDataKinds.Phone.CONTENT_URI ;
30 try {
31 cursor = getContentResolver().query(uri, null, null, null, null) ;
32 while(cursor.moveToNext()){
33 String displayName = cursor.getString(cursor.getColumnIndex(
34 ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME)) ;
35 String number = cursor.getString(cursor.getColumnIndex(
36 ContactsContract.CommonDataKinds.Phone.NUMBER)) ;
37 contractsList.add(displayName + "\n" + number) ;
38 }
39 }catch (Exception e){
40 e.printStackTrace();
41 }finally {
42 if(cursor != null){
43 cursor.close();
44 }
45 }
46 }
47 }
如果想要实现扩程序共享数据的功能,官方推荐的方式是使用内容提供器。我们可以通过新建一个类去继承ContentProvider的方式来创建一个自己的内容提供器。ContentProvider类中有六个抽象方法,我们在使用子类继承它时,需要全部实现这六个方法:
注意这些方法有与ContentResolver完全相同的方法签名。显然,这里我们需要自定义向外提供的对我们的数据的操作的实现。这样其他应用程序通过内容提供器来访问当前数据库中的共享数据的时候可以按照我们的要求得到相应的结果。所以,实现这方法时应该考虑以下事情:
所以,在这里,我们首先需要解析内容URI,一个标准的URI有两种写法:
所以解析内容URI的目的就是要弄清楚所访问内容URI到底是哪一种,从而确定我们要访问的数据是表格的所有数据还是部分数据。以路径结尾就表示期望访问该表中所有的数据,以id结尾就表示期望访问该表中拥有相应id的数据。我们可以使用通配符的方式来分别匹配这两种格式的内容URI,规则如下:
所以,
然后我们可以借助URIMatcher可以实现内容URI的匹配。URIMatcher提供了addURI(),接受三个参数(权限.路径,自定义代码)。返回一个匹配这个人Uri对象所对应的自定义代码。根据代码可知调用方访问的是那张表的数据。
1 public class MyProvider extends ContentProvider {
2
3 /*
4 * MyProvider中新增四个整形常量,其中TABLE1_DIR表示访问table1表中的所有数据,
5 * TABLE1_ITEM表示访问的table1表中的单条数据,TABLE2_DIR表示访问table2表中的所有数据,
6 * TABLE2_ITEM表示访问的table2表中的单条数据。
7 */
8 public static final int TABLE1_DIR = 0;
9 public static final int TABLE1_ITEM = 1;
10 public static final int TABLE2_DIR = 2;
11 public static final int TABLE2_ITEM = 3;
12 private static UriMatcher uriMatcher;
13
14 /*
15 * 上面定义常量以后,接着在静态代码块里,创建UriMatcher的实例,并调用addURI()方法,将期望匹配的内容URI格式传递进去,
16 * 注意这里传入的路径参数是可以使用通配符的。所以,对于隐式数据,我们不匹配进来就可以避免让其他应用程序进行访问。然后当query()方法被调用的时候,
17 * 就会通过UriMatcher的match()方法对传入的Uri对象进行匹配,如果发现UriMatcher中某个内容URI格式成功匹配了该Uri对象,则
18 * 返回相应的自定义代码,然后就可以判断期望访问的到底是什么数据了。这里只使用query()方法做了一个示范,其实
19 * insert(),update(),delete()这几个方法的实现也是差不多的,它们都会携带Uri这个参数,然后同样利用
20 * UriMatcher的match()方法判断出调用期望访问的是哪一张表,在对该表中的数据进行相应的操作就可以了。
21 */
22 static {
23 uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
24 uriMatcher.addURI("com.jack.contactstest.provider", "table1", TABLE1_DIR);
25 uriMatcher.addURI("com.jack.contactstest.provider", "table1/#", TABLE1_ITEM);
26 uriMatcher.addURI("com.jack.contactstest.provider", "table2", TABLE2_DIR);
27 uriMatcher.addURI("com.jack.contactstest.provider", "table2/#", TABLE2_ITEM);
28 }
29
30 /*
31 * onCreate()方法初始化内容提供器的时候调用。通常会在这里完成对数据库的创建和升级等操作,返回
32 * true表示内容提供器初始化成功,返回false则表示失败。注意只有当存在ContentResolver尝试访问
33 * 我们程序中的数据时,内容提供器才会被初始化。
34 */
35 @Override
36 public boolean onCreate() {
37 // TODO Auto-generated method stub
38 return false;
39 }
40
41 /*
42 * query()方法从内容提供器中查询数据。使用uri参数来确定查询哪张表,projection参数用于确定查询
43 * 哪些列,selection和selectionArgs参数用于约束查询哪些行,sortOrder参数用于对结果进行排序,
44 * 查询的结果存放在Cursor对象中返回。
45 */
46 @Override
47 public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
48 // TODO Auto-generated method stub
49 switch (uriMatcher.match(uri)) {
50 case TABLE1_DIR:
51 // 查询table1表中的所有数据
52 break;
53 case TABLE1_ITEM:
54 // 查询table1表中的单条数据
55 break;
56 case TABLE2_DIR:
57 // 查询table2表中的所有数据
58 break;
59 case TABLE2_ITEM:
60 // 查询table2表中的单条数据
61 break;
62 }
63 return null;
64 }
65 。。。
66 }
到这里,一个完整的内容提供器就创建完成了,现在任何一个应用程序都可以使用ContentResolver来访问我们程序中的数据。那么如何才能保证隐私数据不会泄漏出去呢?其实多亏了内容提供器的良好机制,这个问题已经已经在不知不觉中被解决了。因为所有的CRUD操作都一定要匹配到相应的内容URI格式才能进行,而我们当然不可能向UriMatcher中添加隐私数据的URI,所以这部分数据根本无法被外部程序访问到,安全问题也就不存在了。
实现query()方法
ContentProvider.query()方法必须返回一个Cursor对象,如果执行失败,就抛出一个异常。如果你正在使用一个SQLite数据库做为你的数据存储,你能够通过调用SQLiteDatabase类的一个query()方法,就能简单的返回Cursor对象。如果查询不到匹配的行,那么返回的Cursor对象的getCount()方法就会返回0。只有在查询过程期间发生了内部错误,你才应该返回null。
如果你不适用SQLite数据库做为数据存储,那么就要使用Cursor的一个具体子类。如,MatrixCursor类实现了每行是一个对象数组的游标,这个类用addRow()方法来添加新行。
记住,Android系统必须能够跨进程来传递异常。在处理的查询错误中,下列异常可用于进程间传递:
实现insert()方法
insert()方法使用ContentValues参数中的值把一行新的数据添加到相应的表中。如果在ContentValues参数中没有列名,你可能是想要使用提供器代码或数据库模式来提供默认值。
这个方法应该返回新插入行的内容资源标识。使用withAppendedId()方法给这个新行追加一个_ID(或其他主键)值。
实现delete()方法
不要使用delete()方法从你的数据存储中物理的删除行。因为如果你的提供器使用了同步适配器,你就应该使用“delete”标识来标记要删除的行,而不是把完全的删除行。同步适配器会在从提供器中删除它们之前检查要删除的行,并且从服务端删除它们。
实现update()方法
Update()方法需要与insert()方法使用的相同的ContentValues参数,以及与delete()方法和query()方法相同的selection和selectionArgs参数。这种方法允许你在这些方法之间重用代码。
getType()是所有内容提供器都必须提供的方法.用于获取Uri对象对应的MIME类型.MINE由三部分组成:
示例如下,两种不同类型的内容URI的返回的类型如下:
实现onCreate()方法
Android系统会在提供器启动时调用onCreate()方法。你只应该在这个方法中执行快速的初始任务,并且要把数据库的创建和数据的装载延迟到提供器接收到实际的数据请求之后。如果在你onCreate()方法中你执行了长时任务,会降低提供器的启动速度,从而降低提供器对其他应用程序的响应速度。
例如,如果你使用SQLite数据库,而且在onCreate()方法中创建了一个新的SQLiteOpenHelper对象,然后在首次打开数据时,创建SQL表。要做这项工作,首先调用getWritableDatabase()方法,它会自动的调用SQLiteOpenHelper.onCreate()方法。
以下两段代码演示了ContentProvider.onCreate()方法和SQLiteOpenHelper.onCreate()方法之间的交互。
第一段代码时ContentProvider.onCreate()方法的实现。
1 public class ExampleProvider extends ContentProvider
2
3 /*
4 * Defines a handle to the database helper object. The MainDatabaseHelper class is defined
5 * in a following snippet.
6 */
7 private MainDatabaseHelper mOpenHelper;
8
9 // Defines the database name
10 private static final String DBNAME = "mydb";
11
12 // Holds the database object
13 private SQLiteDatabase db;
14
15 public boolean onCreate() {
16
17 /*
18 * Creates a new helper object. This method always returns quickly.
19 * Notice that the database itself isn't created or opened
20 * until SQLiteOpenHelper.getWritableDatabase is called
21 */
22 mOpenHelper = new SQLiteOpenHelper(
23 getContext(), // the application context
24 DBNAME, // the name of the database)
25 null, // uses the default SQLite cursor
26 1 // the version number
27 );
28
29 return true;
30 }
31
32 ...
33
34 // Implements the provider's insert method
35 public Cursor insert(Uri uri, ContentValues values) {
36 // Insert code here to determine which table to open, handle error-checking, and so forth
37
38 ...
39
40 /*
41 * Gets a writeable database. This will trigger its creation if it doesn't already exist.
42 *
43 */
44 db = mOpenHelper.getWritableDatabase();
45 }
46 }
以下代码时是SQLiteOpenHelper.onCreate()方法的实现,而且包括了一个助手类:
// A string that defines the SQL statement for creating a table
private static final String SQL_CREATE_MAIN = "CREATE TABLE " +
"main " + // Table's name
"(" + // The columns in the table
" _ID INTEGER PRIMARY KEY, " +
" WORD TEXT"
" FREQUENCY INTEGER " +
" LOCALE TEXT )";
...
/**
* Helper class that actually creates and manages the provider's underlying data repository.
*/
protected static final class MainDatabaseHelper extends SQLiteOpenHelper {
/*
* Instantiates an open helper for the provider's SQLite data repository
* Do not do database creation and upgrade here.
*/
MainDatabaseHelper(Context context) {
super(context, DBNAME, null, 1);
}
/*
* Creates the data repository. This is called when the provider attempts to open the
* repository and SQLite reports that it doesn't exist.
*/
public void onCreate(SQLiteDatabase db) {
// Creates the main table
db.execSQL(SQL_CREATE_MAIN);
}
}
下面进行实战,体验一下跨程序共享的功能。 简单起见,我们使用上一篇博客的DatabaseTest的项目,在该项目的基础上进行修改继续开发,通过内容提供器给它加入外部访问接口。打开DatabaseTest项目,首先将MyDatabaseHelper中使用Toast弹出创建数据成功的提示去掉,因为跨程序访问时我们不能直接使用Toast。然后添加一个DatabaseProvider类,代码如下所示:
1 public class DatabaseProvider extends ContentProvider {
2
3 //自定义代码
4 public static final int BOOK_DIR=0;
5 public static final int BOOK_ITEM=1;
6 public static final int CATEGORY_DIR=2;
7 public static final int CATEGORY_ITEM=3;
8 //权限
9 public static final String AUTHORITY="com.jack.databasetest.provider";
10 private static UriMatcher uriMatcher;
11 private MyDatabaseHelper dbHelper;
12 //静态代码块进行初始话
13 static {
14 uriMatcher=new UriMatcher(UriMatcher.NO_MATCH);
15 uriMatcher.addURI(AUTHORITY, "book", BOOK_DIR);
16 uriMatcher.addURI(AUTHORITY, "book/#", BOOK_ITEM);
17 uriMatcher.addURI(AUTHORITY, "category", CATEGORY_DIR);
18 uriMatcher.addURI(AUTHORITY, "category/#", CATEGORY_ITEM);
19 }
20
21
22
23 @Override
24 public int delete(Uri uri, String selection, String[] selectionArgs) {
25 // TODO Auto-generated method stub
26 //删除数据
27 SQLiteDatabase db=dbHelper.getWritableDatabase();
28 int deleteRows=0;
29 switch(uriMatcher.match(uri)){
30 case BOOK_DIR:
31 deleteRows=db.delete("book", selection, selectionArgs);
32 break;
33 case BOOK_ITEM:
34 String bookId=uri.getPathSegments().get(1);
35 deleteRows=db.delete("book", "id=?", new String[]{bookId});
36 break;
37 case CATEGORY_DIR:
38 deleteRows=db.delete("category", selection, selectionArgs);
39 break;
40 case CATEGORY_ITEM:
41 String categoryId=uri.getPathSegments().get(1);
42 deleteRows=db.delete("category", "id=?",new String[]{categoryId});
43 break;
44 default:
45 break;
46 }
47 return deleteRows;//被删除的行数作为返回值返回
48 }
49
50 @Override
51 public String getType(Uri uri) {
52 // TODO Auto-generated method stub
53 switch(uriMatcher.match(uri)){
54 case BOOK_DIR:
55 return "vnd.android.cursor.dir/vnd.com.jack.databasetest.provider.book";
56 case BOOK_ITEM:
57 return "vnd.android.cursor.item/vnd.com.jack.databasetest.provider.book";
58 case CATEGORY_DIR:
59 return "vnd.android.cursor.dir/vnd.com.jack.databasetest.provider.category";
60 case CATEGORY_ITEM:
61 return "vnd.android.cursor.item/vnd.com.jack.databasetest.provider.category";
62 }
63 return null;
64 }
65
66 @Override
67 public Uri insert(Uri uri, ContentValues values) {
68 // TODO Auto-generated method stub
69 //添加数据
70 SQLiteDatabase db=dbHelper.getWritableDatabase();
71 Uri uriReturn=null;
72 switch(uriMatcher.match(uri)){
73 case BOOK_DIR:
74 case BOOK_ITEM:
75 long newBookId=db.insert("book", null, values);
76 uriReturn=Uri.parse("content://"+AUTHORITY+"/book/"+newBookId);
77 break;
78 case CATEGORY_DIR:
79 case CATEGORY_ITEM:
80 long newCategoryId=db.insert("category", null, values);
81 uriReturn=Uri.parse("content://"+AUTHORITY+"/book/"+newCategoryId);
82 break;
83 default:
84 break;
85 }
86 /*
87 * insert()方法要求返回一个能够表示这条新增数据的URI,所以需要调用Uri.parse()方法来将一个内容
88 * URI解析成Uri对象,当然这个内容是以新增数据的id结尾的。
89 * */
90 return uriReturn;
91 }
92
93 @Override
94 public boolean onCreate() {
95 // TODO Auto-generated method stub
96 dbHelper=new MyDatabaseHelper(getContext(), "BookStore.db", null, 2);
97 return true;//返回true表示内容提供器初始化成功,这时数据库就已经完成了创建或升级操作
98 }
99
100 @Override
101 public Cursor query(Uri uri, String[] projection, String selection,
102 String[] selectionArgs, String sortOrder) {
103 // TODO Auto-generated method stub
104 //查询数据
105 SQLiteDatabase db=dbHelper.getReadableDatabase();//获得SQLiteDatabase对象
106 Cursor cursor=null;
107 switch(uriMatcher.match(uri)){
108 case BOOK_DIR:
109 //进行查询
110 cursor=db.query("book", projection, selection, selectionArgs,
111 null, null, sortOrder);
112 break;
113 case BOOK_ITEM:
114 //进行查询
115 /*Uri对象的getPathSegments()方法会将内容URI权限之后的部分以“、”符号进行分割,并把分割后的结果
116 * 放入到一个字符串列表中,那这个列表的第0个位置存放的就是路径,第1个位置存放的就是id,得到id后,在通过
117 * selection和selectionArgs参数就实现了查询单条数据的功能。
118 * */
119 String bookId=uri.getPathSegments().get(1);
120 cursor=db.query("book", projection, "id=?", new String[]{bookId},
121 null, null, sortOrder);
122 break;
123 case CATEGORY_DIR:
124 //进行查询
125 cursor=db.query("category", projection, selection, selectionArgs,
126 null, null, sortOrder);
127 break;
128 case CATEGORY_ITEM:
129 //进行查询
130 String categoryId=uri.getPathSegments().get(1);
131 cursor=db.query("book", projection, "id=?", new String[]{categoryId},
132 null, null, sortOrder);
133 break;
134 default:
135 break;
136 }
137 return cursor;
138 }
139
140 @Override
141 public int update(Uri uri, ContentValues values, String selection,
142 String[] selectionArgs) {
143 // TODO Auto-generated method stub
144 SQLiteDatabase db=dbHelper.getWritableDatabase();
145 int updatedRows=0;
146 //更新数据
147 switch(uriMatcher.match(uri)){
148 case BOOK_DIR:
149 updatedRows=db.update("book", values, selection,selectionArgs);
150 break;
151 case BOOK_ITEM:
152 String bookId=uri.getPathSegments().get(1);
153 updatedRows=db.update("book", values, "id=?", new String[]{bookId});
154 break;
155 case CATEGORY_DIR:
156 updatedRows=db.update("category", values, selection,selectionArgs);
157 break;
158 case CATEGORY_ITEM:
159 String categoryId=uri.getPathSegments().get(1);
160 updatedRows=db.update("book", values, "id=?", new String[]{categoryId});
161 break;
162 default:
163 break;
164 }
165 return updatedRows;//受影响的行数作为返回值
166 }
167
168 }
上面的功能,在注释已经说名了,就不多说了,经过上面的步骤,内容提供器的代码全部编写完了,不过离跨实现程序数据共享的功能还差了一小步,因为还需要将内容提供器在AndroidManifest.xml文件中注册才可以,如下所示:
1 <pre name="code" class="html"><?xml version="1.0" encoding="utf-8"?>
2 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
3 package="com.jack.databasetest"
4 android:versionCode="1"
5 android:versionName="1.0" >
6
7 <uses-sdk
8 android:minSdkVersion="13"
9 android:targetSdkVersion="17" />
10
11 <application
12 android:allowBackup="true"
13 android:icon="@drawable/ic_launcher"
14 android:label="@string/app_name"
15 android:theme="@style/AppTheme" >
16 <activity
17 android:name="com.jack.databasetest.MainActivity"
18 android:label="@string/app_name" >
19 <intent-filter>
20 <action android:name="android.intent.action.MAIN" />
21
22 <category android:name="android.intent.category.LAUNCHER" />
23 </intent-filter>
24 </activity>
25
26
27 <provider
28 android:name="com.jack.databasetest.DatabaseProvider"
29 android:authorities="com.jack.databasetest.provider"
30 android:exported="true"
31 ></provider>
32
33 </application>
34
35 </manifest>
android:exported=”true”这个属性,值加了上面的android:name=”com.jack.databasetest.DatabaseProvider” android:authorities=”com.jack.databasetest.provider”属性,程序访问出现安全问题了,百度后,说是需要android:exported=”true”这个属性,才能跨程序被其他的程序访问。我试试了下,当中需要这个属性,不然后面进行跨程序访问的时候会出现错误。
现在DatabaseTest这个项目就已经拥有了跨程序共享数据的功能了,现在我们来试试。首先需要将DatabaseTest程序从模拟器中删除掉,以防止以前的遗留数据对我们产生影响。然后运行下项目,将DatabaseTest程序重写安装在模拟器上。接着关闭这个项目,并创建一个新项目ProviderTest,我们就通过这个程序去访问DatabaseTest中的数据。 先修改下ProviderTest的布局文件activity_main.xml中的代码,如下所示:
1 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
2 xmlns:tools="http://schemas.android.com/tools"
3 android:layout_width="match_parent"
4 android:layout_height="match_parent"
5 android:orientation="vertical"
6 >
7
8 <Button
9 android:id="@+id/add_data"
10 android:layout_width="match_parent"
11 android:layout_height="wrap_content"
12 android:text="add data to book"
13 />
14
15 <Button
16 android:id="@+id/query_data"
17 android:layout_width="match_parent"
18 android:layout_height="wrap_content"
19 android:text="query from book"
20 />
21
22 <Button
23 android:id="@+id/update_data"
24 android:layout_width="match_parent"
25 android:layout_height="wrap_content"
26 android:text="update book"
27 />
28
29 <Button
30 android:id="@+id/delete_data"
31 android:layout_width="match_parent"
32 android:layout_height="wrap_content"
33 android:text="delete data from book"
34 />
35
36
37 </LinearLayout>
放置了四个按钮,分别用来添加数据,查询,修改和删除数据。然后在修改MainActivity中的代码,如下所示:
1 package com.jack.providertest;
2
3 import android.net.Uri;
4 import android.os.Bundle;
5 import android.app.Activity;
6 import android.content.ContentValues;
7 import android.database.Cursor;
8 import android.util.Log;
9 import android.view.Menu;
10 import android.view.View;
11 import android.view.View.OnClickListener;
12 import android.widget.Button;
13
14 public class MainActivity extends Activity {
15
16 private String newId;
17 @Override
18 protected void onCreate(Bundle savedInstanceState) {
19 super.onCreate(savedInstanceState);
20 setContentView(R.layout.activity_main);
21
22 Button addData=(Button) findViewById(R.id.add_data);
23 addData.setOnClickListener(new OnClickListener() {
24
25 @Override
26 public void onClick(View v) {
27 // TODO Auto-generated method stub
28 //添加数据
29 Uri uri=Uri.parse("content://com.jack.databasetest.provider/book");
30 ContentValues values=new ContentValues();
31 values.put("name", "a clash of kings");
32 values.put("author", "george martin");
33 values.put("pages", 1050);
34 values.put("price", 88.9);
35 Uri newUri=getContentResolver().insert(uri, values);//插入数据
36 newId=newUri.getPathSegments().get(1);
37 }
38 });
39
40
41 Button queryData=(Button) findViewById(R.id.query_data);
42 queryData.setOnClickListener(new OnClickListener(){
43
44 @Override
45 public void onClick(View v) {
46 // TODO Auto-generated method stub
47 //查询数据
48 Uri uri=Uri.parse("content://com.jack.databasetest.provider/book");
49 Cursor cursor=getContentResolver().query(uri, null, null, null, null);
50 if(cursor!=null){
51 while(cursor.moveToNext()){
52 String name=cursor.getString(cursor.getColumnIndex("name"));
53 String author=cursor.getString(cursor.getColumnIndex("author"));
54 int pages=cursor.getInt(cursor.getColumnIndex("pages"));
55 double price=cursor.getDouble(cursor.getColumnIndex("price"));
56
57 Log.d("MainActivity", "book name is "+name);
58 Log.d("MainActivity", "book author is "+author);
59 Log.d("MainActivity", "book pages is "+pages);
60 Log.d("MainActivity", "book price is "+price);
61 }
62 cursor.close();
63 }
64 }
65
66 });
67
68
69 Button updateData=(Button) findViewById(R.id.update_data);
70 updateData.setOnClickListener(new OnClickListener(){
71
72 @Override
73 public void onClick(View v) {
74 // TODO Auto-generated method stub
75 //更新数据
76 Uri uri=Uri.parse("content://com.jack.databasetest.provider/book/"+newId);
77 ContentValues values=new ContentValues();
78 values.put("name", "a storm of swords");
79 values.put("pages", 1216);
80 values.put("price", 77.8);
81 getContentResolver().update(uri, values, null, null);
82 }
83
84 });
85
86
87 Button deleteData=(Button) findViewById(R.id.delete_data);
88 deleteData.setOnClickListener(new OnClickListener(){
89
90 @Override
91 public void onClick(View v) {
92 // TODO Auto-generated method stub
93 //删除数据
94 Uri uri=Uri.parse("content://com.jack.databasetest.provider/book/"+newId);
95 getContentResolver().delete(uri, null, null);
96 }
97
98 });
99
102 }
103
104 @Override
105 public boolean onCreateOptionsMenu(Menu menu) {
106 // Inflate the menu; this adds items to the action bar if it is present.
107 getMenuInflater().inflate(R.menu.main, menu);
108 return true;
109 }
110
111 }