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

  //此系列博文是《第一行Android代码》的学习笔记,如有错漏,欢迎指正!

  我们继续在Database项目的基础上继续开发,通过内容提供器来给它加入外部访问接口。首先将 MyDatabaseHelper 中使用 Toast弹出创建数据库成功的提示去除掉,因为跨程序访问时我们不能直接使用 Toast。

一、添加一个 DatabaseProvider 类:

  1 public class DatabaseProvider extends ContentProvider {
  2     public static final int BOOK_DIR = 0;
  3     public static final int BOOK_ITEM = 1;
  4     public static final int CATEGORY_DIR = 2;
  5     public static final int CATEGORY_ITEM = 3;
  6     public static final String AUTHORITY = "com.mycompany.database.provider";
  7     private static UriMatcher uriMatcher;
  8     private MyDatabaseHelper dbHelper;
  9     static {
 10         uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
 11         uriMatcher.addURI(AUTHORITY, "book", BOOK_DIR);
 12         uriMatcher.addURI(AUTHORITY, "book/#", BOOK_ITEM);
 13         uriMatcher.addURI(AUTHORITY, "category", CATEGORY_DIR);
 14         uriMatcher.addURI(AUTHORITY, "category/#", CATEGORY_ITEM);
 15     }
 16     @Override
 17     public boolean onCreate() {
 18         dbHelper = new MyDatabaseHelper(getContext(), "BookStore.db", null, 2);
 19         return true;
 20     }
 21     @Override
 22     public Cursor query(Uri uri, String[] projection, String selection,
 23                         String[] selectionArgs, String sortOrder) {
 24     // 查询数据
 25         SQLiteDatabase db = dbHelper.getReadableDatabase();
 26         Cursor cursor = null;
 27         switch (uriMatcher.match(uri)) {
 28             case BOOK_DIR:
 29                 cursor = db.query("Book", projection, selection, selectionArgs,
 30                         null, null, sortOrder);
 31                 break;
 32             case BOOK_ITEM:
 33                 String bookId = uri.getPathSegments().get(1);
 34                 cursor = db.query("Book", projection, "id = ?", new String[]
 35                         { bookId }, null, null, sortOrder);
 36                 break;
 37             case CATEGORY_DIR:
 38                 cursor = db.query("Category", projection, selection,
 39                         selectionArgs, null, null, sortOrder);
 40                 break;
 41             case CATEGORY_ITEM:
 42                 String categoryId = uri.getPathSegments().get(1);
 43                 cursor = db.query("Category", projection, "id = ?", new String[]
 44                         { categoryId }, null, null, sortOrder);
 45                 break;
 46             default:
 47                 break;
 48         }
 49         return cursor;
 50     }
 51     @Override
 52     public Uri insert(Uri uri, ContentValues values) {
 53     // 添加数据
 54         SQLiteDatabase db = dbHelper.getWritableDatabase();
 55         Uri uriReturn = null;
 56         switch (uriMatcher.match(uri)) {
 57             case BOOK_DIR:
 58             case BOOK_ITEM:
 59                 long newBookId = db.insert("Book", null, values);
 60                 uriReturn = Uri.parse("content://" + AUTHORITY + "/book/" + newBookId);
 61                 break;
 62             case CATEGORY_DIR:
 63             case CATEGORY_ITEM:
 64                 long newCategoryId = db.insert("Category", null, values);
 65                 uriReturn = Uri.parse("content://" + AUTHORITY + "/category/" +
 66                         newCategoryId);
 67                 break;
 68             default:
 69                 break;
 70         }
 71         return uriReturn;
 72     }
 73     @Override
 74     public int update(Uri uri, ContentValues values, String selection,
 75                       String[] selectionArgs) {
 76     // 更新数据
 77         SQLiteDatabase db = dbHelper.getWritableDatabase();
 78         int updatedRows = 0;
 79         switch (uriMatcher.match(uri)) {
 80             case BOOK_DIR:
 81                 updatedRows = db.update("Book", values, selection, selectionArgs);
 82                 break;
 83             case BOOK_ITEM:
 84                 String bookId = uri.getPathSegments().get(1);
 85                 updatedRows = db.update("Book", values, "id = ?", new String[]
 86                         { bookId });
 87                 break;
 88             case CATEGORY_DIR:
 89                 updatedRows = db.update("Category", values, selection,
 90                         selectionArgs);
 91                 break;
 92             case CATEGORY_ITEM:
 93                 String categoryId = uri.getPathSegments().get(1);
 94                 updatedRows = db.update("Category", values, "id = ?", new String[]
 95                         { categoryId });
 96                 break;
 97             default:
 98                 break;
 99         }
100         return updatedRows;
101     }
102     @Override
103     public int delete(Uri uri, String selection, String[] selectionArgs) {
104     // 删除数据
105         SQLiteDatabase db = dbHelper.getWritableDatabase();
106         int deletedRows = 0;
107         switch (uriMatcher.match(uri)) {
108             case BOOK_DIR:
109                 deletedRows = db.delete("Book", selection, selectionArgs);
110                 break;
111             case BOOK_ITEM:
112                 String bookId = uri.getPathSegments().get(1);
113                 deletedRows = db.delete("Book", "id = ?", new String[] { bookId });
114                 break;
115             case CATEGORY_DIR:
116                 deletedRows = db.delete("Category", selection, selectionArgs);
117                 break;
118             case CATEGORY_ITEM:
119                 String categoryId = uri.getPathSegments().get(1);
120                 deletedRows = db.delete("Category", "id = ?", new String[]
121                         { categoryId });
122                 break;
123             default:
124                 break;
125         }
126         return deletedRows;
127     }
128     @Override
129     public String getType(Uri uri) {
130         switch (uriMatcher.match(uri)) {
131             case BOOK_DIR:
132                 return "vnd.android.cursor.dir/vnd.com.mycompany.database.provider.book";
133             case BOOK_ITEM:
134                 return "vnd.android.cursor.item/vnd.com.mycompany.database.provider.book";
135             case CATEGORY_DIR:
136                 return "vnd.android.cursor.dir/vnd.com.mycompany.database.provider.category";
137             case CATEGORY_ITEM:
138                 return "vnd.android.cursor.item/vnd.com.mycompany.database.provider.category";
139         }
140         return null;
141     }
142 }

  代码中我们主要重写了CRUD操作:

  1)query()方法:在这个方法中先获取到了 SQLiteDatabase 的实例,然后根据传入的 Uri参数判断出用户想要访问哪张表,再调用 SQLiteDatabase 的 query()进行查询,并将 Cursor 对象返回就好了。注意当访问单条数据的时候有一个细节,这里调用了 Uri 对象的getPathSegments()方法,它会将内容 URI权限之后的部分以“/”符号进行分割,并把分割后的结果放入到一个字符串列表中,那这个列表的第 0 个位置存放的就是路径,第 1个位置存放的就是 id 了。得到了 id 之后,再通过 selection 和 selectionArgs 参数进行约束,就实现了查询单条数据的功能。

  2)insert()方法: 同样先获取到了 SQLiteDatabase 的实例, 然后根据传入的 Uri参数判断出用户想要往哪张表里添加数据, 再调用 SQLiteDatabase 的 insert()方法进行添加。 注意 insert()方法要求返回一个能够表示这条新增数据的 URI, 所以我们还需要调用 Uri.parse()方法来将一个内容 URI解析成 Uri对象,当然这个内容 URI是以新增数据的 id 结尾的。

  3)update()方法:先获取SQLiteDatabase 的实例,然后根据传入的 Uri 参数判断出用户想要更新哪张表里的数据,再调用 SQLiteDatabase的 update()方法进行更新,最后将受影响的行数作为返回值返回。

  4)delete()方法:仍然是先获取到 SQLiteDatabase 的实例,然后根据传入的 Uri 参数判断出用户想要删除哪张表里的数据,再调用 SQLiteDatabase 的 delete()方法进行删除就好了,被删除的行数将作为返回值返回。

