Android中ContentProvider和Uri详解
来源:互联网 发布:郑钧 私奔知乎 编辑:程序博客网 时间:2024/05/20 22:30
一、使用ContentProvider(内容提供者)共享数据
ContentProvider在android中的作用是对外共享数据,也就是说你可以通过ContentProvider把应用中的数据共享给其他应用访问,其他应用可以通过ContentProvider对你应用中的数据进行添删改查。关于数据共享,以前我们学习过文件操作模式,知道通过指定文件的操作模式为Context.MODE_WORLD_READABLE或Context.MODE_WORLD_WRITEABLE同样也可以对外共享数据。那么,这里为何要使用ContentProvider对外共享数据呢?是这样的,如果采用文件操作模式对外共享数据,数据的访问方式会因数据存储的方式而不同,导致数据的访问方式无法统一,如:采用xml文件对外共享数据,需要进行xml解析才能读取数据;采用sharedpreferences共享数据,需要使用sharedpreferences API读取数据。
使用ContentProvider对外共享数据的好处是统一了数据的访问方式。
当应用需要通过ContentProvider对外共享数据时,第一步需要继承ContentProvider并重写下面方法:
public class PersonContentProvider extends ContentProvider{ public boolean onCreate() public Uri insert(Uri uri, ContentValues values) public int delete(Uri uri, String selection, String[] selectionArgs) public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) public String getType(Uri uri)}
第二步需要在AndroidManifest.xml使用<provider>对该ContentProvider进行配置,为了能让其他应用找到该ContentProvider ,ContentProvider采用了authorities(主机名/域名)对它进行唯一标识,你可以把ContentProvider看作是一个网站(想想,网站也是提供数据者),authorities 就是他的域名:
<manifest.... > <application android:icon="@drawable/icon" android:label="@string/app_name"> <provider android:name=".PersonContentProvider" android:authorities="com.ljq.providers.personprovider"/> </application></manifest>
二、Uri介绍
Uri代表了要操作的数据,Uri主要包含了两部分信息:1》需要操作的ContentProvider ,2》对ContentProvider中的什么数据进行操作,一个Uri由以下几部分组成:
ContentProvider(内容提供者)的scheme已经由Android所规定, scheme为:content://
主机名(或叫Authority)用于唯一标识这个ContentProvider,外部调用者可以根据这个标识来找到它。
路径(path)可以用来表示我们要操作的数据,路径的构建应根据业务而定,如下:
要操作person表中id为10的记录,可以构建这样的路径:/person/10
要操作person表中id为10的记录的name字段, person/10/name
要操作person表中的所有记录,可以构建这样的路径:/person
要操作xxx表中的记录,可以构建这样的路径:/xxx
当然要操作的数据不一定来自数据库,也可以是文件、xml或网络等其他存储方式,如下:
要操作xml文件中person节点下的name节点,可以构建这样的路径:/person/name
如果要把一个字符串转换成Uri,可以使用Uri类中的parse()方法,如下:
Uri uri = Uri.parse("content://com.ljq.provider.personprovider/person")
三、UriMatcher类使用介绍
因为Uri代表了要操作的数据,所以我们经常需要解析Uri,并从Uri中获取数据。Android系统提供了两个用于操作Uri的工具类,分别为UriMatcher和ContentUris 。掌握它们的使用,会便于我们的开发工作。
UriMatcher类用于匹配Uri,它的用法如下:
首先第一步把你需要匹配Uri路径全部给注册上,如下:
//常量UriMatcher.NO_MATCH表示不匹配任何路径的返回码UriMatcher sMatcher = new UriMatcher(UriMatcher.NO_MATCH);//如果match()方法匹配content://com.ljq.provider.personprovider/person路径,返回匹配码为1sMatcher.addURI("com.ljq.provider.personprovider", "person", 1);//添加需要匹配uri,如果匹配就会返回匹配码//如果match()方法匹配content://com.ljq.provider.personprovider/person/230路径,返回匹配码为2sMatcher.addURI("com.ljq.provider.personprovider", "person/#", 2);//#号为通配符switch (sMatcher.match(Uri.parse("content://com.ljq.provider.personprovider/person/10"))) { case 1 break; case 2 break; default://不匹配 break;}注册完需要匹配的Uri后,就可以使用sMatcher.match(uri)方法对输入的Uri进行匹配,如果匹配就返回匹配码,匹配码是调用addURI()方法传入的第三个参数,假设匹配content://com.ljq.provider.personprovider/person路径,返回的匹配码为1
四、ContentUris类使用介绍
ContentUris类用于操作Uri路径后面的ID部分,它有两个比较实用的方法:
withAppendedId(uri, id)用于为路径加上ID部分:
Uri uri = Uri.parse("content://com.ljq.provider.personprovider/person")Uri resultUri = ContentUris.withAppendedId(uri, 10); //生成后的Uri为:content://com.ljq.provider.personprovider/person/10
parseId(uri)方法用于从路径中获取ID部分:
Uri uri = Uri.parse("content://com.ljq.provider.personprovider/person/10")long personid = ContentUris.parseId(uri);//获取的结果为:10
五、使用ContentProvider共享数据
ContentProvider类主要方法的作用:
public boolean onCreate():该方法在ContentProvider创建后就会被调用,Android开机后,ContentProvider在其它应用第一次访问它时才会被创建。
public Uri insert(Uri uri, ContentValues values):该方法用于供外部应用往ContentProvider添加数据。
public int delete(Uri uri, String selection, String[] selectionArgs):该方法用于供外部应用从ContentProvider删除数据。
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs):该方法用于供外部应用更新ContentProvider中的数据。
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder):该方法用于供外部应用从ContentProvider中获取数据。
public String getType(Uri uri):该方法用于返回当前Url所代表数据的MIME类型。
如果操作的数据属于集合类型,那么MIME类型字符串应该以vnd.android.cursor.dir/开头,
例如:要得到所有person记录的Uri为content://com.ljq.provider.personprovider/person,那么返回的MIME类型字符串应该为:"vnd.android.cursor.dir/person"。
如果要操作的数据属于非集合类型数据,那么MIME类型字符串应该以vnd.android.cursor.item/开头,
例如:得到id为10的person记录,Uri为content://com.ljq.provider.personprovider/person/10,那么返回的MIME类型字符串为:"vnd.android.cursor.item/person"。
六、使用ContentResolver操作ContentProvider中的数据
当外部应用需要对ContentProvider中的数据进行添加、删除、修改和查询操作时,可以使用ContentResolver 类来完成,要获取ContentResolver 对象,可以使用Activity提供的getContentResolver()方法。 ContentResolver 类提供了与ContentProvider类相同签名的四个方法:
public Uri insert(Uri uri, ContentValues values):该方法用于往ContentProvider添加数据。
public int delete(Uri uri, String selection, String[] selectionArgs):该方法用于从ContentProvider删除数据。
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs):该方法用于更新ContentProvider中的数据。
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder):该方法用于从ContentProvider中获取数据。
这些方法的第一个参数为Uri,代表要操作的ContentProvider和对其中的什么数据进行操作,
假设给定的是:Uri.parse("content://com.ljq.providers.personprovider/person/10"),那么将会对主机名为com.ljq.providers.personprovider的ContentProvider进行操作,操作的数据为person表中id为10的记录。
使用ContentResolver对ContentProvider中的数据进行添加、删除、修改和查询操作:
ContentResolver resolver = getContentResolver();Uri uri = Uri.parse("content://com.ljq.provider.personprovider/person");//添加一条记录ContentValues values = new ContentValues();values.put("name", "linjiqin");values.put("age", 25);resolver.insert(uri, values); //获取person表中所有记录Cursor cursor = resolver.query(uri, null, null, null, "personid desc");while(cursor.moveToNext()){ Log.i("ContentTest", "personid="+ cursor.getInt(0)+ ",name="+ cursor.getString(1));}//把id为1的记录的name字段值更改新为zhangsanContentValues updateValues = new ContentValues();updateValues.put("name", "zhangsan");Uri updateIdUri = ContentUris.withAppendedId(uri, 2);resolver.update(updateIdUri, updateValues, null, null);//删除id为2的记录Uri deleteIdUri = ContentUris.withAppendedId(uri, 2);resolver.delete(deleteIdUri, null, null);
七、监听ContentProvider中数据的变化
如果ContentProvider的访问者需要知道ContentProvider中的数据发生变化,可以在ContentProvider发生数据变化时调用getContentResolver().notifyChange(uri, null)来通知注册在此URI上的访问者,例子如下:
public class PersonContentProvider extends ContentProvider { public Uri insert(Uri uri, ContentValues values) { db.insert("person", "personid", values); getContext().getContentResolver().notifyChange(uri, null); }}
如果ContentProvider的访问者需要得到数据变化通知,必须使用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) { //此处可以进行相应的业务处理 }}
案例***************************************************************************************************
建立DBOpenHelper类:创建了一个person表
package com.example.administrator.myapplication;import android.content.Context;import android.database.sqlite.SQLiteDatabase;import android.database.sqlite.SQLiteOpenHelper;public class DBOpenHelper extends SQLiteOpenHelper { private static final String DATABASE_NAME = "person.db"; //数据库名称 private static final int DATABASE_VERSION = 1;//数据库版本 public DBOpenHelper(Context context) { super(context, DATABASE_NAME, null, DATABASE_VERSION); } @Override public void onCreate(SQLiteDatabase db) { db.execSQL("CREATE TABLE person (_id integer primary key autoincrement, name varchar(20), age varchar(10))"); } @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { db.execSQL("DROP TABLE IF EXISTS person"); onCreate(db); }}
创建一个PersonService 用于增删改查
package com.example.administrator.myapplication;import android.content.Context;import android.database.Cursor;import android.database.sqlite.SQLiteDatabase;import java.util.ArrayList;import java.util.List;public class PersonService { private DBOpenHelper dbOpenHelper; public PersonService(Context context) { dbOpenHelper=new DBOpenHelper(context); } public void save(Person person){ SQLiteDatabase db=dbOpenHelper.getWritableDatabase(); db.execSQL("insert into person(name,age) values(?,?)",new Object[]{person.getName(),person.getAge()}); } public void delete(Integer _id){ SQLiteDatabase db=dbOpenHelper.getWritableDatabase(); db.execSQL("delete from person where _id=?",new Object[]{_id}); } public Person find(Integer _id){ SQLiteDatabase db=dbOpenHelper.getReadableDatabase(); Cursor cursor=db.rawQuery("select * from person where _id=?", new String[]{_id.toString()}); if(cursor.moveToFirst()){ int id = cursor.getInt(cursor.getColumnIndex("_id")); String name = cursor.getString(cursor.getColumnIndex("name")); String age = cursor.getString(cursor.getColumnIndex("age")); Person person = new Person(); person.set_id(id); person.setName(name); person.setAge(age); return person; } return null; } public List<Person> findAll(){ SQLiteDatabase db=dbOpenHelper.getReadableDatabase(); List<Person> persons = new ArrayList<Person>(); Cursor cursor=db.rawQuery("select * from person", null); while(cursor.moveToNext()){ Person person=new Person(); int id=cursor.getInt(cursor.getColumnIndex("_id")); String name=cursor.getString(cursor.getColumnIndex("name")); String age=cursor.getString(cursor.getColumnIndex("age")); person.set_id(id); person.setName(name); person.setAge(age); persons.add(person); } return persons; }}
创建一个Person 类
package com.example.administrator.myapplication;public class Person { private Integer _id; private String name; private String age; public Integer get_id() { return _id; } public void set_id(Integer _id) { this._id = _id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getAge() { return age; } public void setAge(String age) { this.age = age; }}
数据库已建好,接下来就是要实现ContentProvider来提供统一的访问接口:
package com.example.administrator.myapplication;import android.content.ContentProvider;import android.content.ContentUris;import android.content.ContentValues;import android.content.UriMatcher;import android.database.Cursor;import android.database.sqlite.SQLiteDatabase;import android.net.Uri;public class PersonProvider extends ContentProvider { private DBOpenHelper dbOpenHelper; private static final UriMatcher MATCHER = new UriMatcher( UriMatcher.NO_MATCH); private static final int PERSONS = 1; private static final int PERSON = 2; static { MATCHER.addURI("com.ljg.provider.personprovider", "person", PERSONS); MATCHER.addURI("com.ljg.provider.personprovider", "person/#", PERSON); } @Override public boolean onCreate() { this.dbOpenHelper = new DBOpenHelper(this.getContext()); return false; } public Cursor query(Uri uri, String[] projection, String selection,String[] selectionArgs, String sortOrder) { SQLiteDatabase db = dbOpenHelper.getReadableDatabase(); switch (MATCHER.match(uri)) { case PERSONS: return db.query("person", projection, selection, selectionArgs, null, null, sortOrder); case PERSON: long id = ContentUris.parseId(uri); String where = "_id=" + id; if (selection != null && !"".equals(selection)) { where = selection + " and " + where; } return db.query("person", projection, where, selectionArgs, null, null, sortOrder); default: throw new IllegalArgumentException("Unkwon Uri:" + uri.toString()); } } //返回数据的MIME类型。 @Override public String getType(Uri uri) { // TODO Auto-generated method stub switch (MATCHER.match(uri)) { case PERSONS: return "vnd.android.cursor.dir/person"; case PERSON: return "vnd.android.cursor.item/person"; default: throw new IllegalArgumentException("Unkwon Uri:" + uri.toString()); } } @Override public Uri insert(Uri uri, ContentValues values) { // TODO Auto-generated method stub SQLiteDatabase db = dbOpenHelper.getWritableDatabase(); switch (MATCHER.match(uri)) { case PERSONS: // 特别说一下第二个参数是当name字段为空时,将自动插入一个NULL。 long rowid = db.insert("person", "name", values); Uri insertUri = ContentUris.withAppendedId(uri, rowid);// 得到代表新增记录的Uri this.getContext().getContentResolver().notifyChange(uri, null); return insertUri; default: throw new IllegalArgumentException("Unkwon Uri:" + uri.toString()); } } @Override public int delete(Uri uri, String selection, String[] selectionArgs) { // TODO Auto-generated method stub SQLiteDatabase db = dbOpenHelper.getWritableDatabase(); int count = 0; switch (MATCHER.match(uri)) { case PERSONS: count = db.delete("person", selection, selectionArgs); return count; case PERSON: long id = ContentUris.parseId(uri); String where = "_id=" + id; if (selection != null && !"".equals(selection)) { where = selection + " and " + where; } count = db.delete("person", where, selectionArgs); return count; default: throw new IllegalArgumentException("Unkwon Uri:" + uri.toString()); } } @Override public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { // TODO Auto-generated method stub SQLiteDatabase db = dbOpenHelper.getWritableDatabase(); int count = 0; switch (MATCHER.match(uri)) { case PERSONS: count = db.update("person", values, selection, selectionArgs); return count; case PERSON: long id = ContentUris.parseId(uri); String where = "_id=" + id; if (selection != null && !"".equals(selection)) { where = selection + " and " + where; } count = db.update("person", values, where, selectionArgs); return count; default: throw new IllegalArgumentException("Unkwon Uri:" + uri.toString()); } }}
最后不要忘记在manifest里注册
<provider android:name=".PersonProvider" android:authorities="com.ljg.provider.personprovider" android:enabled="true" android:exported="true" />
让我们再写个项目访问一下,建立ResolverDemo项目:
为了展现效果,我们用了ListView,在res下建立item.xml
<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="horizontal" android:layout_width="fill_parent" android:layout_height="30dp"> <TextView android:layout_width="80dip" android:layout_height="wrap_content" android:text="435" android:id="@+id/id" /> <TextView android:layout_width="100dip" android:layout_height="wrap_content" android:text="liming" android:id="@+id/name" /> <TextView android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="45" android:id="@+id/age" /></LinearLayout>
package com.example.administrator.myapplication;import android.app.Activity;import android.content.ContentResolver;import android.content.ContentValues;import android.database.Cursor;import android.net.Uri;import android.os.Bundle;import android.view.View;import android.widget.AdapterView;import android.widget.Button;import android.widget.ListView;import android.widget.SimpleCursorAdapter;import android.widget.Toast;public class MainActivity extends Activity { private SimpleCursorAdapter adapter; private ListView listView; protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); listView=(ListView) this.findViewById(R.id.listView); ContentResolver contentResolver = getContentResolver(); Uri selectUri = Uri.parse("content://com.ljg.provider.personprovider/person"); Cursor cursor=contentResolver.query(selectUri, null, null, null, null); adapter = new SimpleCursorAdapter(this, R.layout.item, cursor, new String[]{"_id", "name", "age"}, new int[]{R.id.id, R.id.name, R.id.age}); listView.setAdapter(adapter); listView.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { ListView lView = (ListView)parent; Cursor data = (Cursor)lView.getItemAtPosition(position); int _id = data.getInt(data.getColumnIndex("_id")); Toast.makeText(MainActivity.this,_id+"", Toast.LENGTH_SHORT).show(); } }); Button button = (Button) this.findViewById(R.id.insertbutton); button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { ContentResolver contentResolver = getContentResolver(); Uri insertUri = Uri.parse("content://com.ljg.provider.personprovider/person"); ContentValues values = new ContentValues(); values.put("name", "wangkuifeng"); values.put("age", 23); Uri uri = contentResolver.insert(insertUri, values); Toast.makeText(MainActivity.this, "添加完成",Toast.LENGTH_SHORT).show(); } }); }}
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent" android:orientation="vertical" > <Button android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="添加" android:id="@+id/insertbutton" /> <ListView android:layout_width="fill_parent" android:layout_height="wrap_content" android:id="@+id/listView" ></ListView> </LinearLayout>
- Android 中ContentProvider和Uri详解
- Android 中ContentProvider和Uri详解
- Android中ContentProvider和Uri详解
- Android ContentProvider和Uri详解
- Android ContentProvider和Uri详解
- Android ContentProvider和Uri详解
- 【Android】ContentProvider和Uri详解
- Android ContentProvider和Uri详解
- Android ContentProvider和Uri详解
- Android ContentProvider和Uri详解
- Android ContentProvider和Uri详解
- Android——ContentProvider和Uri详解
- android之ContentProvider和Uri详解
- android之ContentProvider和Uri详解
- Android ContentProvider和Uri详解 (绝对全面)
- android之ContentProvider和Uri详解
- Android ContentProvider和Uri详解 (绝对全面)
- Android ContentProvider和Uri详解 (绝对全面)
- PyCharm v2016.3发布,专业版新增多项功能
- 级数求和ACC程序加题解PASCAL
- docker iotop :OSError: Netlink error: No such file or directory
- 修改Struts2的struts.xml配置文件位置
- DevOps的概念和工具学习
- Android中ContentProvider和Uri详解
- Android Volley完全解析(二),使用Volley加载网络图片
- android设备执行shell命令
- Hystrix Clients译 断路器(熔断器)
- 什么时候用@Resource,什么时候用@service
- HFSC Scheduling with Linux
- 声卡 PCM数据格式
- neutron firewall as a sevice
- Android JNI编程(四)——C语言多级指针、数组取值、从控制台输入数组