《第一行代码》读书笔记(十一)----内容提供器
来源:互联网 发布:杜兰特07年首秀数据 编辑:程序博客网 时间:2024/05/14 04:22
Android 的数据持久化所保存的数据只能在当前应用程序中访问.
SharedPreferences 存储中提供了 MODE_WORLD_READABLE
和 MODE_WORLD_WRITEABLE
两种模式为什么在 4.2 版本以后被废弃了呢?
官方不推荐这种方式, 应该使用更加安全可靠的内容提供器技术.
账号密码等隐私数据不应提供给其他应用, 但是可以让其他程序进行二次开发的基础性数据还是可以提供的, 比如说电话簿, 短信, 媒体库等都提供了跨程序数据共享的功能.
简介
内容提供器(Content Provider) 用于不同的应用程序之间实现数据共享的功能. 它允许一个程序访问另一个程序的数据, 同时还能保证安全性. 可以选择只对哪一部分数据进行共享, 从而保证隐私数据不会泄露.
访问其他程序中的数据
ContentResolver 的基本用法
如果想要访问内容提供器中共享的数据, 必须使用 ContentResolve 类, 可以通过 Context 的 getContentResolver()
方法获取到该类的实例. 该类提供了一系列方法诸如 insert()
, update()
, delete()
, query()
来对数据进行 CRUD 操作.
这些方法的参数不接收表名, 而是使用 Uri 参数, 称为 内容URI. 它为数据建立了唯一标识符, 由 权限(authority) 和路径(path) 两部分组成. 一般使用程序包名的方式命名权限, 路径添加在权限的后面. 还需要在字符串的头部加上协议声明. 标准格式:
content://com.example.app.provider/table1content://com.example.app.provider/table2
有了 内容URI字符串, 需要将它解析成Uri对象才可以作为参数传入:
Uri uri = Uri.parse("content://com.example.app.provider/table1");
使用这个 Uri 对象来查询 table1 表中的数据:
Cursor cursor = getContentResolver().query( uri, projection, selection, selectionArgs, sortOrder);
这些参数的意义:
- uri: 指定查询某个应用程序下的某一张表
- projection: 指定查询的列名
- selection: 指定 where 的约束条件
- selectionArgs: 为 where 中的占位符提供具体的值
- orderBy: 指定查询结果的排序方式
查询返回的是一个 Cursor 对象, 读取:
if (cursor != null) { while (cursor.moveToNext()) { String column1 = cursor.getString(cursor.getColumnIndex("column1")); int culumn2 = cursor.getInt(cursor.getColumnIndex("column2")); }}
添加数据:
ContentValues values = new ContentValues();values.put("column1", "text");values.put("column2", 1);getContentResolver().insert(uri, values);
更新数据:
ContentValues values = new ContentValues();values.put("column1", "");getContentResolver().update(uri, values, "column1 = ? and column2 = ?", new String[] {"text", "1"});
删除数据:
getContentResolver.delete(uri, "column2 = ?", new String[] {"1"});
读取系统联系人
新建一个项目, 修改 activity_main.xml, 添加一个 ListView, 用来显示系统的联系人.
修改 MainActivity :
public class MainActivity extends Activity { ListView contactsView; ArrayAdapter<String> adapter; List<String> contactsList = new ArrayList<String>(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); contactsView = (ListView) findViewById(R.id.contacts_view); adapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, contactsList); contactsView.setAdapter(adapter); readContacts(); } private void readContacts() { Cursor cursor = null; try { //查询联系人数据 cursor = getContentResolver().query( ContactsContract.CommonDataKinds.Phone.CONTENT_URI, null, null, null, null); while (cursor.moveToNext()) { //获取联系人姓名 String displayName = cursor.getString(cursor.getColumnIndex( ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME)); //获取联系人手机号 String number = cursor.getString(cursor.getColumnIndex( ContactsContract.CommonDataKinds.Phone.NUMBER)); contactsList.add(displayName + "\n" + number); } } catch (Exception e) { e.printStackTrace(); } finally { if (cursor != null) { cursor.close(); } } }}
最后添加权限:
<uses-permission android:name="android.permission.READ_CONTACTS"/>
创建自己的内容提供器
创建内容提供器的步骤
新建一个类继承 ContentProvider 类, 重写它的全部六个抽象方法.
* onCreate()
: 初始化时调用. 在这里完成对数据库的创建和升级等操作.返回的是布尔值. 只有当存在 ContentResolver 尝试访问程序中数据时, 内容提供器才会被初始化.
* query()
: 从内容提供器中查询数据.
* insert()
: 向内容提供器添加数据.
* update()
: 更新内容提供器中的已有数据.
* delete()
: 从内容提供器中删除数据.
* getType()
: 根据传入的内容URI来返回相应的 MIME 类型. 一个内容URI所对应的 MIME 字符串主要由三部分组成:
* 必须以 vnd
开头.
* 如果内容URI以路径结尾, 则后接 android.cursor.dir/
, 如果内容URI以id 结尾, 则后接android.cursor.item/
* 最后接上 vnd.<authority>.<path>
内容URI的格式还有另外一种, 就是以id结尾, 比如:
content://com.example.app.provider/table1/1
表示期望访问该表中拥有相应 id 的数据.
如果以路径结尾, 表示期望访问该表中所有的数据.
可以使用通配符的方式来分别匹配这两种格式的内容URI, 规则:
1, *
: 表示匹配任何长度的任意字符
2, #
: 表示匹配任意长度的数字
所以, 能够匹配任意表的内容URI可以写成:
content://com.example.app.provider/*
能够匹配table1表中任意一行数据的内容URI可以写成:
content://com.example.app.provider/table1/#
可以借助 UriMatcher 类来实现匹配内容URI功能. 该类提供一个 addURI()
方法, 三个参数可以分别把权限, 路径 和 一个自定义代码传进去. 当调用 UriMatcher 的 match()
方法时, 就可以将一个 Uri 对象传入, 返回值是某个能够匹配这个 Uri 对象所对应的自定义代码, 利用这个代码, 判断出掉用方期望访问的是哪张表中的数据.
新建 MyProvider 继承自 ContentProvider:
public class MyProvider extends ContentProvider { public static final int TABLE1_DIR = 0; public static final int TABLE1_ITEM = 1; public static final int TABLE2_DIR = 2; public static final int TABLE2_ITEM = 3; private static UriMatcher uriMatcher; static { uriMatcher = new UriMatcher(UriMatcher.NO_MATCH); uriMatcher.addURI("com.example.app.provider", "table1", TABLE1_DIR); uriMatcher.addURI("com.example.app.provider", "table1/#", TABLE1_ITEM); uriMatcher.addURI("com.example.app.provider", "table2", TABLE2_DIR); uriMatcher.addURI("com.example.app.provider", "table2/#", TABLE2_ITEM); } @Override public boolean onCreate() { return false; } @Override public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { switch (uriMatcher.match(uri)) { case TABLE1_DIR: //查询table1表中的所有数据 break; case TABLE1_ITEM: //查询table1表中的单条数据 break; case TABLE2_DIR: //查询table2表中的所有数据 break; case TABLE2_ITEM: //查询table2表中的单条数据 break; default: break; } return null; } @Override public String getType(Uri uri) { switch (uriMatcher.match(uri)) { case TABLE1_DIR: return "vnd.android.cursor.dir/vnd.com.example.app.provider.table1"; case TABLE1_ITEM: return "vnd.android.cursor.item/vnd.com.example.app.provider.table1"; case TABLE2_DIR: return "vnd.android.cursor.dir/vnd.com.example.app.provider.table2"; case TABLE2_ITEM: return "vnd.android.cursor.item/vnd.com.example.app.provider.table2"; default: break; } return null; } @Override public Uri insert(Uri uri, ContentValues values) { return null; } @Override public int delete(Uri uri, String selection, String[] selectionArgs) { return 0; } @Override public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { return 0; }}
所有的 CRUD 操作都一定要匹配到相应的内容URI格式才能进行, 我们不向 UriMatcher 中添加隐私数据的URI, 这样就保证了安全性.
实现跨程序数据共享
在之前写过的 Database 项目里添加一个类 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 = "me.zipstream.database.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 String getType(Uri uri) { switch (uriMatcher.match(uri)) { case BOOK_DIR: return "vnd.android.cursor.dir/vnd.me.zipstream.database.provider.book"; case BOOK_ITEM: return "vnd.android.cursor.item/vnd.me.zipstream.database.provider.book"; case CATEGORY_DIR: return "vnd.android.cursor.dir/vnd.me.zipstream.database.provider.category"; case CATEGORY_ITEM: return "vnd.android.cursor.item/vnd.me.zipstream.database.provider.category"; } return null; } @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 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 int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { SQLiteDatabase db = dbHelper.getWritableDatabase(); int updateRows = 0; switch (uriMatcher.match(uri)) { case BOOK_DIR: updateRows = db.update("Book", values, selection, selectionArgs); break; case BOOK_ITEM: String bookId = uri.getPathSegments().get(1); updateRows = db.update("Book", values, "id = ?", new String[]{bookId}); break; case CATEGORY_DIR: updateRows = db.update("category", values, selection, selectionArgs); break; case CATEGORY_ITEM: String categoryId = uri.getPathSegments().get(1); updateRows = db.update("Category", values, "id = ?", new String[]{categoryId}); break; default: break; } return updateRows; }}
在 AndroidManifest.xml 文件中注册这个内容提供者:
<provider android:authorities="me.zipstream.database.provider" android:name="me.zipstream.database.DatabaseProvider" android:exported="true"></provider>
其中:
android:authorities 指定该内容提供器的权限
android:name 指定该类的全名
android:exported 指定这个内容提供器可以被其他应用程序访问
现在这个程序就已经拥有跨程序共享数据的功能了.
再新建一个项目来访问数据, 布局文件添加了四个按钮, 分别用来增删改查数据.
修改 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); addData.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Uri uri = Uri.parse("content://me.zipstream.database.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); queryData.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Uri uri = Uri.parse("content://me.zipstream.database.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("TAG", "book name is " + name); Log.d("TAG", "book author is " + author); Log.d("TAG", "book pages is " + pages); Log.d("TAG", "book price is " + price); } cursor.close(); } } }); Button updateData = (Button) findViewById(R.id.update); updateData.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Uri uri = Uri.parse("content://me.zipstream.database.provider/book" + newId); ContentValues values = new ContentValues(); values.put("name", "A Storm of Swords"); values.put("pages", 1216); values.put("price", 24.99); getContentResolver().update(uri, values, null, null); } }); Button deleteData = (Button) findViewById(R.id.delete); deleteData.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Uri uri = Uri.parse("content://me.zipstream.database.provider/book" + newId); getContentResolver().delete(uri, null, null); } }); }}
- 《第一行代码》读书笔记(十一)----内容提供器
- 《第一行代码--Android》读书笔记之内容提供器
- 第一行代码-第7章 内容提供器
- [Android][第一行代码][第 7 章 内容提供器]
- 第一行代码-7.1 内容提供器简介
- 第一行代码-7.3 创建自己的内容提供器
- Android第一行代码学习笔记五----内容提供器
- 内容提供器(ContentResolver)---《第一行代码Android》笔记
- 第一行代码 第7章 内容提供器 -- 运行时权限
- 第一行代码 第7章 内容提供器 --基本用法
- 第一行代码 第7章 内容提供器 --实现跨程序数据共享
- 《第一行代码》读书笔记
- 第一行代码读书笔记
- 《第一行代码》读书笔记
- 《第一行代码Android》读书笔记
- 第一行代码 读书笔记 2
- Android第一行代码读书笔记
- 第一行代码读书笔记1
- html颜色代码大全,二进制颜色代码大全,markdown 字体颜色设置
- REST介绍
- redis服务器的运维(配置、启动)
- 0914_vbs加密解密
- Map遍历·
- 《第一行代码》读书笔记(十一)----内容提供器
- 安装 Windows 至虚拟机失败
- virtualBox虚拟机安装CentOS7.0
- (一)Android ViewTreeObserver的常用技巧
- hibernate 实现多表连接查询 查询结果映射到自定义类中
- HTTPS与SSL详解(一)
- java 入门
- MySQL之——修改默认存储引擎
- android 中如何限制 EditText 最大输入字符数