一.什么是ContentProvider?
ContentProvider直译过来就是内容提供者,主要作用就是A应用提供接口给B应用调用数据,和之前介绍的sharedPreference和直接开放文件访问类似,都是共享应用程序数据,不同的是之前的两种文件格式可能完全不同,如可能为xml,txt,sql等等,这里ContentProvider返回的数据格式是统一的,因此应用的更为广泛一点.
二.实例
这里使用的是A应用通过ContentProvider共享数据给B应用.这里A应用用的是前文中的android_db里的person表.B应用是新建的android_content_provider程序.
1.新创建android_content_provider应用程序
2.访问android_db共享的数据
创建好了应用程序以后,
/android_content_provider/src/com/example/android_content_provider/ContentProvider.java
package com.example.android_content_provider; import android.app.Activity; import android.content.ContentResolver; import android.database.Cursor; import android.net.Uri; import android.os.Bundle; import android.util.Log; public class ContentProvider extends Activity { private static String tag = "ContentProvider.class"; /** * Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); //内容解析者 ContentResolver contentResolver = getContentResolver(); Uri uri = Uri.parse("content://com.amos.android_db.provider.PersonProvider/persons"); Cursor cursor = contentResolver.query(uri, null, null, null, null); while(cursor.moveToNext()){ String name = cursor.getString(cursor.getColumnIndex("name")); int age = cursor.getInt(cursor.getColumnIndex("age")); System.out.println("name:"+name+" age:"+age); Log.d(tag,"用户名:"+name+" 年龄:"+age); } } }
3.android_db开放共享数据接口
1)开放一个uri
/android_db/AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.amos.android_db" android:versionCode="1" android:versionName="1.0"> <instrumentation android:name="android.test.InstrumentationTestRunner" android:targetPackage="com.amos.android_db"></instrumentation> <uses-sdk android:minSdkVersion="7"/> <application android:label="@string/app_name"> <uses-library android:name="android.test.runner"/> <activity android:name="MyActivity" android:label="@string/app_name"> <intent-filter> <action android:name="android.intent.action.MAIN"/> <category android:name="android.intent.category.LAUNCHER"/> </intent-filter> </activity> <!--给内容提供者提供定义一个uri,一般建议使用包名+类名,以供其它程序调用 --> <provider android:authorities="com.amos.android_db.provider.PersonProvider" android:name=".provider.PersonProvider"> </provider> </application> </manifest>
2).定义路径匹配(继承ContentProvider类)
/android_db/src/com/amos/android_db/provider/PersonProvider.java
package com.amos.android_db.provider; 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; import android.util.Log; import com.amos.android_db.MyDBHelper; import com.amos.android_db.dao.Person; import com.amos.android_db.dao.PersonDao; import java.util.List; /** * Created by amosli on 14-6-17. */ public class PersonProvider extends ContentProvider { //创建一个路径识别器 //常量UriMatcher.NO_MATCH表示不匹配任何路径的返回码,也就是说如果找不到匹配的类型,返回-1 private static final UriMatcher uriMatcher = new UriMatcher(UriMatcher.NO_MATCH); private static final int ALL_PERSON = 1; private static final int PERSON = 2; private static final int OTHER = 3; private static String tag="PersonProvider.class"; static{ //1.指定一个路径的匹配规则 //如果路径满足content://com.amos.android_db.provider.PersonProvider/persons,返回值就是(ALL_PERSON)=1 uriMatcher.addURI("com.amos.android_db.provider.PersonProvider","persons",ALL_PERSON); //2.如果路径满足content://com.amos.android_db.provider.PersonProvider/person/3,返回值就是(PERSON)=2 //#号为通配符 uriMatcher.addURI("com.amos.android_db.provider.PersonProvider","person/#",PERSON); //3.如果路径满足content://com.amos.android_db.provider.PersonProvider/other,返回值就是(OTHER)=3 uriMatcher.addURI("com.amos.android_db.provider.PersonProvider","other",OTHER); } /** * 一般是对象第一次被创建时调用的方法 * * @return */ @Override public boolean onCreate() { return false; } /** * 让别人去调用返回结果 * * @param uri * @param projection 选择的列 * @param selection 查询条件 * @param selectionArgs 查询条件的value * @param sortOrder 排序 * @return */ @Override public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { int result = uriMatcher.match(uri); switch(result){ //如果路径满足content://com.amos.android_db.provider.PersonProvider/persons,返回值就是(ALL_PERSON)=1 case ALL_PERSON: PersonDao dao = new PersonDao(this.getContext()); return dao.findAllByCursor(); //2.如果路径满足content://com.amos.android_db.provider.PersonProvider/person/3,返回值就是(PERSON)=2 case PERSON: long id = ContentUris.parseId(uri); SQLiteDatabase database = new MyDBHelper(this.getContext()).getReadableDatabase(); if(database.isOpen()){ database.execSQL("select * person where personid = "+id); return database.query("person", null, "personid", new String[]{id + ""}, null, null, null); //不要关闭数据库,否则就没有数据了. } case OTHER: Log.d(tag,"我是其他匹配规则!"); break; default: throw new RuntimeException("出错了!!"); } return null; } @Override public String getType(Uri uri) { return null; } @Override public Uri insert(Uri uri, ContentValues values) { return null; } @Override public int delete(Uri uri, String selection, String[] selectionArgs) { return 0; } @Override public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { return 0; } }
4.效果图
首先,将android_db部署到avd上,其次,运行android_content_provider项目,最后,查看log输出.
5.出现的问题
1).报空指针错误
..... at dalvik.system.NativeStart.main(Native Method) Caused by: java.lang.NullPointerException at com.example.android_content_provider.ContentProvider.onCreate(ContentProvider.java:21) at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1047) at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:1611) .....
这种问题很有可能是路径名称不对,注意提供的接口名称保持一致;并且保证获取数据的接口是正常的,即android_db里获取person表的内容是正常的.
2).intelij中的logcat中看不到log
注意选择Android--->选择要查看Log的进程--->点击上面的双向箭头进行切换日志展示信息.
6.扩展
1),查询,增加,删除,修改的接口全部实现
上面已经实现了查询的接口,这里将实现另外三个接口:
//4.插入数据,如果路径满足content://com.amos.android_db.provider.PersonProvider/insert,返回值就是(INSERT)=4 uriMatcher.addURI("com.amos.android_db.provider.PersonProvider", "insert", INSERT); //5.删除数据,如果路径满足content://com.amos.android_db.provider.PersonProvider/delete,返回值就是(DELETE)=5 uriMatcher.addURI("com.amos.android_db.provider.PersonProvider", "delete", DELETE); //6.更新数据,如果路径满足content://com.amos.android_db.provider.PersonProvider/update,返回值就是(UPDATE)=6 uriMatcher.addURI("com.amos.android_db.provider.PersonProvider", "update", UPDATE);
对应的实现:
@Override public Uri insert(Uri uri, ContentValues values) { //content://com.amos.android_db.provider.PersonProvider/insert int result = uriMatcher.match(uri); switch (result) { case INSERT: SQLiteDatabase database = myDBHelper.getWritableDatabase(); if (database.isOpen()) { database.insert("person", null, values); } return uri; default: throw new RuntimeException("无法识别该URI,出错了!!"); } } @Override public int delete(Uri uri, String selection, String[] selectionArgs) { //删除操作 //content://com.amos.android_db.provider.PersonProvider/delete int result = uriMatcher.match(uri); switch (result) { case DELETE: SQLiteDatabase database = myDBHelper.getWritableDatabase(); return database.delete("person", selection, selectionArgs); default: throw new RuntimeException("无法识别该URI,出错了!!"); } } @Override public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { //更新操作 //content://com.amos.android_db.provider.PersonProvider/update int result = uriMatcher.match(uri); switch (result) { case UPDATE: SQLiteDatabase database = myDBHelper.getWritableDatabase(); return database.update("person", values, selection, selectionArgs); default: throw new RuntimeException("无法识别该URI,出错了!!"); } }
//返回值的类型
@Override public String getType(Uri uri) { int result = uriMatcher.match(uri); switch (result){ case ALL_PERSON: return "List<Person>"; case PERSON: return "Person"; default:return null; } }
2)测试
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.ContentProviderTest" android:versionCode="1" android:versionName="1.0"> <uses-sdk android:minSdkVersion="7"/> <instrumentation android:name="android.test.InstrumentationTestRunner" android:targetPackage="com.example.ContentProviderTest"/> <application android:label="@string/app_name"> <uses-library android:name="android.test.runner"/> <activity android:name="MyActivity" android:label="@string/app_name"> <intent-filter> <action android:name="android.intent.action.MAIN"/> <category android:name="android.intent.category.LAUNCHER"/> </intent-filter> </activity> </application> </manifest>
加入加粗的两行,配置好测试的环境.
/ContentProviderTest/src/com/example/ContentProviderTest/test/TestCase.java
package com.example.ContentProviderTest.test; import android.content.ContentResolver; import android.content.ContentValues; import android.net.Uri; import android.test.AndroidTestCase; /** * Created by amosli on 14-6-19. */ public class TestCase extends AndroidTestCase { public void testInsert(){ ContentResolver contentResolver = getContext().getContentResolver(); Uri uri = Uri.parse("content://com.amos.android_db.provider.PersonProvider/insert"); ContentValues values = new ContentValues(); values.put("name", "bill"); values.put("age", 18); contentResolver.insert(uri, values); } public void testDelete(){ ContentResolver contentResolver = getContext().getContentResolver(); Uri uri = Uri.parse("content://com.amos.android_db.provider.PersonProvider/delete"); contentResolver.delete(uri,"name=?",new String[]{"amos96"}); } public void testUpdate(){ ContentResolver contentResolver = getContext().getContentResolver(); Uri uri = Uri.parse("content://com.amos.android_db.provider.PersonProvider/update"); ContentValues contentValues = new ContentValues(); contentValues.put("name","jack"); contentValues.put("age",30); contentResolver.update(uri,contentValues,"name=?",new String[]{"amos97"}); } }
3)效果图:
插入数据(insert方法),bill
删除数据(delete方法),amos96
更新数据(update方法 ),amos97
本文源码:
https://github.com/amosli/android_basic/tree/android_db
https://github.com/amosli/android_basic/tree/content_provoider
https://github.com/amosli/android_basic/tree/android_contentProviderTest