二、在AndroidManifest.xml文件中进行注册:

 1 <?xml version="1.0" encoding="utf-8"?>
 2 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
 3     package="com.mycompany.database" >
 4
 5     <application
 6         android:allowBackup="true"
 7         android:icon="@mipmap/ic_launcher"
 8         android:label="@string/app_name"
 9         android:supportsRtl="true"
10         android:theme="@style/AppTheme" >
11         <activity android:name=".MainActivity" >
12             <intent-filter>
13                 <action android:name="android.intent.action.MAIN" />
14
15                 <category android:name="android.intent.category.LAUNCHER" />
16             </intent-filter>
17         </activity>
18         <provider
19             android:name="com.mycompany.database.DatabaseProvider"
20             android:authorities="com.mycompany.database.provider" >
21         </provider>
22     </application>
23
24 </manifest>

  这里我们使用了<provider>标签来对 DatabaseProvider 这个内容提供器进行注册,在 android:name 属性中指定了该类的全名,又在 android:authorities 属性中指定了该内容提供器的权限。现在 Database这个项目就已经拥有了跨程序共享数据的功能了。接着我们需要将 Database 程序从手机中删除掉,以防止上次中产生的遗留数据对我们造成干扰。然后运行一下项目,将Database程序重新安装在手机了。接着关闭掉Databaset 这个项目,并创建一个新项目 ProviderTest,我们就将通过这个程序去访问Database中的数据。

