| |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
-> 移动开发 -> Android组件之ContentProvider -> 正文阅读 |
|
[移动开发]Android组件之ContentProvider |
ContentProvider(内容提供者)是 Android 的四大组件之一,管理 Android 以结构化方式存放的数据,以相对安全的方式封装数据(表)并且提供简易的处理机制和统一的访问接口供其他程序调用。Android 的数据存储方式总共有五种,分别是:Shared Preferences、网络存储、文件存储、外储存储、SQLite。但一般这些存储都只是在单独的一个应用程序之中达到一个数据的共享,有时候我们需要操作其他应用程序的一些数据,就会用到 ContentProvider。而且 Android 为常见的一些数据提供了默认的 ContentProvider(包括音频、视频、图片和通讯录等)。要实现与其他的 ContentProvider 通信首先要查找到对应的 ContentProvider 进行匹配。Android 中 ContenProvider 借助 ContentResolver 通过 Uri 与其他的 ContentProvider 进行匹配通信。 URI(Uniform Resource Identifier)其它应用可以通过 ContentResolver 来访问 ContentProvider 提供的数据,而 ContentResolver 通过 uri 来定位自己要访问的数据,所以我们要先了解 uri。URI(Universal Resource Identifier)统一资源定位符,如果您使用过安卓的隐式启动就会发现,在隐式启动的过程中我们也是通过 uri 来定位我们需要打开的 Activity 并且可以在 uri 中传递参数。 URI 为系统中的每一个资源赋予一个名字,比方说通话记录。每一个 ContentProvider 都拥有一个公共的 URI,用于表示 ContentProvider 所提供的数据。URI 的格式如下: // 规则 [scheme:][//host:port][path][?query] // 示例 content://com.wang.provider.myprovider/tablename/id:
对于第三部分路径(path)做进一步的解释,用来表示要操作的数据,构建时应根据实际项目需求而定。如:
Uri uri = Uri.parse("content://com.wang.provider.myprovider/tablename"); 再来看一个例子: http://www.baidu.com:8080/wenku/jiatiao.html?id=123456&name=jack uri 的各个部分在安卓中都是可以通过代码获取的,下面我们就以上面这个 uri 为例来说下获取各个部分的方法:
MIMEMIME 是指定某个扩展名的文件用一种应用程序来打开,就像你用浏览器查看 PDF 格式的文件,浏览器会选择合适的应用来打开一样。Android 中的工作方式跟 HTTP 类似,ContentProvider 会根据 URI 来返回 MIME 类型,ContentProvider 会返回一个包含两部分的字符串。MIME 类型一般包含两部分,如: text/html text/css text/xml application/pdf 分为类型和子类型,Android 遵循类似的约定来定义MIME类型,每个内容类型的 Android MIME 类型有两种形式:多条记录(集合)和单条记录。
vnd.android.cursor.dir/自定义
vnd.android.cursor.item/自定义
vnd 表示这些类型和子类型具有非标准的、供应商特定的形式。Android中类型已经固定好了,不能更改,只能区别是集合还是单条具体记录,子类型可以按照格式自己填写。 在使用 Intent 时,会用到 MIME,根据 Mimetype 打开符合条件的活动。 UriMatcherUri 代表要操作的数据,在开发过程中对数据进行获取时需要解析 Uri,Android 提供了两个用于操作 Uri 的工具类,分别为 UriMatcher 和 ContentUris 。掌握它们的基本概念和使用方法,对一个 Android 开发者来说是一项必要的技能。 UriMatcher 类用于匹配 Uri,它的使用步骤如下:
//常量UriMatcher.NO_MATCH表示不匹配任何路径的返回码 UriMatcher sMatcher = new UriMatcher(UriMatcher.NO_MATCH); //如果match()方法匹配“content://com.wang.provider.myprovider/tablename”路径,返回匹配码为1 sMatcher.addURI("content://com.wang.provider.myprovider", " tablename ", 1); //如果match()方法匹配content://com.wang.provider.myprovider/tablename/11路径,返回匹配码为2 sMatcher.addURI("com.wang.provider.myprovider", "tablename/#", 2); 此处采用 addURI 注册了两个需要用到的 URI;注意,添加第二个 URI 时,路径后面的 id 采用了通配符形式 “#”,表示只要前面三个部分都匹配上了就 OK。
switch (sMatcher.match(Uri.parse("content://com.zhang.provider.yourprovider/tablename/100"))) { case 1: //match 1, todo something break; case 2 //match 2, todo something break; default: //match nothing, todo something break; } ContentUrisContentUris 类用于操作 Uri 路径后面的 ID 部分,它有两个比较实用的方法:withAppendedId(Uri uri, long id) 和 parseId(Uri uri)。
Uri uri = Uri.parse("content://cn.scu.myprovider/user") //生成后的Uri为:content://cn.scu.myprovider/user/7 Uri resultUri = ContentUris.withAppendedId(uri, 7);
Uri uri = Uri.parse("content://cn.scu.myprovider/user/7") //获取的结果为:7 long personid = ContentUris.parseId(uri); ContentProvider 主要方法ContentProvider 是一个抽象类,如果我们需要开发自己的内容提供者我们就需要继承这个类并复写其方法,需要实现的主要方法如下:
数据访问的方法 insert,delete 和 update 可能被多个线程同时调用,此时必须是线程安全的。 如果操作的数据属于集合类型,那么 MIME 类型字符串应该以 vnd.android.cursor.dir/ 开头,
如果要操作的数据属于非集合类型数据,那么 MIME 类型字符串应该以 vnd.android.cursor.item/ 开头,
方法使用示例使用 ContentResolver 对 ContentProvider 中的数据进行操作的代码如下: ContentResolver resolver = getContentResolver(); Uri uri = Uri.parse("content://com.wang.provider.myprovider/tablename"); // 添加一条记录 ContentValues values = new ContentValues(); values.put("name", "wang1"); values.put("age", 28); resolver.insert(uri, values); // 获取tablename表中所有记录 Cursor cursor = resolver.query(uri, null, null, null, "tablename data"); while(cursor.moveToNext()){ Log.i("ContentTest", "tablename_id="+ cursor.getInt(0)+ ", name="+ cursor.getString(1)); } // 把id为1的记录的name字段值更改新为zhang1 ContentValues updateValues = new ContentValues(); updateValues.put("name", "zhang1"); Uri updateIdUri = ContentUris.withAppendedId(uri, 2); resolver.update(updateIdUri, updateValues, null, null); // 删除id为2的记录,即字段age Uri deleteIdUri = ContentUris.withAppendedId(uri, 2); resolver.delete(deleteIdUri, null, null); 监听数据变化如果ContentProvider的访问者需要知道数据发生的变化,可以在ContentProvider发生数据变化时调用getContentResolver().notifyChange(uri, null)来通知注册在此URI上的访问者。只给出类中监听部分的代码: public class MyProvider extends ContentProvider { public Uri insert(Uri uri, ContentValues values) { db.insert("tablename", "tablenameid", values); getContext().getContentResolver().notifyChange(uri, null); } } 而访问者必须使用 ContentObserver 对数据(数据采用 uri 描述)进行监听,当监听到数据变化通知时,系统就会调用 ContentObserver 的 onChange() 方法: getContentResolver().registerContentObserver(Uri.parse("content://com.ljq.providers.personprovider/person"), true, new PersonObserver(new Handler())); public class PersonObserver extends ContentObserver{ public PersonObserver(Handler handler) { super(handler); } public void onChange(boolean selfChange) { //to do something } } 实例说明数据源是 SQLite, 用 ContentResolver 操作 ContentProvider。 Constant.java(储存一些常量) public class Constant { public static final String TABLE_NAME = "user"; public static final String COLUMN_ID = "_id"; public static final String COLUMN_NAME = "name"; public static final String AUTOHORITY = "cn.scu.myprovider"; public static final int ITEM = 1; public static final int ITEM_ID = 2; public static final String CONTENT_TYPE = "vnd.android.cursor.dir/user"; public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/user"; public static final Uri CONTENT_URI = Uri.parse("content://" + AUTOHORITY + "/user"); } DBHelper.java (操作数据库) public class DBHelper extends SQLiteOpenHelper { private static final String DATABASE_NAME = "finch.db"; private static final int DATABASE_VERSION = 1; public DBHelper(Context context) { super(context, DATABASE_NAME, null, DATABASE_VERSION); } @Override public void onCreate(SQLiteDatabase db) throws SQLException { //创建表格 db.execSQL("CREATE TABLE IF NOT EXISTS "+ Constant.TABLE_NAME + "("+ Constant.COLUMN_ID +" INTEGER PRIMARY KEY AUTOINCREMENT," + Constant.COLUMN_NAME +" VARCHAR NOT NULL);"); } @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) throws SQLException { // 这里知识简单删除并创建表格 // 如果需要保留原来的数据,需要先备份再删除 db.execSQL("DROP TABLE IF EXISTS "+ Constant.TABLE_NAME+";"); onCreate(db); } } MyProvider.java (自定义的 ContentProvider ) public class MyProvider extends ContentProvider { DBHelper mDbHelper = null; SQLiteDatabase db = null; private static final UriMatcher mMatcher; static{ mMatcher = new UriMatcher(UriMatcher.NO_MATCH); // 注册 uri mMatcher.addURI(Constant.AUTOHORITY,Constant.TABLE_NAME, Constant.ITEM); mMatcher.addURI(Constant.AUTOHORITY, Constant.TABLE_NAME+"/#", Constant.ITEM_ID); } @Override public String getType(Uri uri) { // 根据匹配规则返回对应的类型 switch (mMatcher.match(uri)) { case Constant.ITEM: return Constant.CONTENT_TYPE; case Constant.ITEM_ID: return Constant.CONTENT_ITEM_TYPE; default: throw new IllegalArgumentException("Unknown URI"+uri); } } @Override public Uri insert(Uri uri, ContentValues values) { // TODO Auto-generated method stub long rowId; if(mMatcher.match(uri)!=Constant.ITEM){ throw new IllegalArgumentException("Unknown URI"+uri); } rowId = db.insert(Constant.TABLE_NAME,null,values); if(rowId>0){ Uri noteUri=ContentUris.withAppendedId(Constant.CONTENT_URI, rowId); getContext().getContentResolver().notifyChange(noteUri, null); return noteUri; } throw new SQLException("Failed to insert row into " + uri); } @Override public boolean onCreate() { // TODO Auto-generated method stub mDbHelper = new DBHelper(getContext()); db = mDbHelper.getReadableDatabase(); return true; } @Override public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { // TODO Auto-generated method stub Cursor c = null; switch (mMatcher.match(uri)) { case Constant.ITEM: c = db.query(Constant.TABLE_NAME, projection, selection, selectionArgs, null, null, sortOrder); break; case Constant.ITEM_ID: c = db.query(Constant.TABLE_NAME, projection,Constant.COLUMN_ID + "="+uri.getLastPathSegment(), selectionArgs, null, null, sortOrder); break; default: throw new IllegalArgumentException("Unknown URI"+uri); } c.setNotificationUri(getContext().getContentResolver(), uri); return c; } @Override public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { // TODO Auto-generated method stub return 0; } @Override public int delete(Uri uri, String selection, String[] selectionArgs) { // TODO Auto-generated method stub return 0; } } MainActivity.java(ContentResolver操作) public class MainActivity extends Activity { private ContentResolver mContentResolver = null; private Cursor cursor = null; @Override protected void onCreate(Bundle savedInstanceState) { // TODO Auto-generated method stub super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); TextView tv = (TextView) findViewById(R.id.tv); mContentResolver = getContentResolver(); tv.setText("添加初始数据 "); for (int i = 0; i < 10; i++) { ContentValues values = new ContentValues(); values.put(Constant.COLUMN_NAME, "fanrunqi"+i); mContentResolver.insert(Constant.CONTENT_URI, values); } tv.setText("查询数据 "); cursor = mContentResolver.query(Constant.CONTENT_URI, new String[]{Constant.COLUMN_ID,Constant.COLUMN_NAME}, null, null, null); if (cursor.moveToFirst()) { String s = cursor.getString(cursor.getColumnIndex(Constant.COLUMN_NAME)); tv.setText("第一个数据: "+s); } } } 最后在manifest申明 : <provider android:name="MyProvider" android:authorities="cn.scu.myprovider" /> 总结:
额外补充:隐式 Intent 中 <data> 标签该部分内容与?ContentProvider 没关系,只是这里讲到了 URI,就顺便此处在插入另外一个知识点:Intent 中 <data> 标签。看不懂的可以直接略过,看下一步分的内容,此处内容与 activity 相关。 Data 的匹配规则:如果过滤规则 intent-filter 中定义了 data,那么 Intent 中必须也要携带可匹配的 data data 的语法如下所示: <data android:scheme=“string” android:host=“string” android:port=“string” android:path=“string” android:pathPattern=“string” android:pathPrefix=“string” android:mimeType=“string”> data 由两部分组成:mimeType 和 URI。mimeType 可以为空,URI 一定不会为空,因为有默认值。mimeType 指媒体类型,比如 image/jpeg,video/* 等,可表示图片,视频等不同的媒体格式 示例1? data 的匹配: <intent-filter> <action android:name="com.action.demo1"></action> <category android:name="android.intent.category.DEFAULT" /> <data android:scheme="x7" android:host="www.360.com" /> </intent-filter>? 清单文件 intent-filter 定义的 data 中,只有 URI, 没有 mimeType 类型,匹配如下 intent.setData(Uri.parse("x7://www.360.com")) 示例2 <intent-filter> <action android:name="com.action.demo1"></action> <category android:name="android.intent.category.DEFAULT" /> <data android:mimeType="image/*" /> </intent-filter> 清单文件 intent-filter 定义的 data 中,没有定义 URI,只有 mimeType 类型,但是 URI 却有默认值,URI 中的 scheme 默认为 content 或者 file,host 一定不能为空,随便给个字符串abc 都可以,匹配如下 intent.setDataAndType(Uri.parse("content://abc"),"image/png"); 注意: 示例3 <intent-filter> <action android:name="com.action.demo1"></action> <category android:name="android.intent.category.DEFAULT" /> <data android:mimeType="image/*" android:scheme="x7" android:host="www.360.com" /> <data android:mimeType="video/*" android:scheme="x7" android:host="www.360.com" /> </intent-filter> 清单文件 intent-filter 定义的 data 中,URI 和 mimeType 都有, 匹配如下 intent.setDataAndType(Uri.parse("x7://www.360.com"),"image/png"); // 或者 intent.setDataAndType(Uri.parse("x7://www.360.com"),"video/mpeg"); Inent 中携带的 data 标签对应的数据,在某一组 intent-filter 中可以找到,即匹配成功。data 标签数据在 intent-filter 中也可以有多组 隐示启动,防止匹配失败的可以提前检测: 判断的方法如下: PackageManager mPackageManager = getPackageManager(); //返回匹配成功中最佳匹配的一个act信息,intent 需要按照前面的把 action, data 等都设置好 ResolveInfo info = mPackageManager.resolveActivity(intent,PackageManager.MATCH_DEFAULT_ONLY); //返回所有匹配成功的act信息,是一个集合 List<ResolveInfo> infoList = mPackageManager.queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY); 只要上述 2 个方法的返回值不为 null,那么 startActivity 一定可以成功 ? |
|
移动开发 最新文章 |
Vue3装载axios和element-ui |
android adb cmd |
【xcode】Xcode常用快捷键与技巧 |
Android开发中的线程池使用 |
Java 和 Android 的 Base64 |
Android 测试文字编码格式 |
微信小程序支付 |
安卓权限记录 |
知乎之自动养号 |
【Android Jetpack】DataStore |
|
上一篇文章 下一篇文章 查看所有文章 |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 | -2024/11/24 1:08:38- |
|
网站联系: qq:121756557 email:121756557@qq.com IT数码 |