ContentProvider,内容提供者
官网结构图:
作为四大组件之一的ContentProvider,主要是用于应用间数据共享使用的。
ContentProvider把应用的数据封装起来,然后提供一个对外访问的ContentResolver接口。
假如只是本应用使用的数据,直接通过SQLiteDatabase操作数据库。
主要原理:一个Uri对象通过ContentResolver请求该应用的数据,ContentProvider解析该Uri并返回给需要的数据。
实现自定义的ContentProvider,需要继承抽象类android.content.ContentProvider,并实现5个方法:
onCreate() 初始化provider,返回一个布尔值,创建成功返回一个true
query(Uri,String[],String,String[],String) 从ContentProvider中查询数据 返回一个Cursor
insert(Uri,ContentValues) 通过ContentProvider插入新的数据 返回一个Uri
update(Uri,ContentValues,String,String[]) 通过ContentProvider更新数据 返回一个int
delete(Uri,String,String[]) 通过ContentProvider删除数据 返回一个int
getType(Uri) 根据传入的Uri,返回相应的MIME类型。 返回一个String
Uri类:
一个Uri有三部分组成:Scheme+Authority+Path
1.scheme, contentprovider为content://
2.Authority,一般使用应用的包名,如com.example.appone或者也可以这样com.example.appone.provider
3.Path,即提供对外访问的table,如访问表table,写成/table
上面的Uri可以写成这样:content://com.example.appone.provider/table
一个字符串通过Uri类中的parse()方法可以转换为Uri对象,如下:
1 Uri uri=Uri.parse("content://com.example.appone.provider/table");
UriMatcher类,ContentUris类和contentResolver类
UriMather类用于匹配uri,用法如下:
1 private static final int PEOPLE = 1; 2 private static final int PEOPLE_ID = 2; 3 private static final int PEOPLE_PHONES = 3; 4 private static final int PEOPLE_PHONES_ID = 4; 5 private static final int PEOPLE_CONTACTMETHODS = 7; 6 private static final int PEOPLE_CONTACTMETHODS_ID = 8; 7 8 private static final int DELETED_PEOPLE = 20; 9 10 private static final int PHONES = 9; 11 private static final int PHONES_ID = 10; 12 private static final int PHONES_FILTER = 14; 13 14 private static final int CONTACTMETHODS = 18; 15 private static final int CONTACTMETHODS_ID = 19; 16 17 private static final int CALLS = 11; 18 private static final int CALLS_ID = 12; 19 private static final int CALLS_FILTER = 15; 20 21 private static final UriMatcher sURIMatcher = new UriMatcher(UriMatcher.NO_MATCH); //常量UriMatcher.NO_MATCH表示不匹配任何路径的返回码(-1) 22 23 static 24 { 25 sURIMatcher.addURI("contacts", "people", PEOPLE); //如果match()方法匹配,返回匹配码为1 26 sURIMatcher.addURI("contacts", "people/#", PEOPLE_ID); //如果match()方法匹配,返回匹配码为2 27 sURIMatcher.addURI("contacts", "people/#/phones", PEOPLE_PHONES); 28 sURIMatcher.addURI("contacts", "people/#/phones/#", PEOPLE_PHONES_ID); 29 sURIMatcher.addURI("contacts", "people/#/contact_methods", PEOPLE_CONTACTMETHODS); 30 sURIMatcher.addURI("contacts", "people/#/contact_methods/#", PEOPLE_CONTACTMETHODS_ID); 31 sURIMatcher.addURI("contacts", "deleted_people", DELETED_PEOPLE); 32 sURIMatcher.addURI("contacts", "phones", PHONES); 33 sURIMatcher.addURI("contacts", "phones/filter/*", PHONES_FILTER); 34 sURIMatcher.addURI("contacts", "phones/#", PHONES_ID); 35 sURIMatcher.addURI("contacts", "contact_methods", CONTACTMETHODS); 36 sURIMatcher.addURI("contacts", "contact_methods/#", CONTACTMETHODS_ID); 37 sURIMatcher.addURI("call_log", "calls", CALLS); 38 sURIMatcher.addURI("call_log", "calls/filter/*", CALLS_FILTER); 39 sURIMatcher.addURI("call_log", "calls/#", CALLS_ID); 40 }
从API:18开始,路径可以用一个斜线开始。 例如:
1 sURIMatcher.addURI("contacts", "/people", PEOPLE);
注册完需要匹配的Uri后,在getType()方法中可以使用sURIMatcher.match(uri)方法对输入的Uri进行匹配,如果匹配成功就返回匹配码,匹配码是调用addURI()方法传入的第三个参数。
1 public String getType(Uri url) 2 { 3 int match = sURIMatcher.match(url); 4 switch (match) 5 { 6 case PEOPLE: 7 return "vnd.android.cursor.dir/person"; 8 case PEOPLE_ID: 9 return "vnd.android.cursor.item/person"; 10 ... snip ... 11 return "vnd.android.cursor.dir/snail-mail"; 12 case PEOPLE_ADDRESS_ID: 13 return "vnd.android.cursor.item/snail-mail"; 14 default: 15 return null; 16 } 17 }
ContentUris类,用于获取Uri路径后面的ID部分,它有两个比较实用的方法:
withAppendedId(uri, id)用于为路径加上ID部分
1 Uri uri = Uri.parse("content://com.ljq.provider.personprovider/person") 2 Uri resultUri = ContentUris.withAppendedId(uri, 10); 3 //生成后的Uri为:content://com.ljq.provider.personprovider/person/10
parseId(uri)方法用于从路径中获取ID部分
1 Uri uri = Uri.parse("content://com.ljq.provider.personprovider/person/10") 2 long personid = ContentUris.parseId(uri);//获取的结果为:10
ContentResolver类,当外部应用需要对ContentProvider的数据库进行CRUD时,可以使用ContentResolver类来完成。获取ContentResolver对象,可以通过Content的getContentResolver()方法获取到。ContentResolver使用insert()、delete()、update()、query()方法,来操作数据。
实现了一个Demo,代码如下:
1.工具类:Xin类
1 package com.example.appone; 2 3 public class Xin { 4 5 public static final String DBNAME = "xinonlinedb"; 6 public static final String TNAME = "xinonline"; 7 public static final int VERSION = 3; 8 9 public static String TID = "tid"; 10 public static final String EMAIL = "email"; 11 public static final String USERNAME = "username"; 12 public static final String DATE = "date"; 13 public static final String SEX = "sex"; 14 15 public static final String AUTOHORITY = "com.example.appone.provider"; 16 public static final int ITEM = 1; 17 public static final int ITEM_ID = 2; 18 19 public static final String CONTENT_TYPE = "vnd.android.cursor.dir"; 20 public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item"; 21 22 }
2.创建数据库:DBlite类
1 package com.example.appone; 2 3 import android.content.Context; 4 import android.database.sqlite.SQLiteDatabase; 5 import android.database.sqlite.SQLiteDatabase.CursorFactory; 6 import android.database.sqlite.SQLiteOpenHelper; 7 8 public class DBlite extends SQLiteOpenHelper { 9 10 public DBlite(Context context, String name, CursorFactory factory, 11 int version) { 12 super(context, name, factory, version); 13 14 } 15 16 @Override 17 public void onCreate(SQLiteDatabase db) { 18 db.execSQL("CREATE TABLE " + Xin.TNAME + "(" + Xin.TID 19 + " integer primary key autoincrement not null," + Xin.EMAIL 20 + " text not null, " + Xin.USERNAME + " text not null," 21 + Xin.DATE + " text not null, " + Xin.SEX + " text not null);"); 22 } 23 24 @Override 25 public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { 26 27 } 28 29 }
3.创建自定义的ContentProvider,对数据库进行包装:MyProvider类
1 package com.example.appone; 2 3 import android.content.ContentProvider; 4 import android.content.ContentValues; 5 import android.content.UriMatcher; 6 import android.database.Cursor; 7 import android.database.sqlite.SQLiteDatabase; 8 import android.net.Uri; 9 10 public class MyProvider extends ContentProvider { 11 12 private DBlite dBlite; 13 private SQLiteDatabase db; 14 private Cursor cursor; 15 16 private static final UriMatcher sMatcher; 17 static { 18 sMatcher = new UriMatcher(UriMatcher.NO_MATCH); 19 sMatcher.addURI(Xin.AUTOHORITY, Xin.TNAME, Xin.ITEM); 20 sMatcher.addURI(Xin.AUTOHORITY, Xin.TNAME + "/#", Xin.ITEM_ID); 21 22 } 23 24 @Override 25 public boolean onCreate() { 26 dBlite = new DBlite(getContext(), Xin.DBNAME, null, Xin.VERSION); 27 return true; 28 29 } 30 31 @Override 32 public Cursor query(Uri uri, String[] projection, String selection, 33 String[] selectionArgs, String sortOrder) { 34 db = dBlite.getWritableDatabase(); 35 switch (sMatcher.match(uri)) { 36 case Xin.ITEM: 37 cursor = db.query(Xin.TNAME, projection, selection, selectionArgs, 38 null, null, sortOrder); 39 break; 40 case Xin.ITEM_ID: 41 String id = uri.getPathSegments().get(1); 42 cursor = db.query(Xin.TNAME, projection, "tid=?", 43 new String[] { id }, null, null, sortOrder); 44 break; 45 46 default: 47 break; 48 } 49 return cursor; 50 } 51 52 @Override 53 public Uri insert(Uri uri, ContentValues values) { 54 db = dBlite.getWritableDatabase(); 55 long returnId = db.insert(Xin.TNAME, null, values); 56 Uri uriReturn = Uri.parse("content://" + Xin.AUTOHORITY + "/" 57 + Xin.TNAME + "/" + returnId); 58 59 return uriReturn; 60 } 61 62 @Override 63 public int delete(Uri uri, String selection, String[] selectionArgs) { 64 db = dBlite.getWritableDatabase(); 65 int deleteId = 0; 66 switch (sMatcher.match(uri)) { 67 case Xin.ITEM: 68 deleteId = db.delete(Xin.TNAME, selection, selectionArgs); 69 70 break; 71 case Xin.ITEM_ID: 72 String id = uri.getPathSegments().get(1); 73 deleteId = db.delete(Xin.TNAME, "tid=?", new String[] { id }); 74 75 default: 76 break; 77 } 78 return deleteId; 79 } 80 81 @Override 82 public int update(Uri uri, ContentValues values, String selection, 83 String[] selectionArgs) { 84 db = dBlite.getWritableDatabase(); 85 int updateId = 0; 86 switch (sMatcher.match(uri)) { 87 case Xin.ITEM: 88 updateId = db.update(Xin.TNAME, values, selection, selectionArgs); 89 break; 90 case Xin.ITEM_ID: 91 String id = uri.getPathSegments().get(1); 92 updateId = db.update(Xin.TNAME, values, "tid= ?", 93 new String[] { id }); 94 break; 95 96 default: 97 break; 98 } 99 return updateId; 100 } 101 102 @Override 103 public String getType(Uri uri) { 104 switch (sMatcher.match(uri)) { 105 case Xin.ITEM: 106 return Xin.CONTENT_TYPE + "/vnd." + Xin.AUTOHORITY + Xin.TNAME; 107 case Xin.ITEM_ID: 108 return Xin.CONTENT_ITEM_TYPE + "/vnd." + Xin.AUTOHORITY + Xin.TNAME; 109 110 } 111 return null; 112 } 113 114 }
4.在manifest文件中对MyProvider进行注册
1 <?xml version="1.0" encoding="utf-8"?> 2 <manifest xmlns:android="http://schemas.android.com/apk/res/android" 3 package="com.example.appone" 4 android:versionCode="1" 5 android:versionName="1.0" > 6 7 <uses-sdk 8 android:minSdkVersion="14" 9 android:targetSdkVersion="21" /> 10 11 <application 12 android:allowBackup="true" 13 android:icon="@drawable/ic_launcher" 14 android:label="@string/app_name" 15 android:theme="@style/AppTheme" > 16 <activity 17 android:name=".MainActivity" 18 android:label="@string/app_name" > 19 <intent-filter> 20 <action android:name="android.intent.action.MAIN" /> 21 22 <category android:name="android.intent.category.LAUNCHER" /> 23 </intent-filter> 24 </activity> 25 26 <provider 27 android:name="com.example.appone.MyProvider" 28 android:authorities="com.example.appone.provider" 29 android:exported="true" > 30 </provider> 31 </application> 32 33 </manifest>
5.在MainActivity中使用ContentProvider进行CRUD
1 package com.example.appone; 2 3 import android.app.Activity; 4 import android.content.ContentValues; 5 import android.database.Cursor; 6 import android.net.Uri; 7 import android.os.Bundle; 8 import android.util.Log; 9 import android.view.View; 10 import android.view.View.OnClickListener; 11 import android.view.Window; 12 13 public class MainActivity extends Activity implements OnClickListener { 14 15 private String newid; 16 17 @Override 18 protected void onCreate(Bundle savedInstanceState) { 19 super.onCreate(savedInstanceState); 20 requestWindowFeature(Window.FEATURE_NO_TITLE); 21 setContentView(R.layout.activity_contentprovider); 22 23 findViewById(R.id.contentProvider_query).setOnClickListener(this); 24 findViewById(R.id.contentProvider_insert).setOnClickListener(this); 25 findViewById(R.id.contentProvider_update).setOnClickListener(this); 26 findViewById(R.id.contentProvider_delete).setOnClickListener(this); 27 } 28 29 @Override 30 public void onClick(View v) { 31 Uri uriItem = Uri.parse("content://"+Xin.AUTOHORITY+"/"+Xin.TNAME); 32 Uri uriId = Uri.parse("content://"+Xin.AUTOHORITY+"/"+Xin.TNAME+"/"+newid); 33 switch (v.getId()) { 34 case R.id.contentProvider_query: 35 Cursor cursor=this.getContentResolver().query(uriItem, null, null, null, null); 36 if (cursor!=null) { 37 while (cursor.moveToNext()) { 38 String userName=cursor.getString(cursor.getColumnIndex(Xin.USERNAME)); 39 String sex = cursor.getString(cursor.getColumnIndex(Xin.SEX)); 40 String email = cursor.getString(cursor.getColumnIndex(Xin.EMAIL)); 41 String date = cursor.getString(cursor.getColumnIndex(Xin.DATE)); 42 Log.e("appone", "获取到的信息为:"+userName+email+date+sex); 43 } 44 cursor.close(); 45 } 46 break; 47 48 case R.id.contentProvider_insert: 49 ContentValues values = new ContentValues(); 50 values.put(Xin.EMAIL, "1234@12.com"); 51 values.put(Xin.USERNAME, "张三"); 52 values.put(Xin.DATE, "2015"); 53 values.put(Xin.SEX, "男"); 54 Uri newUri = this.getContentResolver().insert(uriItem,values); 55 newid = newUri.getPathSegments().get(1); 56 break; 57 58 case R.id.contentProvider_update: 59 ContentValues values2= new ContentValues(); 60 values2.put(Xin.USERNAME, "王五"); 61 values2.put(Xin.EMAIL, "xxx@ddd.com"); 62 values2.put(Xin.SEX, "女"); 63 this.getContentResolver().update(uriId, values2, null, null); 64 break; 65 66 case R.id.contentProvider_delete: 67 this.getContentResolver().delete(uriId, null, null); 68 break; 69 70 default: 71 break; 72 } 73 } 74 75 }
注意:
首先需要插入消息,然后再查询或者更新或者删除,否则newId的值为空。