三、编写ProviderTest的布局文件:

 1 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
 2     xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
 3     android:orientation="vertical"
 4     android:layout_height="match_parent" android:paddingLeft="@dimen/activity_horizontal_margin"
 5     android:paddingRight="@dimen/activity_horizontal_margin"
 6     android:paddingTop="@dimen/activity_vertical_margin"
 7     android:paddingBottom="@dimen/activity_vertical_margin" tools:context=".MainActivity">
 8
 9     <Button
10         android:id="@+id/add_data"
11         android:layout_width="match_parent"
12         android:layout_height="wrap_content"
13         android:text="Add To Book" />
14     <Button
15         android:id="@+id/query_data"
16         android:layout_width="match_parent"
17         android:layout_height="wrap_content"
18         android:text="Query From Book" />
19     <Button
20         android:id="@+id/update_data"
21         android:layout_width="match_parent"
22         android:layout_height="wrap_content"
23         android:text="Update Book" />
24     <Button
25         android:id="@+id/delete_data"
26         android:layout_width="match_parent"
27         android:layout_height="wrap_content"
28         android:text="Delete From Book" />
29 </LinearLayout>

  布局文件比较简单,里面放置了四个按钮,分别用于添加、查询、修改和删除数据的。

四、修改ProviderTest主代码:

 1 public class MainActivity extends AppCompatActivity {
 2
 3     private String newId;
 4     @Override
 5     protected void onCreate(Bundle savedInstanceState) {
 6         super.onCreate(savedInstanceState);
 7         setContentView(R.layout.activity_main);
 8         Button addData = (Button) findViewById(R.id.add_data);
 9         addData.setOnClickListener(new View.OnClickListener() {
10             @Override
11             public void onClick(View v) {
12             // 添加数据
13                 Uri uri = Uri.parse("content://com.mycompany.database.provider/book");
14                 ContentValues values = new ContentValues();
15                 values.put("name", "A Clash of Kings");
16                 values.put("author", "George Martin");
17                 values.put("pages", 1040);
18                 values.put("price", 22.85);
19                 Uri newUri = getContentResolver().insert(uri, values);
20                 newId = newUri.getPathSegments().get(1);
21             }
22         });
23         Button queryData = (Button) findViewById(R.id.query_data);
24         queryData.setOnClickListener(new View.OnClickListener() {
25             @Override
26             public void onClick(View v) {
27                 // 查询数据
28                 Uri uri = Uri.parse("content://com.mycompany.database.provider/book");
29                         Cursor cursor = getContentResolver().query(uri, null, null, null, null);
30                 if (cursor != null) {
31                     while (cursor.moveToNext()) {
32                         String name = cursor.getString(cursor.
33                                 getColumnIndex("name"));
34                         String author = cursor.getString(cursor.
35                                 getColumnIndex("author"));
36                         int pages = cursor.getInt(cursor.getColumnIndex
37                                 ("pages"));
38                         double price = cursor.getDouble(cursor.
39                                 getColumnIndex("price"));
40                         Log.d("MainActivity", "book name is " + name);
41                         Log.d("MainActivity", "book author is " + author);
42                         Log.d("MainActivity", "book pages is " + pages);
43                         Log.d("MainActivity", "book price is " + price);
44                     }
45                     cursor.close();
46                 }
47             }
48         });
49         Button updateData = (Button) findViewById(R.id.update_data);
50         updateData.setOnClickListener(new View.OnClickListener() {
51             @Override
52             public void onClick(View v) {
53             // 更新数据
54                 Uri uri = Uri.parse("content://com.mycompany.database.provider/book/" + newId);
55                         ContentValues values = new ContentValues();
56                 values.put("name", "A Storm of Swords");
57                 values.put("pages", 1216);
58                 values.put("price", 24.05);
59                 getContentResolver().update(uri, values, null, null);
60             }
61         });
62         Button deleteData = (Button) findViewById(R.id.delete_data);
63         deleteData.setOnClickListener(new View.OnClickListener() {
64             @Override
65             public void onClick(View v) {
66             // 删除数据
67                 Uri uri = Uri.parse("content://com.mycompany.database.provider/book/" + newId);
68                         getContentResolver().delete(uri, null, null);
69             }
70         });
71     }
72 }

  我们分别在这四个按钮的点击事件里面处理了增删改查的逻辑。

  1)Add To Book: 添加数据的时候,首先调用了 Uri.parse()方法将一个内容 URI解析成 Uri对象,然后把要添加的数据都存放到 ContentValues对象中,接着调用 ContentResolver 的 insert()方法执行添加操作就可以了。注意 insert()方法会返回一个 Uri 对象,这个对象中包含了新增数据的 id,我们通过getPathSegments()方法将这个 id 取出,稍后会用到它。
  2)Query From Book: 查询数据的时候,同样是调用了 Uri.parse()方法将一个内容 URI解析成 Uri对象,然后调用 ContentResolver 的 query()方法去查询数据,查询的结果当然还是存放在 Cursor对象中的。之后对 Cursor 进行遍历,从中取出查询结果,并一一打印出来。
  3)Update Book: 更新数据的时候,也是先将内容 URI 解析成 Uri 对象,然后把想要更新的数据存放到ContentValues对象中,再调用 ContentResolver 的 update()方法执行更新操作就可以了。注意这里我们为了不想让 Book 表中其他的行受到影响,在调用 Uri.parse()方法时,给内容 URI的尾部增加了一个 id,而这个 id 正是添加数据时所返回的。这就表示我们只希望更新刚刚添加的那条数据,Book 表中的其他行都不会受影响。
  4)Delete From Book: 删除数据的时候,也是使用同样的方法解析了一个以 id 结尾的内容 URI,然后调用ContentResolver 的 delete()方法执行删除操作就可以了。由于我们在内容 URI 里指定了一个id,因此只会删掉拥有相应 id 的那行数据,Book 表中的其他数据都不会受影响。

  程序运行效果如下:

  

  按下Add按钮后,再按下查询,在Android Monitor中可看到:

