【Android数据存储】ContentProvider详细介绍(附实例源码)
1.ContentProvider是什么?
ContentProvider——内容提供者。它是一个类,这个类主要是对Android系统中进行共享的数据进行包装,并提供了一组统一的访问接口供其他程序调用。这些被共享的数据,可以使系统自己的也可以使我们个人应用程序中的数据,ContentProvider使用表的形式来组织数据.
2.为什么要有ContentProvider?
在Android中,数据的存储有很多种方式,最常用的就是SQLite和XML文件方式。在不同的应用程序间,其实数据是不能直接被相互访问和操作的,在这种情况下,ContentProvider很好的被用来解决了不同应用程序间数据共享的问题。
其实在Android系统中,已经为我们提供了许多ContentProvider,如:Contacts、Browser、CallLog、 Settings等等。那么,Android系统中提供了这么多的ContentProvider,另外还有我们自己公开的共享数据,我们在写程序的时 候,怎么才能让我们的应用程序知道去哪儿取、如何取这些数据呢?我们自然的会想到URI。
一个Content Provider类实现了一组标准的方法接口,从而能够让其他的应用保存或读取此Content Provider的各种数据类型。
也就是说,一个程序可以通过实现一个Content Provider的抽象接口将自己的数据暴露出去。
外界根本看不到,也不用看到这个应用暴露的数据在应用当中是如何存储的,或者是用数据库存储还是用文件存储,还是通过网上获得,这些一切都不重要,
重要的是外界可以通过这一套标准及统一的接口和程序里的数据打交道,可以读取程序的数据,也可以删除程序的数据
3.URI是什么?
URI(Uniform Resource Identifier)——统一资源定位符,URI在ContentProvider中代表了要操做的数据。
在Android系统中通常的URI格式为:content://com.wirelessqa.content.provider/profile/10
在万维网访问时通常用的URI格式为:http://www.XXXX.com/AAA/123
- content://——schema,这个是Android中已经定义好的一个标准。我个人一直认为这和我们的http://有异曲同工之妙,都是代表的协议。ContentProvider(内容提供者)的scheme已经由Android所规定为:content://
- com.wirelessqa.content.provider——authority(主机名),用于唯一标识这个ContentProvider,外部调用者通过这个authority来找到它。相当于www.XXXX.com, 代表的是我们ContentProvider所在的”域名”,这个”域名”在我们Android中一定要是唯一的,否则系统怎么能知道该找哪一个 Provider呢?所以一般情况下,建议采用完整的包名加类名来标识这个ContentProvider的authority。
- /profile/10——路径,用来标识我们要操作的数据。/profile/10表示的意思是——找到profile中id为10的记录。其实这个相当于/AAA/123。
【扩展阅读】
1.要操作profile表中id为10的记录,可以构建这样的路径:/profile/10
2.要操作profile表中id为10的记录的name字段, profile/10/name
3.要操作profile表中的所有记录,可以构建这样的路径:/profile
4.要操作xxx表中的记录,可以构建这样的路径:/xxx
5.当然要操作的数据不一定来自数据库,也可以是文件、xml或网络等其他存储方式,如下:要操作xml文件中profile节点下的name节点,可以构建这样的路径:/profile/name
6.如果要把一个字符串转换成Uri,可以使用Uri类中的parse()方法,如下:Uri uri = Uri.parse(“content://com.wirelessqa.content.provider/pofile”)
综上所述,content://com.wirelessqa.content.provider/profile/10/User 所代表的URI的意思为:标识com.wirelessqa.content.provider中proifle表中_ID为10的User项。
4.URI常用方法有哪些?
UriMatcher:用于匹配Uri,它的用法如下:
1. 首先把你需要匹配Uri路径全部给注册上,如下:
1
|
//常量UriMatcher.NO_MATCH表示不匹配任何路径的返回码(-1)。
|
2
|
UriMatcher uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
|
3
|
//如果match()方法匹配content://com.wirelessqa.content.provider/profile路径,返回匹配码为1
|
4
|
uriMatcher.addURI(“com.wirelessqa.content.provider”, “profile”, 1 ); //添加需要匹配uri,如果匹配就会返回匹配码
|
5
|
//如果match()方法匹配 content://com.wirelessqa.content.provider/profile/路径,返回匹配码为2
|
6
|
uriMatcher.addURI(“com.wirelessqa.content.provider”, “profile/#”, 2 ); //#号为通配符
|
2.注册完需要匹配的Uri后,就可以使用uriMatcher.match(uri)方法对输入的Uri进行匹配,如果匹配就返回匹配码,匹配码是调用 addURI()方法传入的第三个参数,假设匹配 content://com.wirelessqa.content.provider/profile路径,返回的匹配 码为1。
ContentUris:用于获取Uri路径后面的ID部分,它有两个比较实用的方法:
1
|
withAppendedId(uri, id) // 用于为路径加上ID部分
|
2
|
parseId(uri) //用于从路径中获取ID部分
|
ContentResolver:当外部应用需要对ContentProvider中的数据进行添加、删除、修改和查询操作时,可以使用ContentResolver 类来完成,要获取ContentResolver 对象,可以使用Activity提供的getContentResolver()方法。 ContentResolver使用insert、delete、update、query方法,来操作数据。
5. ContentProvider中公开的几个方法
- public boolean onCreate():该方法在ContentProvider创建后就会被调用,Android系统运行后,ContentProvider只有在被第一次使用它时才会被创建。
- public Uri insert(Uri uri, ContentValues values):外部应用程序通过这个方法向 ContentProvider添加数据。
- uri—— 标识操作数据的URI
- values—— 需要添加数据的键值对
- public int delete(Uri uri, String selection, String[] selectionArgs):外部应用程序通过这个方法从 ContentProvider中删除数据。
- uri——标识操作数据的URI
- selection——构成筛选添加的语句,如”id=1″ 或者 “id=?”
- selectionArgs——对应selection的两种情况可以传入null 或者 new String[]{“1″}
- public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs):外部应用程序通过这个方法对 ContentProvider中的数据进行更新。
- values——对应需要更新的键值对,键为对应共享数据中的字段,值为对应的修改值
- 其余参数同delete方法
- public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder):外部应用程序通过这个方法从ContentProvider中获取数据,并返回一个Cursor对象。
- projection——需要从Contentprovider中选择的字段,如果为空,则返回的Cursor将包含所有的字段。
- sortOrder——默认的排序规则
- 其余参数同delete方法
- public String getType(Uri uri):该方法用于返回当前Url所代表数据的MIME类型。
如果操作的数据属于集合类型,那么MIME类型字符串应该以vnd.android.cursor.dir/开头,
例如:要得到所有person记录的Uri为content://com.wirelessqa.content.provider/profile,那么返回的MIME类型字符串应该为:”vnd.android.cursor.dir/profile”。
如果要操作的数据属于非集合类型数据,那么MIME类型字符串应该以vnd.android.cursor.item/开头,
例如:得到id为10的person记录,Uri为content://com.wirelessqa.content.provider/profile/10,那么返回的MIME类型字符串为:”vnd.android.cursor.item/profile”。
6.实现步骤
想要自己的程序里面的数据能够被其他程序所访问到,有以下步骤:
第一:首先生成一个继承contentprovider的类.
第二:在androidMainfest.xml里面添加一个provider的标签就可以了.
1
|
<provider android:name= "MyProvider" android:authorities= "com.wirelessqa.content.provider" />
|
是不是很简单?其他程序访问的时候只要按以下步骤就可以访问到了:
1
|
Uri uri=Uri.Uri.parse( "content://" +AUTHORY+ "/profile" );
|
AUTHORY其实就是 android:authorities的值.,注意.这里必须一样..否则系统是找不到的.也是就是 String AUTHORY=”content://com.wirelessqa.content.provider”
然后获取一个 ContentResolver mContentResolver=getContentResolver();这样就其他程序就可以反问我们的数据了
ContentResolver对应的几个方法:
- query(Uri, String[], String, String[], String) which returns data to the caller
- insert(Uri, ContentValues) which inserts new data into the content provider
- update(Uri, ContentValues, String, String[]) which updates existing data in the content provider
- delete(Uri, String, String[]) which deletes data from the content provider
其实和contentprovider里面的方法是一样的..他们所对应的数据,最终是会被传到我们在之前程序里面定义的那个contentprovider类的方法,至于你想要在这几个方法里面做什么事,随你
7.实例讲解
AndroidManifest.xml
在AndroidManifest.xml的<application>和</application>之间加入:
1
|
<provider android:name= "MyProvider" android:authorities= "com.wirelessqa.content.provider" />
|
Profile.java
01
|
package com.wirlessqa.content.provider;
|
02
|
03
|
import android.net.Uri;
|
04
|
05
|
/**
|
06
|
* Profile类用于存放各种常量
|
07
|
*
|
08
|
* @author www.wirelessqa.com 2013-2-26 下午11:01:46
|
09
|
*/
|
10
|
public class Profile {
|
11
|
12
|
public static final String TABLE_NAME = "profile"; // 表格名称
|
13
|
14
|
public static final String COLUMN_ID = "_id"; // 列表一,_ID,自动增加
|
15
|
16
|
public static final String COLUMN_NAME = "name"; // 列表二,名称
|
17
|
18
|
public static final String AUTOHORITY = "com.wirlessqa.content.provider";
|
19
|
public static final int ITEM = 1;
|
20
|
public static final int ITEM_ID = 2;
|
21
|
22
|
// 如果操作的数据属于集合类型,那么MIME类型字符串应该以vnd.android.cursor.dir/开头,
|
23
|
public static final String CONTENT_TYPE = "vnd.android.cursor.dir/profile";
|
24
|
// 如果要操作的数据属于非集合类型数据,那么MIME类型字符串应该以vnd.android.cursor.item/开头
|
25
|
public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/profile";
|
26
|
27
|
public static final Uri CONTENT_URI = Uri.parse("content://" + AUTOHORITY + "/profile");
|
28
|
}
|
DBHelper.java
01
|
package com.wirlessqa.content.provider;
|
02
|
03
|
import android.content.Context;
|
04
|
import android.database.SQLException;
|
05
|
import android.database.sqlite.SQLiteDatabase;
|
06
|
import android.database.sqlite.SQLiteOpenHelper;
|
07
|
08
|
/**
|
09
|
* 定义一个数据库类
|
10
|
*
|
11
|
* @author www.wirelessqa.com 2013-2-26 下午11:01:46
|
12
|
*/
|
13
|
public class DBHelper extends SQLiteOpenHelper {
|
14
|
15
|
private static final String DATABASE_NAME = "wirelessqa.db" ; // 数据库名
|
16
|
17
|
private static final int DATABASE_VERSION = 1 ; // 版本号
|
18
|
19
|
public DBHelper(Context context){
|
20
|
super (context, DATABASE_NAME, null , DATABASE_VERSION);
|
21
|
}
|
22
|
23
|
@Override
|
24
|
public void onCreate(SQLiteDatabase db) throws SQLException {
|
25
|
// 创建的数据表中必须含有"_id"这个字段,这个字段是自增长的,插入的时候不用管这个字段,数据库会自己递增地加上
|
26
|
db.execSQL( "CREATE TABLE IF NOT EXISTS " + Profile.TABLE_NAME + "(" + Profile.COLUMN_ID
|
27
|
+ " INTEGER PRIMARY KEY AUTOINCREMENT," + Profile.COLUMN_NAME + " VARCHAR NOT NULL);" );
|
28
|
}
|
29
|
30
|
@Override
|
31
|
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) throws SQLException {
|
32
|
// 删除并创建表格
|
33
|
db.execSQL( "DROP TABLE IF EXISTS " + Profile.TABLE_NAME + ";" );
|
34
|
onCreate(db);
|
35
|
}
|
36
|
}
|
MyProvider.java
001
|
package com.wirlessqa.content.provider;
|
002
|
003
|
import android.content.ContentProvider;
|
004
|
import android.content.ContentUris;
|
005
|
import android.content.ContentValues;
|
006
|
import android.content.UriMatcher;
|
007
|
import android.database.Cursor;
|
008
|
import android.database.SQLException;
|
009
|
import android.database.sqlite.SQLiteDatabase;
|
010
|
import android.net.Uri;
|
011
|
012
|
/**
|
013
|
* contentprovider的调用者有可能是Activity,Service,Application这3种context,被谁调用,getContext就是谁
|
014
|
* @author www.wirelessqa.com 2013-2-26 下午11:01:46
|
015
|
*/
|
016
|
public class MyProvider extends ContentProvider {
|
017
|
018
|
DBHelper mDbHelper = null ;
|
019
|
SQLiteDatabase db = null ;
|
020
|
021
|
private static final UriMatcher mMatcher;
|
022
|
// 1.第一步把你需要匹配Uri路径全部给注册上
|
023
|
static {
|
024
|
// UriMatcher:用于匹配Uri
|
025
|
// 常量UriMatcher.NO_MATCH表示不匹配任何路径的返回码(-1)。
|
026
|
mMatcher = new UriMatcher(UriMatcher.NO_MATCH);
|
027
|
// 如果match()方法匹配content://com.wirlessqa.content.provider/profile路径,返回匹配码为1
|
028
|
mMatcher.addURI(Profile.AUTOHORITY, Profile.TABLE_NAME, Profile.ITEM); // 添加需要匹配uri,如果匹配就会返回匹配码
|
029
|
// 如果match()方法匹配 content://com.wirlessqa.content.provider/profile/#路径,返回匹配码为2
|
030
|
mMatcher.addURI(Profile.AUTOHORITY, Profile.TABLE_NAME + "/#" , Profile.ITEM_ID); // #号为通配符
|
031
|
032
|
// 注册完需要匹配的Uri后,就可以使用mMatcher.match(uri)方法对输入的Uri进行匹配,如果匹配就返回匹配码,
|
033
|
// 匹配码是调用addURI()方法传入的第三个参数,假设匹配content://com.wirelessqa.content.provider/profile路径,返回的匹配码为1
|
034
|
}
|
035
|
036
|
// onCreate()方法在ContentProvider创建后就会被调用,Android系统运行后,ContentProvider只有在被第一次使用它时才会被创建。
|
037
|
@Override
|
038
|
public boolean onCreate() {
|
039
|
mDbHelper = new DBHelper(getContext());
|
040
|
041
|
db = mDbHelper.getReadableDatabase();
|
042
|
043
|
return true ;
|
044
|
}
|
045
|
046
|
047
|
@Override
|
048
|
public int delete(Uri uri, String selection, String[] selectionArgs) {
|
049
|
long rowId;
|
050
|
if (mMatcher.match(uri) != Profile.ITEM) {
|
051
|
throw new IllegalArgumentException( "Unknown URI" + uri);
|
052
|
}
|
053
|
rowId = db.delete(Profile.TABLE_NAME, selection, selectionArgs);
|
054
|
// if(rowId>0){
|
055
|
// Uri noteUri = ContentUris.withAppendedId(Profile.CONTENT_URI, rowId);
|
056
|
// getContext().getContentResolver().notifyChange(noteUri, null);
|
057
|
// }
|
058
|
return 0 ;
|
059
|
}
|
060
|
061
|
@Override
|
062
|
public String getType(Uri uri) {
|
063
|
switch (mMatcher.match(uri)) {
|
064
|
case Profile.ITEM:
|
065
|
return Profile.CONTENT_TYPE;
|
066
|
case Profile.ITEM_ID:
|
067
|
return Profile.CONTENT_ITEM_TYPE;
|
068
|
default :
|
069
|
throw new IllegalArgumentException( "Unknown URI" + uri);
|
070
|
}
|
071
|
}
|
072
|
073
|
// 外部应用程序通过这个方法向 ContentProvider添加数据。
|
074
|
@Override
|
075
|
public Uri insert(Uri uri, ContentValues values) {
|
076
|
long rowId;
|
077
|
// mMatcher.match(uri)对输入的Uri进行匹配,如果匹配就返回匹配码
|
078
|
if (mMatcher.match(uri) != Profile.ITEM) {
|
079
|
throw new IllegalArgumentException( "Unknown URI" + uri);
|
080
|
}
|
081
|
rowId = db.insert(Profile.TABLE_NAME, null , values); //向数据库里插入数据
|
082
|
if (rowId > 0 ) {
|
083
|
// ContentUris.withAoppendedId 用于为路径加上ID部分
|
084
|
Uri noteUri = ContentUris.withAppendedId(Profile.CONTENT_URI, rowId);
|
085
|
// 当外部应用需要对ContentProvider中的数据进行添加、删除、修改和查询操作时,可以使用ContentResolver 类来完成
|
086
|
//ContentResolver是属于context的,通过getContentResolver获取
|
087
|
//ContentResolver可以通过registerContentObserver注册观察者(观察者是ContentObserver 的派生类)
|
088
|
//一旦ContentProvider操作的数据变化后,调用ContentResolver的notifyChange方法即可通知到观察者(回调观察者的onChange方法)
|
089
|
//注册观察者不是必须的,所有notifyChange不是必须调用的
|
090
|
getContext().getContentResolver().notifyChange(noteUri, null );
|
091
|
return noteUri;
|
092
|
}
|
093
|
094
|
throw new SQLException( "Failed to insert row into " + uri);
|
095
|
}
|
096
|
097
|
@Override
|
098
|
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
|
099
|
Cursor c = null ;
|
100
|
switch (mMatcher.match(uri)) {
|
101
|
case Profile.ITEM:
|
102
|
c = db.query(Profile.TABLE_NAME, projection, selection, selectionArgs, null , null , sortOrder);
|
103
|
break ;
|
104
|
case Profile.ITEM_ID:
|
105
|
c = db.query(Profile.TABLE_NAME, projection, Profile.COLUMN_ID + "=" + uri.getLastPathSegment(),
|
106
|
selectionArgs, null , null , sortOrder);
|
107
|
break ;
|
108
|
default :
|
109
|
throw new IllegalArgumentException( "Unknown URI" + uri);
|
110
|
}
|
111
|
//从而在ContentService中注册contentservice的观察者,这个观察者是cursor的内部成员(cursor是一个接口,此处真正的cursor是sqlitecursor)
|
112
|
//这样每个查询返回的cursor都能在contentprovider对应数据改变时得到通知,因为这些cursor都有一个成员注册成了contentservice的观察者
|
113
|
c.setNotificationUri(getContext().getContentResolver(), uri);
|
114
|
return c;
|
115
|
}
|
116
|
117
|
@Override
|
118
|
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
|
119
|
// TODO Auto-generated method stub
|
120
|
return 0 ;
|
121
|
}
|
122
|
123
|
}
|
定义ContentProvider的子类
1. contentprovider的调用者有可能是Activity,Service,Application这3种context,被谁调用getContext就是谁
2. ContentResolver是属于context的,通过getContentResolver获取,ContentResolver可以通过registerContentObserver注册观察者(观察者是ContentObserver 的派生类),一旦ContentProvider操作的数据变化后,调用ContentResolver的notifyChange方法即可通知到观察者(回调观察者的onChange方法),注册观察者不是必须的,所有notifyChange不是必须调用的
3. ContentValues 一次只能放入一行数据(可以使多个字段,即多个名值对)
4. onCreate只在ContentProvider第一次被调用的时候调用,多次调用共享的是一个ContentProvider
5. Cursor即数据库查询结果的操作游标,用户随机访问查询结果,获取查询结果的数目等
6. 在Content Resolver中有几个需要注意的接口:
notifyChange (Uri uri, ContentObserver observer, boolean syncToNetwork);
registerContentObserver (Uri uri, boolean notifyForDescendents, ContentObserver observer);
unregisterContentObserver (ContentObserver observer);
7.在Cursor中也有几个类似的接口:
setNotificationUri (ContentResolver cr, Uri uri);
registerContentObserver (ContentObserver observer);
unregisterContentObserver (ContentObserver observer);
8.要在query中调用
setNotificationUri(ContentResolver cr, Uri notifyUri)从而在ContentService中注册contentservice的观察者,这个观察者是cursor的内部成员(cursor是一个接口,此处真正的cursor是sqlitecursor),这样每个查询返回的cursor都能在contentprovider对应数据改变时得到通知,因为这些cursor都有一个成员注册成了contentservice的观察者
那么数据改变时,是怎么通知这些cursor的观察者成员呢?
这就需要在delete,update,insert这些改变数据的方法中调用contentresolver的notifychange方法,这个notifychange实际调用的是contentservice的notifychange,在这个notifychange方法里,contentservice查找所有在其中注册的观察者,找出对这次更新数据感兴趣的观察者(通过uri),然后通知它们数据改变
contentservice有很多观察者,它是系统服务,管理系统中所有contentprovider,通过uri匹配查找观察者,通知符合要求的观察者(实际就是通知对应的cursor)
cursor得到通知以后做些什么呢?
事实上,cursor也可以有很多观察者,因为一个查询出来的结果集可能会被多个地方使用(比如多个listview使用一个cursor),cursor对应的数据改变的时候,它也会通知到所有关注它的观察者(调用它们的onchange)
那么,cursor的观察者是怎么注册进去的呢?
是通过cursor的registerContentObserver这个方法注册进去的
以下例作为解析,在simplecursoradapter(继承自cursoradapter)的构造函数中,调用父类cursoradapter的构造函数,在这个构造函数里,
调用了cursor的registerContentObserver,把一个继承自contentobserver的成员对象作为观察者注册进了cursor的观察者里。这样cursor变化了,就会通知simplecursoradapter,simplecursoradapter里就可以重新查询结果并显示在listview中
以上其实就是两个观察者模式,cursor观察contentservice,同时cursor又被cursoradapter观察(都是通过其成员变量观察,不是直接观察),我们也可以通过contentservice和cursoradapter提供的接口注册我们自己的观察者,也就是说contentservice的观察者可以不是cursor,cursor的观察者可以不是cursoradapter
MainActivity.java
01
|
package com.wirlessqa.content.provider;
|
02
|
03
|
import android.app.ListActivity;
|
04
|
import android.content.ContentResolver;
|
05
|
import android.content.ContentValues;
|
06
|
import android.database.Cursor;
|
07
|
import android.os.Bundle;
|
08
|
import android.widget.SimpleCursorAdapter;
|
09
|
10
|
/**
|
11
|
* @author www.wirelessqa.com 2013-2-26 下午11:00:29
|
12
|
*/
|
13
|
public class MainActivity extends ListActivity {
|
14
|
15
|
private SimpleCursorAdapter adapter = null ;
|
16
|
private Cursor mCursor = null ;
|
17
|
private ContentResolver mContentResolver = null ;
|
18
|
19
|
@Override
|
20
|
public void onCreate(Bundle savedInstanceState) {
|
21
|
super .onCreate(savedInstanceState);
|
22
|
initData();
|
23
|
initAdapter();
|
24
|
}
|
25
|
26
|
public void initData() {
|
27
|
mContentResolver = getContentResolver();
|
28
|
// 删除一条记录可以用下面的方法
|
29
|
// String where = "_id = '1'";
|
30
|
// mContentResolver.delete(Profile.CONTENT_URI, where, null);
|
31
|
// 填充数据
|
32
|
for ( int i = 0 ; i < 20 ; i++) {
|
33
|
// ContentValues 和HashTable类似都是一种存储的机制 但是两者最大的区别就在于
|
34
|
// contenvalues只能存储基本类型的数据,像string,int之类的,不能存储对象这种东西
|
35
|
ContentValues values = new ContentValues();
|
36
|
values.put(Profile.COLUMN_NAME, i + " 网址:www.wirelessqa.com" );
|
37
|
// 通过ContentResolver来向数据库插入数据
|
38
|
mContentResolver.insert(Profile.CONTENT_URI, values);
|
39
|
}
|
40
|
}
|
41
|
42
|
public void initAdapter() {
|
43
|
// 查询表格,并获得Cursor
|
44
|
// 查询全部数据
|
45
|
mCursor = mContentResolver.query(Profile.CONTENT_URI, new String[] { Profile.COLUMN_ID, Profile.COLUMN_NAME },
|
46
|
null , null , null );
|
47
|
48
|
// 查询部分数据
|
49
|
// String selection = Profile.COLUMN_ID + " LIKE '%1'";
|
50
|
// mCursor = mContentResolver.query(Profile.CONTENT_URI, new String[]{Profile.COLUMN_ID,Profile.COLUMN_NAME},
|
51
|
// selection, null, null);
|
52
|
53
|
// 查询一个数据
|
54
|
// Uri uri = ContentUris.withAppendedId(Profile.CONTENT_URI, 50);
|
55
|
// mCursor = mContentResolver.query(uri, new String[]{Profile.COLUMN_ID,Profile.COLUMN_NAME}, null, null, null);
|
56
|
57
|
startManagingCursor(mCursor);
|
58
|
59
|
// 设置adapter
|
60
|
adapter = new SimpleCursorAdapter( this , android.R.layout.simple_list_item_2, mCursor, new String[] {
|
61
|
Profile.COLUMN_ID, Profile.COLUMN_NAME }, new int [] { android.R.id.text1, android.R.id.text2 });
|
62
|
setListAdapter(adapter);
|
63
|
}
|
64
|
65
|
}
|
此文参考多份网络上的文章
源码下载:http://download.csdn.net/detail/wirelessqa/5091983
本文链接:【Android数据存储】ContentProvider详细介绍(附实例源码)
转载声明:本站文章若无特别说明,皆为原创,转载请注明来源:WirelessQA,谢谢!^^
【Android数据存储】ContentProvider详细介绍(附实例源码)相关推荐
- Android开发应用案例——简易计算器(附完整源码)
Android开发-AS学习(一) Android开发-AS学习(二) 使用android studio开发简易计算器app(完整源码可在博主资源中自行下载) 最终效果: 开发步骤: 创建一个名为ca ...
- redis详细介绍附实例代码--看一篇就够了
自己开发了一个股票智能分析软件,功能很强大,需要的点击下面的链接获取: https://www.cnblogs.com/bclshuai/p/11380657.html redis介绍详解附实例代码- ...
- 【Kotlin】Android-使用WebDAV协议云存储文件(详细)—附demo源码
比起云服务器.云数据库.云存储等等方式将文件存储至云端,网盘的WebDAV协议对新手就友好的多,不仅仅有免部署.使用方式简单等等原因,更重要是免费,国内首推坚果云网盘! 三个注意点: 使用第三方库Sa ...
- ((ios开发学习笔记九)) Simple TableView 实现(附 实例源码)
实现效果: 实现过程: Step One 创建单个窗体项目 Step Two 创建control 接口 Step Three 创建窗体和关联关系 Step four 实现table view 的接口 ...
- OpenCV自适应阈值化函数adaptiveThreshold详解,并附实例源码
图像处理开发需求.图像处理接私活挣零花钱,请加微信/QQ 2487872782 图像处理开发资料.图像处理技术交流请加QQ群,群号 271891601 2016-6-14日:又发现一种阈值分割法,最大 ...
- 【android-tips】android程序执行adb shell命令(实例源码)
(转载请注明出处:http://blog.csdn.net/buptgshengod) package net.gimite.nativeexe;import java.io.BufferedRead ...
- 详解numpy中的array(附实例源码)
Numpy定义 NumPy(Numerical Python) 是 Python 语言的一个扩展程序库,支持大量的维度数组与矩阵运算,此外也针对数组运算提供大量的数学函数库.NumPy 通常与 Sci ...
- python爬取大众点评网商家信息以及评价,并将数据存储到excel表中(源码及注释)
import requests from bs4 import BeautifulSoup import traceback # 异常处理 import xlwt # 写入xls表 # Cookie记 ...
- python大众点评网实训报告中的参考文献_python爬取大众点评网商家信息以及评价,并将数据存储到excel表中(源码及注释)...
import requests from bs4 import BeautifulSoup import traceback # 异常处理 import xlwt # 写入xls表 # Cookie记 ...
最新文章
- 每一次宕机都是新的开始
- 5.QT5中的connect的实现
- 计算机二级c语言109套,2016年9月计算机二级C语言操作题109套讲述.docx
- Altium Desiger18 打印 丝印简单的方法
- [css] css中的选择器、属性、属性值区分大小写吗?
- 蓝桥杯 2017 国赛B组C/C++【对局匹配】
- Linux Ubuntu下Jupyter Notebook的安装
- Hive 之 分析窗口函数
- P2525 Uim的情人节礼物·其之壱(入门,数学)
- UCI数据集中文介绍:Waveform Database Generator (Version 1) Data Set
- mysql原生态查询java_java使用原生MySQL实现数据的增删改查
- 看大神如何用python爬虫爬取京东商品评论
- python 按规则拆分文件_python实现按行分割文件
- 新手小白建议收藏,美女剪30条视频,在西瓜头条赚了3000元
- TextView 字体中间加 横划线
- [极致用户体验] 微信设置大字号后,iOS加载网页时闪动怎么办?
- 缠中说禅108课》87:逗庄家玩的一些杂史 4
- SecureCRT快速连接服务器
- Unity Shader Graph 制作Emission发光效果
- 计算机要大牛 掌握的方法【上】写的很全面,收藏