当前位置:   article > 正文

6.5 共享数据

6.5 共享数据

        本节介绍Android的四大组件之一ContentProvider的基本概念和常见用法:首先说明如何使用内容提供器封装内部数据的外部访问接口,然后阐述如何使用内容解析器通过外部接口操作内部数据,最后叙述如何利用内容解析器读写联系人信息,以及如何利用内容观察器监听收到的短信内容。

6.5.1  通过ContentProvider封装数据

        Android提供了四大组件,分别是活动Activity、广播Broadcast、服务Service和内容提供器ContentProvider。其中内容提供器涵盖与内部数据存取有关的一系列组件,完整的内容组件由内容提供器ContentProvider、内容解析器ContentResolver、内容观察器ContentObserver三部分组成。

        ContentProvider给App存取内部数据提供了统一的外部接口,让不同的应用之间得以互相共享数据。像上一章提到的SQLite可操作应用自身的内部数据库,上传和下载功能可操作后端服务器的文件,而ContentProvider可操作当前设备其他应用的内部数据,它是一种中间层次的数据存储形式。

        在实际编码中,ContentProvider只是服务端App存储数据的抽象类,开发者需要在其基础上实现一个完整的内容提供器,并重写下列数据库管理方法。

        ●  onCreate:创建数据库并获得数据库连接。

        ●  insert:插入数据。

        ●  delete:删除数据。

        ●  update:更新数据。

        ●  query:查询数据,并返回结果集的游标。

        ●  getType:获取内容提供器支持的数据类型。

        这些方法看起来是不是很像SQLite?没错,ContentProvider作为中间接口,本身并不直接保存数据,而是通过SQLiteOpenHelper与SQLiteDatabase间接操作底层的数据库。所以要想使用ContentProvider,首先得实现SQLite的数据库帮助器,然后由ContentProvider封装对外的接口。以封装用户信息为例,具体步骤主要分成以下3步。

        1.  编写用户信息表的数据库帮助器

        这个数据库帮助器就是常规的SQLite操作代码,实现过程参见本章的“6.2.3  数据库帮助器SQLiteOpenHelper”,完整代码如下:

  1. package com.example.roomdatabase.entity;
  2. //用户信息
  3. public class UserInfo {
  4. public long rowid; // 行号
  5. public int xuhao; // 序号
  6. public String name; // 姓名
  7. public int age; // 年龄
  8. public long height; // 身高
  9. public float weight; // 体重
  10. public boolean married; // 婚否
  11. public String update_time; // 更新时间
  12. public String phone; // 手机号
  13. public String password; // 密码
  14. public UserInfo() {
  15. rowid = 0L;
  16. xuhao = 0;
  17. name = "";
  18. age = 0;
  19. height = 0L;
  20. weight = 0.0f;
  21. married = false;
  22. update_time = "";
  23. phone = "";
  24. password = "";
  25. }
  26. }
  1. package com.example.roomdatabase.database;
  2. import android.content.ContentValues;
  3. import android.content.Context;
  4. import android.database.Cursor;
  5. import android.database.sqlite.SQLiteDatabase;
  6. import android.database.sqlite.SQLiteOpenHelper;
  7. import android.util.Log;
  8. import com.example.roomdatabase.entity.UserInfo;
  9. import java.util.ArrayList;
  10. import java.util.List;
  11. public class UserDBHelper extends SQLiteOpenHelper {
  12. private static final String TAG = "UserDBHelper";
  13. private static final String DB_NAME = "user.db"; // 数据库的名称
  14. private static final int DB_VERSION = 1; // 数据库的版本号
  15. private static UserDBHelper mHelper = null; // 数据库帮助器的实例
  16. private SQLiteDatabase mDB = null; // 数据库的实例
  17. public static final String TABLE_NAME = "user_info"; // 表的名称
  18. private UserDBHelper(Context context) {
  19. super(context, DB_NAME, null, DB_VERSION);
  20. }
  21. private UserDBHelper(Context context, int version) {
  22. super(context, DB_NAME, null, version);
  23. }
  24. // 利用单例模式获取数据库帮助器的唯一实例
  25. public static UserDBHelper getInstance(Context context, int version) {
  26. if (version > 0 && mHelper == null) {
  27. mHelper = new UserDBHelper(context, version);
  28. } else if (mHelper == null) {
  29. mHelper = new UserDBHelper(context);
  30. }
  31. return mHelper;
  32. }
  33. // 打开数据库的读连接
  34. public SQLiteDatabase openReadLink() {
  35. if (mDB == null || !mDB.isOpen()) {
  36. mDB = mHelper.getReadableDatabase();
  37. }
  38. return mDB;
  39. }
  40. // 打开数据库的写连接
  41. public SQLiteDatabase openWriteLink() {
  42. if (mDB == null || !mDB.isOpen()) {
  43. mDB = mHelper.getWritableDatabase();
  44. }
  45. return mDB;
  46. }
  47. // 关闭数据库连接
  48. public void closeLink() {
  49. if (mDB != null && mDB.isOpen()) {
  50. mDB.close();
  51. mDB = null;
  52. }
  53. }
  54. // 创建数据库,执行建表语句
  55. @Override
  56. public void onCreate(SQLiteDatabase db) {
  57. Log.d(TAG, "onCreate");
  58. String drop_sql = "DROP TABLE IF EXISTS " + TABLE_NAME + ";";
  59. Log.d(TAG, "drop_sql:" + drop_sql);
  60. db.execSQL(drop_sql);
  61. String create_sql = "CREATE TABLE IF NOT EXISTS " + TABLE_NAME + " ("
  62. + "_id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,"
  63. + "name VARCHAR NOT NULL," + "age INTEGER NOT NULL,"
  64. + "height INTEGER NOT NULL," + "weight FLOAT NOT NULL,"
  65. + "married INTEGER NOT NULL," + "update_time VARCHAR NOT NULL"
  66. //演示数据库升级时要先把下面这行注释
  67. + ",phone VARCHAR" + ",password VARCHAR"
  68. + ");";
  69. Log.d(TAG, "create_sql:" + create_sql);
  70. db.execSQL(create_sql); // 执行完整的SQL语句
  71. }
  72. // 升级数据库,执行表结构变更语句
  73. @Override
  74. public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
  75. Log.d(TAG, "onUpgrade oldVersion=" + oldVersion + ", newVersion=" + newVersion);
  76. if (newVersion > 1) {
  77. //Android的ALTER命令不支持一次添加多列,只能分多次添加
  78. String alter_sql = "ALTER TABLE " + TABLE_NAME + " ADD COLUMN " + "phone VARCHAR;";
  79. Log.d(TAG, "alter_sql:" + alter_sql);
  80. db.execSQL(alter_sql);
  81. alter_sql = "ALTER TABLE " + TABLE_NAME + " ADD COLUMN " + "password VARCHAR;";
  82. Log.d(TAG, "alter_sql:" + alter_sql);
  83. db.execSQL(alter_sql); // 执行完整的SQL语句
  84. }
  85. }
  86. // 根据指定条件删除表记录
  87. public int delete(String condition) {
  88. // 执行删除记录动作,该语句返回删除记录的数目
  89. return mDB.delete(TABLE_NAME, condition, null);
  90. }
  91. // 删除该表的所有记录
  92. public int deleteAll() {
  93. // 执行删除记录动作,该语句返回删除记录的数目
  94. return mDB.delete(TABLE_NAME, "1=1", null);
  95. }
  96. // 往该表添加一条记录
  97. public long insert(UserInfo info) {
  98. List<UserInfo> infoList = new ArrayList<UserInfo>();
  99. infoList.add(info);
  100. return insert(infoList);
  101. }
  102. // 往该表添加多条记录
  103. public long insert(List<UserInfo> infoList) {
  104. long result = -1;
  105. for (int i = 0; i < infoList.size(); i++) {
  106. UserInfo info = infoList.get(i);
  107. List<UserInfo> tempList = new ArrayList<UserInfo>();
  108. // 如果存在同名记录,则更新记录
  109. // 注意条件语句的等号后面要用单引号括起来
  110. if (info.name != null && info.name.length() > 0) {
  111. String condition = String.format("name='%s'", info.name);
  112. tempList = query(condition);
  113. if (tempList.size() > 0) {
  114. update(info, condition);
  115. result = tempList.get(0).rowid;
  116. continue;
  117. }
  118. }
  119. // 如果存在同样的手机号码,则更新记录
  120. if (info.phone != null && info.phone.length() > 0) {
  121. String condition = String.format("phone='%s'", info.phone);
  122. tempList = query(condition);
  123. if (tempList.size() > 0) {
  124. update(info, condition);
  125. result = tempList.get(0).rowid;
  126. continue;
  127. }
  128. }
  129. // 不存在唯一性重复的记录,则插入新记录
  130. ContentValues cv = new ContentValues();
  131. cv.put("name", info.name);
  132. cv.put("age", info.age);
  133. cv.put("height", info.height);
  134. cv.put("weight", info.weight);
  135. cv.put("married", info.married);
  136. cv.put("update_time", info.update_time);
  137. cv.put("phone", info.phone);
  138. cv.put("password", info.password);
  139. // 执行插入记录动作,该语句返回插入记录的行号
  140. result = mDB.insert(TABLE_NAME, "", cv);
  141. if (result == -1) { // 添加成功则返回行号,添加失败则返回-1
  142. return result;
  143. }
  144. }
  145. return result;
  146. }
  147. // 根据条件更新指定的表记录
  148. public int update(UserInfo info, String condition) {
  149. ContentValues cv = new ContentValues();
  150. cv.put("name", info.name);
  151. cv.put("age", info.age);
  152. cv.put("height", info.height);
  153. cv.put("weight", info.weight);
  154. cv.put("married", info.married);
  155. cv.put("update_time", info.update_time);
  156. cv.put("phone", info.phone);
  157. cv.put("password", info.password);
  158. // 执行更新记录动作,该语句返回更新的记录数量
  159. return mDB.update(TABLE_NAME, cv, condition, null);
  160. }
  161. public int update(UserInfo info) {
  162. // 执行更新记录动作,该语句返回更新的记录数量
  163. return update(info, "rowid=" + info.rowid);
  164. }
  165. // 根据指定条件查询记录,并返回结果数据列表
  166. public List<UserInfo> query(String condition) {
  167. String sql = String.format("select rowid,_id,name,age,height," +
  168. "weight,married,update_time,phone,password " +
  169. "from %s where %s;", TABLE_NAME, condition);
  170. Log.d(TAG, "query sql: " + sql);
  171. List<UserInfo> infoList = new ArrayList<UserInfo>();
  172. // 执行记录查询动作,该语句返回结果集的游标
  173. Cursor cursor = mDB.rawQuery(sql, null);
  174. // 循环取出游标指向的每条记录
  175. while (cursor.moveToNext()) {
  176. UserInfo info = new UserInfo();
  177. info.rowid = cursor.getLong(0); // 取出长整型数
  178. info.xuhao = cursor.getInt(1); // 取出整型数
  179. info.name = cursor.getString(2); // 取出字符串
  180. info.age = cursor.getInt(3); // 取出整型数
  181. info.height = cursor.getLong(4); // 取出长整型数
  182. info.weight = cursor.getFloat(5); // 取出浮点数
  183. //SQLite没有布尔型,用0表示false,用1表示true
  184. info.married = (cursor.getInt(6) == 0) ? false : true;
  185. info.update_time = cursor.getString(7); // 取出字符串
  186. info.phone = cursor.getString(8); // 取出字符串
  187. info.password = cursor.getString(9); // 取出字符串
  188. infoList.add(info);
  189. }
  190. cursor.close(); // 查询完毕,关闭数据库游标
  191. return infoList;
  192. }
  193. // 根据手机号码查询指定记录
  194. public UserInfo queryByPhone(String phone) {
  195. UserInfo info = null;
  196. List<UserInfo> infoList = query(String.format("phone='%s'", phone));
  197. if (infoList.size() > 0) { // 存在该号码的登录信息
  198. info = infoList.get(0);
  199. }
  200. return info;
  201. }
  202. }
        2.  编写内容提供器的基础字段类       

        该类需要实现接口BaseColumns,同时加入几个常量定义。详细代码示例如下:

  1. package com.example.roomdatabase.entity;
  2. import android.net.Uri;
  3. import android.provider.BaseColumns;
  4. import com.example.roomdatabase.database.UserDBHelper;
  5. public class UserInfoContent implements BaseColumns {
  6. // 这里的名称必须与AndroidManifest.xml里的android:authorities保持一致
  7. public static final String AUTHORITIES = "com.example";
  8. // 内容提供器的外部表名
  9. public static final String TABLE_NAME = UserDBHelper.TABLE_NAME;
  10. // 访问内容提供器的URI
  11. public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITIES + "/user");
  12. // 下面是该表的各个字段名称
  13. public static final String USER_NAME = "name";
  14. public static final String USER_AGE = "age";
  15. public static final String USER_HEIGHT = "height";
  16. public static final String USER_WEIGHT = "weight";
  17. public static final String USER_MARRIED = "married";
  18. // 默认的排序方法
  19. public static final String DEFAULT_SORT_ORDER = "_id desc";
  20. }
        3.  通过右键菜单创建内容提供器

        右击App模块的包名目录,在弹出的右键菜单中依次选择New→Other→Content Provider,打开如图所示的组件创建对话框。

        在创建对话框的Class Name一栏填写内容提供器的名称,比如UserInfoProvider;在URI Authorities一栏填写URI的授权串,比如“com.example”,注意这个授权串要跟 UserInfoContent里的一样;然后单击对话框右下角的Finish按钮,完成提供器的创建操作。

       上述创建过程会自动修改App模块的两处地方,一处是往AndroidManifest.xml添加内容提供器的注册配置,配置信息示例如下:

  1. <provider
  2. android:name=".entity.UserInfoProvider"
  3. android:authorities="com.example"
  4. android:enabled="true"
  5. android:exported="true" >
  6. </provider>

        另一处是在包名目录下生成名为UserInfoProvider.java的代码文件,打开一看发现该类继承了ContentProvider,并且提示重写onCreate、insert、delete、query、update、getType等方法,以便对数据进行增删改查等操作。这个提供器代码显然只有一个框架,还需补充详细的实现代码,为此重写onCreate方法,在此获取用户信息表的数据库帮助器实例,其他insert、delete、query等方法也要加入对应的数据库操作代码,修改之后的内容提供器代码如下:

  1. package com.example.roomdatabase.entity;
  2. import android.content.ContentProvider;
  3. import android.content.ContentUris;
  4. import android.content.ContentValues;
  5. import android.content.UriMatcher;
  6. import android.database.Cursor;
  7. import android.database.sqlite.SQLiteDatabase;
  8. import android.net.Uri;
  9. import com.example.roomdatabase.database.UserDBHelper;
  10. public class UserInfoProvider extends ContentProvider {
  11. private final static String TAG = "UserInfoProvider";
  12. private UserDBHelper userDB; // 声明一个用户数据库的帮助器对象
  13. public static final int USER_INFO = 1; // Uri匹配时的代号
  14. public static final UriMatcher uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
  15. static { // 往Uri匹配器中添加指定的数据路径
  16. uriMatcher.addURI(UserInfoContent.AUTHORITIES, "/user", USER_INFO);
  17. }
  18. public UserInfoProvider() {
  19. }
  20. // 根据指定条件删除数据
  21. @Override
  22. public int delete(Uri uri, String selection, String[] selectionArgs) {
  23. // Implement this to handle requests to delete one or more rows.
  24. int count = 0;
  25. if (uriMatcher.match(uri) == USER_INFO) { // 匹配到了用户信息表
  26. // 获取SQLite数据库的写连接
  27. SQLiteDatabase db = userDB.getWritableDatabase();
  28. // 执行SQLite的删除操作,并返回删除记录的数目
  29. count = db.delete(UserInfoContent.TABLE_NAME, selection, selectionArgs);
  30. db.close(); // 关闭SQLite数据库连接
  31. }
  32. return count;
  33. }
  34. // 获取Uri支持的数据类型,暂未实现
  35. @Override
  36. public String getType(Uri uri) {
  37. // TODO: Implement this to handle requests for the MIME type of the data
  38. // at the given URI.
  39. throw new UnsupportedOperationException("Not yet implemented");
  40. }
  41. // 插入数据
  42. @Override
  43. public Uri insert(Uri uri, ContentValues values) {
  44. // TODO: Implement this to handle requests to insert a new row.
  45. if (uriMatcher.match(uri) == USER_INFO) { // 匹配到了用户信息表
  46. // 获取SQLite数据库的写连接
  47. SQLiteDatabase db = userDB.getWritableDatabase();
  48. // 向指定的表插入数据,返回记录的行号
  49. long rowId = db.insert(UserInfoContent.TABLE_NAME, null, values);
  50. if (rowId > 0) { // 判断插入是否执行成功
  51. // 如果添加成功,就利用新记录的行号生成新的地址
  52. Uri newUri = ContentUris.withAppendedId(UserInfoContent.CONTENT_URI, rowId);
  53. // 通知监听器,数据已经改变
  54. getContext().getContentResolver().notifyChange(newUri, null);
  55. }
  56. db.close(); // 关闭SQLite数据库连接
  57. }
  58. return uri;
  59. }
  60. // 创建ContentProvider时调用,可在此获取具体的数据库帮助器实例
  61. @Override
  62. public boolean onCreate() {
  63. // TODO: Implement this to initialize your content provider on startup.
  64. userDB = UserDBHelper.getInstance(getContext(), 1);
  65. return true;
  66. }
  67. // 根据指定条件查询数据库
  68. @Override
  69. public Cursor query(Uri uri, String[] projection, String selection,
  70. String[] selectionArgs, String sortOrder) {
  71. Cursor cursor = null;
  72. if (uriMatcher.match(uri) == USER_INFO) { // 匹配到了用户信息表
  73. // 获取SQLite数据库的读连接
  74. SQLiteDatabase db = userDB.getReadableDatabase();
  75. // 执行SQLite的查询操作
  76. cursor = db.query(UserInfoContent.TABLE_NAME,
  77. projection, selection, selectionArgs, null, null, sortOrder);
  78. // 设置内容解析器的监听
  79. cursor.setNotificationUri(getContext().getContentResolver(), uri);
  80. }
  81. return cursor; // 返回查询结果集的游标
  82. }
  83. // 更新数据,暂未实现
  84. @Override
  85. public int update(Uri uri, ContentValues values, String selection,
  86. String[] selectionArgs) {
  87. // TODO: Implement this to handle requests to update one or more rows.
  88. throw new UnsupportedOperationException("Not yet implemented");
  89. }
  90. }

        经过以上3个步骤之后,便完成了服务端App的接口封装工作,接下来再由其他App去访问服务端App的数据。

