电脑知识|欧美黑人一区二区三区|软件|欧美黑人一级爽快片淫片高清|系统|欧美黑人狂野猛交老妇|数据库|服务器|编程开发|网络运营|知识问答|技术教程文章 - 好吧啦网

您的位置:首頁技術文章
文章詳情頁

詳解Android ContentProvider的基本原理和使用

瀏覽:2日期:2022-09-17 17:05:51
目錄一、前言二、URI(Uniform Resource Identifier)三、MIME四、UriMatcher五、ContentUris5.1、方法使用示例5.2、監聽數據變化六、實例說明七、總結7.1、額外補充:隱式 Intent 中 <data> 標簽一、前言

Android 的數據存儲方式總共有五種,分別是:Shared Preferences、網絡存儲、文件存儲、外儲存儲、SQLite。但一般這些存儲都只是在單獨的一個應用程序之中達到一個數據的共享,有時候我們需要操作其他應用程序的一些數據,就會用到 ContentProvider。而且 Android 為常見的一些數據提供了默認的 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:

1.標準前綴(scheme)——content://,用來說明一個Content Provider控制這些數據;

2.URI 的標識 (host:port)—— com.wang.provider.myprovider,用于唯一標識這個 ContentProvider,外部調用者可以根據這個標識來找到它。對于第三方應用程序,為了保證 URI 標識的唯一性,它必須是一個完整的、小寫的類名。這個標識在元素的authorities屬性中說明,一般是定義該 ContentProvider 的包.類的名稱;

3.路徑(path)——tablename,通俗的講就是你要操作的數據庫中表的名字,或者你也可以自己定義,記得在使用的時候保持一致就可以了;

4.記錄ID(query)——id,如果URI中包含表示需要獲取的記錄的 ID,則返回該id對應的數據,如果沒有ID,就表示返回全部;

對于第三部分路徑(path)做進一步的解釋,用來表示要操作的數據,構建時應根據實際項目需求而定。如:

操作tablename表中id為11的記錄,構建路徑:/tablename/11; 操作tablename表中id為11的記錄的name字段:tablename/11/name; 操作tablename表中的所有記錄:/tablename; 操作來自文件、xml或網絡等其他存儲方式的數據,如要操作xml文件中tablename節點下name字段:/ tablename/name; 若需要將一個字符串轉換成Uri,可以使用Uri類中的parse()方法,

Uri uri = Uri.parse('content://com.wang.provider.myprovider/tablename');

再來看一個例子:

http://www.baidu.com:8080/wenku/jiatiao.html?id=123456&name=jack

uri 的各個部分在安卓中都是可以通過代碼獲取的,下面我們就以上面這個 uri 為例來說下獲取各個部分的方法:

getScheme():獲取 Uri 中的 scheme 字符串部分,在這里是 http getHost():獲取 Authority 中的 Host 字符串,即 www.baidu.com getPost():獲取 Authority 中的 Port 字符串,即 8080 getPath():獲取 Uri 中 path 部分,即 wenku/jiatiao.html getQuery():獲取 Uri 中的 query 部分,即 id=15&name=du三、MIME

MIME 是指定某個擴展名的文件用一種應用程序來打開,就像你用瀏覽器查看 PDF 格式的文件,瀏覽器會選擇合適的應用來打開一樣。Android 中的工作方式跟 HTTP 類似,ContentProvider 會根據 URI 來返回 MIME 類型,ContentProvider 會返回一個包含兩部分的字符串。MIME 類型一般包含兩部分,如:

text/html

text/css

text/xml

application/pdf

分為類型和子類型,Android 遵循類似的約定來定義MIME類型,每個內容類型的 Android MIME 類型有兩種形式:多條記錄(集合)和單條記錄。

集合記錄(dir):

vnd.android.cursor.dir/自定義 

單條記錄(item):

vnd.android.cursor.item/自定義 

vnd 表示這些類型和子類型具有非標準的、供應商特定的形式。Android中類型已經固定好了,不能更改,只能區別是集合還是單條具體記錄,子類型可以按照格式自己填寫。

在使用 Intent 時,會用到 MIME,根據 Mimetype 打開符合條件的活動。

四、UriMatcher

Uri 代表要操作的數據,在開發過程中對數據進行獲取時需要解析 Uri,Android 提供了兩個用于操作 Uri 的工具類,分別為 UriMatcher 和 ContentUris 。掌握它們的基本概念和使用方法,對一個 Android 開發者來說是一項必要的技能。

UriMatcher 類用于匹配 Uri,它的使用步驟如下:

將需要匹配的Uri路徑進行注冊,代碼如下:

//常量UriMatcher.NO_MATCH表示不匹配任何路徑的返回碼UriMatcher sMatcher = new UriMatcher(UriMatcher.NO_MATCH);//如果match()方法匹配“content://com.wang.provider.myprovider/tablename”路徑,返回匹配碼為1sMatcher.addURI('content://com.wang.provider.myprovider', ' tablename ', 1);//如果match()方法匹配content://com.wang.provider.myprovider/tablename/11路徑,返回匹配碼為2sMatcher.addURI('com.wang.provider.myprovider', 'tablename/#', 2);

此處采用 addURI 注冊了兩個需要用到的 URI;注意,添加第二個 URI 時,路徑后面的 id 采用了通配符形式 “#”,表示只要前面三個部分都匹配上了就 OK。

注冊完需要匹配的 Uri 后,可以使用 sMatcher.match(Uri) 方法對輸入的 Uri 進行匹配,如果匹配就返回對應的匹配碼,匹配碼為調用 addURI() 方法時傳入的第三個參數。

switch (sMatcher.match(Uri.parse('content://com.zhang.provider.yourprovider/tablename/100'))) { case 1://match 1, todo somethingbreak; case 2//match 2, todo somethingbreak; default://match nothing, todo somethingbreak;}五、ContentUris

ContentUris 類用于操作 Uri 路徑后面的 ID 部分,它有兩個比較實用的方法:withAppendedId(Uri uri, long id) 和 parseId(Uri uri)。

withAppendedId(Uri uri, long id) 用于為路徑加上 ID 部分:

Uri uri = Uri.parse('content://cn.scu.myprovider/user')//生成后的Uri為:content://cn.scu.myprovider/user/7Uri resultUri = ContentUris.withAppendedId(uri, 7);

parseId(Uri uri) 則從路徑中獲取 ID 部分:

Uri uri = Uri.parse('content://cn.scu.myprovider/user/7')//獲取的結果為:7long personid = ContentUris.parseId(uri);

ContentProvider 主要方法ContentProvider 是一個抽象類,如果我們需要開發自己的內容提供者我們就需要繼承這個類并復寫其方法,需要實現的主要方法如下:

public boolean onCreate():在創建 ContentProvider 時使用 public Cursor query():用于查詢指定 uri 的數據返回一個 Cursor public Uri insert():用于向指定uri的 ContentProvider 中添加數據 public int delete():用于刪除指定 uri 的數據 public int update():用戶更新指定 uri 的數據 public String getType():用于返回指定的 Uri 中的數據 MIME 類型

數據訪問的方法 insert,delete 和 update 可能被多個線程同時調用,此時必須是線程安全的。

如果操作的數據屬于集合類型,那么 MIME 類型字符串應該以 vnd.android.cursor.dir/ 開頭,

要得到所有 tablename 記錄: Uri 為 content://com.wang.provider.myprovider/tablename,那么返回的MIME類型字符串應該為:vnd.android.cursor.dir/table。

如果要操作的數據屬于非集合類型數據,那么 MIME 類型字符串應該以 vnd.android.cursor.item/ 開頭,

要得到 id 為 10 的 tablename 記錄,Uri 為 content://com.wang.provider.myprovider/tablename/10,那么返回的 MIME 類型字符串為:vnd.android.cursor.item/tablename 。

5.1、方法使用示例

使用 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字段值更改新為zhang1ContentValues updateValues = new ContentValues();updateValues.put('name', 'zhang1');Uri updateIdUri = ContentUris.withAppendedId(uri, 2);resolver.update(updateIdUri, updateValues, null, null);// 刪除id為2的記錄,即字段ageUri deleteIdUri = ContentUris.withAppendedId(uri, 2);resolver.delete(deleteIdUri, null, null);5.2、監聽數據變化

如果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。

詳解Android 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);} @Overridepublic 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); }} @Overridepublic 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);} @Overridepublic boolean onCreate() { // TODO Auto-generated method stub mDbHelper = new DBHelper(getContext()); db = mDbHelper.getReadableDatabase();return true;} @Overridepublic 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;} @Overridepublic 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 stubreturn 0; }}

MainActivity.java(ContentResolver操作)

public class MainActivity extends Activity { private ContentResolver mContentResolver = null; private Cursor cursor = null; @Overrideprotected 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' />七、總結 如何通過 ContentProvider 查詢數據? 通過 ContentResolver 進行uri匹配 如何實現自己的ContentProvider? 繼承 ContentProvider,實現對應的方法。在 manifest 中聲明7.1、額外補充:隱式 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');

注意:

content://abc 換成 file://abc 在 7.0 以上的版本(即把 targetSdkVersion 指定成 24 及之上并且在 API>=24 的設備上運行)會出現 FileUriExposedException 異常,google 提供了FileProvider 解決這個異常,使用它可以生成 content://Uri 來替代 file://Uri.

示例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 一定可以成功

以上就是詳解Android ContentProvider的基本原理和使用的詳細內容,更多關于Android ContentProvider的資料請關注好吧啦網其它相關文章!

標簽: Android
相關文章:
主站蜘蛛池模板: MES系统-WMS系统-MES定制开发-制造执行MES解决方案-罗浮云计算 | 北京网络营销推广_百度SEO搜索引擎优化公司_网站排名优化_谷歌SEO - 北京卓立海创信息技术有限公司 | 汽车水泵_汽车水泵厂家-瑞安市骏迪汽车配件有限公司 | 渣土车电机,太阳能跟踪器电机,蜗轮蜗杆减速电机厂家-淄博传强电机 | 丽陂特官网_手机信号屏蔽器_Wifi信号干扰器厂家_学校考场工厂会议室屏蔽仪 | 盘煤仪,盘料仪,盘点仪,堆料测量仪,便携式激光盘煤仪-中科航宇(北京)自动化工程技术有限公司 | 合肥弱电工程_安徽安防工程_智能化工程公司-合肥雷润 | 压滤机滤板_厢式_隔膜_板框压滤机滤板厂家价格型号材质-大凯环保 | 低噪声电流前置放大器-SR570电流前置放大器-深圳市嘉士达精密仪器有限公司 | 股指期货-期货开户-交易手续费佣金加1分-保证金低-期货公司排名靠前-万利信息开户 | 重庆波纹管|重庆钢带管|重庆塑钢管|重庆联进管道有限公司 | 苏商学院官网 - 江苏地区唯一一家企业家自办的前瞻型、实操型商学院 | [品牌官网]贵州遵义双宁口腔连锁_贵州遵义牙科医院哪家好_种植牙_牙齿矫正_原华美口腔 | 车辆定位管理系统_汽车GPS系统_车载北斗系统 - 朗致物联 | 老城街小面官网_正宗重庆小面加盟技术培训_特色面馆加盟|牛肉拉面|招商加盟代理费用多少钱 | 环讯传媒,永康网络公司,永康网站建设,永康小程序开发制作,永康网站制作,武义网页设计,金华地区网站SEO优化推广 - 永康市环讯电子商务有限公司 | 烟雾净化器-滤筒除尘器-防爆除尘器-除尘器厂家-东莞执信环保科技有限公司 | 云南丰泰挖掘机修理厂-挖掘机维修,翻新,再制造的大型企业-云南丰泰工程机械维修有限公司 | 高扬程排污泵_隔膜泵_磁力泵_节能自吸离心水泵厂家-【上海博洋】 | SDG吸附剂,SDG酸气吸附剂,干式酸性气体吸收剂生产厂家,超过20年生产使用经验。 - 富莱尔环保设备公司(原名天津市武清县环保设备厂) | ET3000双钳形接地电阻测试仪_ZSR10A直流_SXJS-IV智能_SX-9000全自动油介质损耗测试仪-上海康登 | 断桥铝破碎机_铝合金破碎机_废铁金属破碎机-河南鑫世昌机械制造有限公司 | 导电银胶_LED封装导电银胶_半导体封装导电胶厂家-上海腾烁 | 数字展示在线_数字展示行业门户网站 | Win10系统下载_32位/64位系统/专业版/纯净版下载 | 天品互联-北京APP开发公司-小程序开发制作-软件开发 | 电动葫芦|环链电动葫芦-北京凌鹰名优起重葫芦 | 煤粉取样器-射油器-便携式等速飞灰取样器-连灵动 | 衢州装饰公司|装潢公司|办公楼装修|排屋装修|别墅装修-衢州佳盛装饰 | 北京成考网-北京成人高考网| 激光内雕_led玻璃_发光玻璃_内雕玻璃_导光玻璃-石家庄明晨三维科技有限公司 激光内雕-内雕玻璃-发光玻璃 | 复合肥,化肥厂,复合肥批发,化肥代理,复合肥品牌-红四方 | 广州昊至泉水上乐园设备有限公司 | 量子管通环-自清洗过滤器-全自动反冲洗过滤器-沼河浸过滤器 | 不锈钢复合板|钛复合板|金属复合板|南钢集团安徽金元素复合材料有限公司-官网 | 硅胶制品-硅橡胶制品-东莞硅胶制品厂家-广东帝博科技有限公司 | 河南砖机首页-全自动液压免烧砖机,小型砌块水泥砖机厂家[十年老厂] | 【灵硕展览集团】展台展会设计_展览会展台搭建_展览展示设计一站式服务公司 | 高效节能电机_伺服主轴电机_铜转子电机_交流感应伺服电机_图片_型号_江苏智马科技有限公司 | 恒温振荡混匀器-微孔板振荡器厂家-多管涡旋混匀器厂家-合肥艾本森(www.17world.net) | 螺旋丝杆升降机-SWL蜗轮-滚珠丝杆升降机厂家-山东明泰传动机械有限公司 |