04-25 21:49:18.327 31285-31285/com.mycompany.database D/MainActivity: book name is A Clash of Kings
04-25 21:49:18.327 31285-31285/com.mycompany.database D/MainActivity: book author is George Martin
04-25 21:49:18.327 31285-31285/com.mycompany.database D/MainActivity: book pages is 1040
04-25 21:49:18.337 31285-31285/com.mycompany.database D/MainActivity: book price is 22.85

  按下Update后,再按下查询,可看到:

04-25 22:11:10.768 31285-31285/com.mycompany.database D/MainActivity: book name is A Storm of Swords
04-25 22:11:10.768 31285-31285/com.mycompany.database D/MainActivity: book author is George Martin
04-25 22:11:10.768 31285-31285/com.mycompany.database D/MainActivity: book pages is 1216
04-25 22:11:10.768 31285-31285/com.mycompany.database D/MainActivity: book price is 24.05

  当按下Delete后,再按下查询,此时已没有数据显示了。

  //End.

 

时间: 2024-10-07 05:43:13

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

Android学习笔记二十一.使用ContentProvider实现数据共享(四).操作系统(联系人)的ContentProvider

Android系统本身提供了大量的ContentProvider,例如联系人信息.系统的多媒体信息等,我们开发的应用程序主要是通过ContentResolver来调用系统的ContentProvider提供的query().insert().update()和delete()方法来获取Android内部的数据. 一.如何使用ContentResolver操作系统ContentProvider暴露的内部数据? 1.调用Activity的getContentResolver()获取ContentRe

Android学习笔记二

17. 在ContentProvider中定义的getType()方法是定义URI的内容类型. 18. SQLiteDatabase类中的insert/delete/update/query方法其实也挺好用的,我在EquipmentProvider类中做了实现 19. Android专门有个单元测试项目(Android Test Project),在这个项目中,可以新建一个继承AndroidTestCase类的具体测试类来单元测试某个功能.我新建了一个AndroidTestProject项目,在