6.5.2  通过ContentResolver访问数据

        上一小节提到了利用ContentProvider封装服务端App的数据,如果客户App想访问对方的内部数据,就要借助内容解析器ContentResolver。内容解析器是客户端App操作服务端数据的工具,与之对应的内容提供器则是服务端的数据接口。在活动代码中调用getContentResolver方法,即可获取内容解析器的实例。

        ContentResolver提供的方法与ContentProvider壹壹对应,比如insert、delete、query、update、getType等,甚至连方法的参数类型都雷同。以添加操作为例,针对前面UserInfoProvider提供的数据接口,下面由内容解析器调用insert方法,使之往内容提供器中插入一条用户信息,记录添加代码如下:

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  3. xmlns:app="http://schemas.android.com/apk/res-auto"
  4. xmlns:tools="http://schemas.android.com/tools"
  5. android:layout_width="match_parent"
  6. android:layout_height="match_parent"
  7. android:orientation="vertical"
  8. android:padding="5dp"
  9. tools:context=".ContentWriteActivity">
  10. <RelativeLayout
  11. android:layout_width="match_parent"
  12. android:layout_height="56dp" >
  13. <TextView
  14. android:id="@+id/tv_name"
  15. android:layout_width="wrap_content"
  16. android:layout_height="match_parent"
  17. android:gravity="center"
  18. android:text="姓名:"
  19. android:textColor="@color/black"
  20. android:textSize="17sp" />
  21. <EditText
  22. android:id="@+id/et_name"
  23. android:layout_width="match_parent"
  24. android:layout_height="match_parent"
  25. android:layout_marginBottom="3dp"
  26. android:layout_marginTop="3dp"
  27. android:layout_toRightOf="@+id/tv_name"
  28. android:background="@drawable/editext_selector"
  29. android:gravity="left|center"
  30. android:hint="请输入姓名"
  31. android:inputType="text"
  32. android:maxLength="12"
  33. android:textColor="@color/black"
  34. android:textSize="17sp" />
  35. </RelativeLayout>
  36. <RelativeLayout
  37. android:layout_width="match_parent"
  38. android:layout_height="56dp" >
  39. <TextView
  40. android:id="@+id/tv_age"
  41. android:layout_width="wrap_content"
  42. android:layout_height="match_parent"
  43. android:gravity="center"
  44. android:text="年龄:"
  45. android:textColor="@color/black"
  46. android:textSize="17sp" />
  47. <EditText
  48. android:id="@+id/et_age"
  49. android:layout_width="match_parent"
  50. android:layout_height="match_parent"
  51. android:layout_marginBottom="3dp"
  52. android:layout_marginTop="3dp"
  53. android:layout_toRightOf="@+id/tv_age"
  54. android:background="@drawable/editext_selector"
  55. android:gravity="left|center"
  56. android:hint="请输入年龄"
  57. android:inputType="number"
  58. android:maxLength="2"
  59. android:textColor="@color/black"
  60. android:textSize="17sp" />
  61. </RelativeLayout>
  62. <RelativeLayout
  63. android:layout_width="match_parent"
  64. android:layout_height="56dp" >
  65. <TextView
  66. android:id="@+id/tv_height"
  67. android:layout_width="wrap_content"
  68. android:layout_height="match_parent"
  69. android:gravity="center"
  70. android:text="身高:"
  71. android:textColor="@color/black"
  72. android:textSize="17sp" />
  73. <EditText
  74. android:id="@+id/et_height"
  75. android:layout_width="match_parent"
  76. android:layout_height="match_parent"
  77. android:layout_marginBottom="3dp"
  78. android:layout_marginTop="3dp"
  79. android:layout_toRightOf="@+id/tv_height"
  80. android:background="@drawable/editext_selector"
  81. android:gravity="left|center"
  82. android:hint="请输入身高"
  83. android:inputType="number"
  84. android:maxLength="3"
  85. android:textColor="@color/black"
  86. android:textSize="17sp" />
  87. </RelativeLayout>
  88. <RelativeLayout
  89. android:layout_width="match_parent"
  90. android:layout_height="56dp" >
  91. <TextView
  92. android:id="@+id/tv_weight"
  93. android:layout_width="wrap_content"
  94. android:layout_height="match_parent"
  95. android:gravity="center"
  96. android:text="体重:"
  97. android:textColor="@color/black"
  98. android:textSize="17sp" />
  99. <EditText
  100. android:id="@+id/et_weight"
  101. android:layout_width="match_parent"
  102. android:layout_height="match_parent"
  103. android:layout_marginBottom="3dp"
  104. android:layout_marginTop="3dp"
  105. android:layout_toRightOf="@+id/tv_weight"
  106. android:background="@drawable/editext_selector"
  107. android:gravity="left|center"
  108. android:hint="请输入体重"
  109. android:inputType="numberDecimal"
  110. android:maxLength="5"
  111. android:textColor="@color/black"
  112. android:textSize="17sp" />
  113. </RelativeLayout>
  114. <Button
  115. android:id="@+id/btn_add_user"
  116. android:layout_width="match_parent"
  117. android:layout_height="wrap_content"
  118. android:gravity="center"
  119. android:text="添加用户信息"
  120. android:textColor="@color/black"
  121. android:textSize="17sp" />
  122. <Button
  123. android:id="@+id/btn_jump"
  124. android:layout_width="match_parent"
  125. android:layout_height="wrap_content"
  126. android:gravity="center"
  127. android:text="跳转到用户信息"
  128. android:textColor="@color/black"
  129. android:textSize="17sp" />
  130. </LinearLayout>
  1. package com.example.roomdatabase;
  2. import androidx.appcompat.app.AppCompatActivity;
  3. import android.content.ContentValues;
  4. import android.os.Bundle;
  5. import android.view.View;
  6. import android.widget.EditText;
  7. import android.widget.Toast;
  8. import com.example.roomdatabase.entity.UserInfo;
  9. import com.example.roomdatabase.entity.UserInfoContent;
  10. public class ContentWriteActivity extends AppCompatActivity implements View.OnClickListener {
  11. private static final String TAG = "ContentWriteActivity";
  12. private EditText et_name; // 声明一个编辑框对象
  13. private EditText et_age; // 声明一个编辑框对象
  14. private EditText et_height; // 声明一个编辑框对象
  15. private EditText et_weight; // 声明一个编辑框对象
  16. @Override
  17. protected void onCreate(Bundle savedInstanceState) {
  18. super.onCreate(savedInstanceState);
  19. setContentView(R.layout.activity_content_write);
  20. et_name = findViewById(R.id.et_name);
  21. et_age = findViewById(R.id.et_age);
  22. et_height = findViewById(R.id.et_height);
  23. et_weight = findViewById(R.id.et_weight);
  24. findViewById(R.id.btn_add_user).setOnClickListener(this);
  25. findViewById(R.id.btn_jump).setOnClickListener(this);
  26. }
  27. @Override
  28. public void onClick(View v) {
  29. if (v.getId() == R.id.btn_add_user) {
  30. UserInfo user = new UserInfo();
  31. user.name = et_name.getText().toString();
  32. user.age = Integer.parseInt(et_age.getText().toString());
  33. user.height = Integer.parseInt(et_height.getText().toString());
  34. user.weight = Float.parseFloat(et_weight.getText().toString());
  35. addUser(user); // 添加一条用户记录
  36. }
  37. }
  38. // 添加一条用户记录
  39. private void addUser(UserInfo user) {
  40. ContentValues name = new ContentValues();
  41. name.put("name", user.name);
  42. name.put("age", user.age);
  43. name.put("height", user.height);
  44. name.put("weight", user.weight);
  45. name.put("married", 0);
  46. name.put("update_time", DateUtil.getNowDateTime(""));
  47. // 通过内容解析器往指定Uri添加用户信息
  48. getContentResolver().insert(UserInfoContent.CONTENT_URI, name);
  49. Toast.makeText(this, "成功添加用户信息", Toast.LENGTH_SHORT).show();
  50. }
  51. }

        至于删除操作就更简单了,只要下面一行代码就删除了所有记录:

getContentResolver().delete(UserInfoContent.CONTENT_URI,"1=1",null);

        查询操作稍微复杂一些,调用query方法会返回游标对象,这个游标正是SQLite的游标Cursor,详细用法参见本章的“6.2.3  数据库帮助器SQLiteOpenHelper”。query方法的参数有好几个,具体说明如下(依参数顺序排列):

        ●  uri:Uri类型,指定本次操作的数据表路径。

        ●  projection:字符串数组类型,指定将要查询的字段名称列表。

        ●  selection:字符串类型,指定查询条件。

        ●  selectionArgs:字符串数组类型,指定查询条件中的参数取值列表。

        ●  sortOrder:字符串类型,指定排序条件。

        下面是调用query方法从内容提供器查询所有用户信息的代码例子:

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  3. xmlns:app="http://schemas.android.com/apk/res-auto"
  4. xmlns:tools="http://schemas.android.com/tools"
  5. android:layout_width="match_parent"
  6. android:layout_height="match_parent"
  7. android:orientation="vertical"
  8. tools:context=".ContentReadActivity">
  9. <Button
  10. android:id="@+id/btn_delete"
  11. android:layout_width="match_parent"
  12. android:layout_height="wrap_content"
  13. android:text="删除所有记录"
  14. android:textColor="@color/black"
  15. android:textSize="17sp" />
  16. <TextView
  17. android:id="@+id/tv_desc"
  18. android:layout_width="match_parent"
  19. android:layout_height="wrap_content"
  20. android:paddingLeft="5dp"
  21. android:textColor="@color/black"
  22. android:textSize="17sp" />
  23. <ScrollView
  24. android:layout_width="match_parent"
  25. android:layout_height="wrap_content">
  26. <LinearLayout
  27. android:id="@+id/ll_list"
  28. android:layout_width="match_parent"
  29. android:layout_height="wrap_content"
  30. android:orientation="vertical" />
  31. </ScrollView>
  32. </LinearLayout>
  1. package com.example.roomdatabase;
  2. import androidx.appcompat.app.AppCompatActivity;
  3. import android.annotation.SuppressLint;
  4. import android.database.Cursor;
  5. import android.graphics.Color;
  6. import android.os.Bundle;
  7. import android.view.View;
  8. import android.widget.LinearLayout;
  9. import android.widget.TextView;
  10. import android.widget.Toast;
  11. import com.example.roomdatabase.entity.UserInfo;
  12. import com.example.roomdatabase.entity.UserInfoContent;
  13. import java.util.ArrayList;
  14. import java.util.List;
  15. @SuppressLint("Range")
  16. public class ContentReadActivity extends AppCompatActivity implements View.OnClickListener {
  17. private static final String TAG = "ContentReadActivity";
  18. private TextView tv_desc; // 声明一个文本视图对象
  19. private LinearLayout ll_list; // 用户信息列表的线性布局
  20. @Override
  21. protected void onCreate(Bundle savedInstanceState) {
  22. super.onCreate(savedInstanceState);
  23. setContentView(R.layout.activity_content_read);
  24. findViewById(R.id.btn_delete).setOnClickListener(this);
  25. tv_desc = findViewById(R.id.tv_desc);
  26. ll_list = findViewById(R.id.ll_list);
  27. showAllUser(); // 显示所有的用户记录
  28. }
  29. // 显示所有的用户记录
  30. private void showAllUser() {
  31. List<UserInfo> userList = new ArrayList<UserInfo>();
  32. // 通过内容解析器从指定Uri中获取用户记录的游标
  33. Cursor cursor = getContentResolver().query(UserInfoContent.CONTENT_URI, null, null, null, null);
  34. // 循环取出游标指向的每条用户记录
  35. while (cursor.moveToNext()) {
  36. UserInfo user = new UserInfo();
  37. user.name = cursor.getString(cursor.getColumnIndex(UserInfoContent.USER_NAME));
  38. user.age = cursor.getInt(cursor.getColumnIndex(UserInfoContent.USER_AGE));
  39. user.height = cursor.getInt(cursor.getColumnIndex(UserInfoContent.USER_HEIGHT));
  40. user.weight = cursor.getFloat(cursor.getColumnIndex(UserInfoContent.USER_WEIGHT));
  41. userList.add(user); // 添加到用户信息列表
  42. }
  43. cursor.close(); // 关闭数据库游标
  44. String contactCount = String.format("当前共找到%d个用户", userList.size());
  45. tv_desc.setText(contactCount);
  46. ll_list.removeAllViews(); // 移除线性布局下面的所有下级视图
  47. for (UserInfo user : userList) { // 遍历用户信息列表
  48. String contactDesc = String.format("姓名为%s,年龄为%d,身高为%d,体重为%f\n",
  49. user.name, user.age, user.height, user.weight);
  50. TextView tv_contact = new TextView(this); // 创建一个文本视图
  51. tv_contact.setText(contactDesc);
  52. tv_contact.setTextColor(Color.BLACK);
  53. tv_contact.setTextSize(17);
  54. int pad = Utils.dip2px(this, 5);
  55. tv_contact.setPadding(pad, pad, pad, pad); // 设置文本视图的内部间距
  56. ll_list.addView(tv_contact); // 把文本视图添加至线性布局
  57. }
  58. }
  59. @Override
  60. public void onClick(View v) {
  61. if (v.getId() == R.id.btn_delete) {
  62. getContentResolver().delete(UserInfoContent.CONTENT_URI, "1=1", null);
  63. showAllUser();
  64. Toast.makeText(this, "已删除所有记录", Toast.LENGTH_SHORT).show();
  65. }
  66. }
  67. }
  1. package com.example.roomdatabase;
  2. import android.content.Context;
  3. public class Utils {
  4. // 根据手机的分辨率从 dp 的单位 转成为 px(像素)
  5. public static int dip2px(Context context, float dpValue) {
  6. // 获取当前手机的像素密度(1个dp对应几个px)
  7. float scale = context.getResources().getDisplayMetrics().density;
  8. return (int) (dpValue * scale + 0.5f); // 四舍五入取整
  9. }
  10. // 根据手机的分辨率从 px(像素) 的单位 转成为 dp
  11. public static int px2dip(Context context, float pxValue) {
  12. // 获取当前手机的像素密度(1个dp对应几个px)
  13. float scale = context.getResources().getDisplayMetrics().density;
  14. return (int) (pxValue / scale + 0.5f); // 四舍五入取整
  15. }
  16. // 获得屏幕的宽度
  17. public static int getScreenWidth(Context ctx) {
  18. return ctx.getResources().getDisplayMetrics().widthPixels;
  19. }
  20. // 获得屏幕的高度
  21. public static int getScreenHeight(Context ctx) {
  22. return ctx.getResources().getDisplayMetrics().heightPixels;
  23. }
  24. }

        接下来分别演示通过内容解析器添加和查询用户信息的过程,其中记录添加页面为ContentWriteActivity.java,记录查询页面为ContentReadActivity.java。运行测试App,先打开记录添加页面,输入用户信息后点击“添加用户信息”按钮,由内容解析器执行插入操作,此时添加界面如图所示。

接着打开记录查询页面,内容解析器自动执行查询操作,并将查到的用户信息一一显示出来,此时查询界面如图所示。

        对比添加页面和查询页面的用户信息,可知成功查到了新增的用户记录。

6.5.3  利用ContentResolver读写联系人

        在实际开发中,普通App很少会开放数据接口给其他应用访问,作为服务端接口的ContentProvider基本用不到。内容组件能够派上用场的情况,往往是App想要访问系统应用的通讯数据,比如查看联系人、短信、通话记录,以及对这些通讯数据进行增删改查。

        在访问系统的通讯数据之前,得先在AndroidManifest.xml中添加相应的权限配置,常见的通讯权限配置主要有下面几个:

本文内容由网友自发贡献,转载请注明出处:https://www.wpsshop.cn/w/凡人多烦事01/article/detail/205381
推荐阅读
相关标签
  

闽ICP备14008679号