Android学习笔记二十之Toast吐司、Notification通知、PopupWindow弹出窗

Android学习笔记二十之Toast吐司.Notification通知.PopupWindow弹出窗 Toast吐司 Toast吐司是我们经常用到的一个控件,Toast是AndroidOS用来显示消息的一种机制,它与Dialog不同,Toast不会获取到焦点,通常显示一段时间之后就会自动消失,下面我们来介绍Toast的几种常用方式: 第一种,默认显示方式,也是最常用的方式: Toast.makeText(MainActivity.this, "这是默认的显示方式", Toast.LE

Android学习笔记(十一)——从意图返回结果

从意图返回结果 startActivity()方法调用另一个活动,但并没有返回结果给当前活动.此时如想从一个活动中回传数据,就要使用startActivityForResult()方法. 点此获取完整代码~                                                                  1.使用上一篇中创建的项目,在secondactivity.xml文件中添加文本框和按钮,代码如下: <TextView android:layout_width

Android学习笔记二十九之SwipeRefreshLayout、RecyclerView和CardView

Android学习笔记二十九之SwipeRefreshLayout.RecyclerView和CardView 前面我们介绍了AlertDialog和几个常用的Dialog,ProgressDialog进度条提示框.DatePickerDialog日期选择对话框和TimePickerDialog时间选择对话框.这一节我们介绍几个新的API控件SwipeRefreshLayout.RecyclerView和CardView,这几个API控件都是google在Android5.0推出的.下面我们来学

马哥学习笔记二十一——LVS DR模型

kernel parameter: arp_ignore: 定义接收到ARP请求时的响应级别: 0:只要本地配置的有相应地址,就给予响应: 1:仅在请求的目标地址配置请求到达的接口上的时候,才给予响应: arp_announce:定义将自己地址向外通告时的通告级别: 0:将本地任何接口上的任何地址向外通告: 1:试图仅向目标网络通告与其网络匹配的地址: 2:仅向与本地接口上地址匹配的网络进行通告: curl命令选项: --cacert <file> CA证书 (SSL) --capath &l

Android学习笔记二十五之ListView多布局实现

Android学习笔记二十五之ListView多布局实现 这一节是介绍ListView这个控件的最后一节,实现一个Item的多布局.像我们经常在用的各种即时通讯工具,QQ.微信等,假设他们的会话界面是ListView实现的,那么ListView就有多种Item布局,这一节,我们就来实现一个ListView的多种Item. 要实现ListView里面有多种Item,就要重写适配器的两个方法getViewTypeCount()和getItemViewType(int position),第一个方法是

Android学习笔记二十四之ListView列表视图二

Android学习笔记二十四之ListView列表视图二 前面一篇我们介绍了常用的几种适配器的简单实现和ListView的简单使用,这一篇中,我们介绍一下ListView的优化和一些其它的问题. ListView优化方法一 在ListView中,我们最常用的就是自定义Adapter,在我们自定义Adapter中,需要实现两个比较重要的方法getCount()和getView(),前者是负责计算ListView的总Item数,后者是生成Item,有多少个Item就会调用getView()方法多少次

Android学习笔记二十七之ExpandableListView可折叠列表和StackView栈视图

Android学习笔记二十七之ExpandableListView可折叠列表和StackView栈视图 ExpandableListView可折叠列表 这一节我们介绍第三个用适配器的控件,ExpandableListView可折叠列表.这个控件可以实现我们在QQ中非常常见好友分组功能,ExpandableListView是ListView的子类,用法跟ListView差不多,下面我们来学习这个控件的基本使用: 常用属性: android:childDivider:指定各组内子类表项之间的分隔条,

Android学习笔记二-Linear Layout

1.LinearLayout是子view均为单方向的,即均为水平或垂直方向的布局.你可以用android:orientation属性来 定义layout方向 所有子view都是一个挨着一个的,所以一个垂直列表,不管它本身多宽,同时只能有一行.若是水平列表,则都等高. 2.LayoutWeight 用来操控各个子view的相对比例,,即各个子元素对空间的使用权重 Weight并非网上很多文章所叙述的那样(文章都过于片面),weight是指某个组件在布局中[剩余空间]中的显示权重,那么所谓的剩余空间