当多个应用程序共享同一数据时,我们可以给这个数据定义一个URI,然后把这个URI以接口的形式暴漏出去,之后其他应用程序对此数据进行增删改查时,只需要从当前上下文对象获得一个ContentResolver(内容解析器)传入相应的URI就可以了。数据库支持直接操作db文件,问什么还要用内容提供人?这是出于安全的考虑,内容提供者可以根据自己的意愿选择性的提供数据给客户端使用。
1、编写一个类,让其继承自ContentProvider类;
2、实现ContentProvider类中所有的抽象方法;需要实现:onCreate() 、getType() 、query() 、insert() 、update()、delete() 等方法。
3、定义ContentProvider的Uri。这个Uri是ContentResolver对象执行CRUD操作时重要的参数;
4、使用UriMatcher对象映射Uri返回代码;
5、在AndroidMainfest.xml文件中使用<provider>标签注册ContentProvider。
备注:
ContentProvider暴露出来的数据和方法并不是给自身调用的,而是给其他应用程序来调用。其他应用程序通过其ContentResolver对象调用的query() 、insert() 、update()、delete() 等方法就是我们在这里暴露出来的ContentProvider类中的重写后的query() 、insert() 、update()、delete() 方法。
(二)、ContentProvider类中的六个抽象方法:
1、boolean onCreate()
2、Uri insert(Uri uri, ContentValues values)
3、int delete(Uri uri, String selection, String[] selectionArgs)
4、int update(Uri uri, ContentValues values, String selection, String[] selectionArgs)
5、Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder)
6、String getType(Uri uri)
(三)、ContentProvider类中六个抽象方法的说明:
1、onCreate() 初始化provider
2、query() 返回数据给调用者
3、insert() 插入新数据到ContentProvider
4、update() 更新ContentProvider已经存在的数据
5、delete() 从ContentProvider中删除数据
6、getType() 返回ContentProvider数据的Mime类型
(四)、在清单文件中声明注册ContentProvider:
<provider android:name=".MyWordsProvider"
android:authorities="com.steven.wordscontentprovider"
android:exported="true" />
//android:name 属性的值是:ContentProvider类的子类的完整路径;
//android:authorities 属性的值是:content:URI中authority部分。一般就是将name属性的值全小写。
//android:exported 属性是否允许其他应用调用。如果是false,则该ContentProvider不允许其他应用调用。
private static UriMatcher matcher = null;
static {
// 定义一个Uri匹配器。将UriMatcher.NO_MATCH,即-1作为参数。
matcher = new UriMatcher(UriMatcher.NO_MATCH);
// 定义一组匹配规则
matcher.addURI(AUTHORITY, "words", 1);
matcher.addURI(AUTHORITY, "newwords", 2);
}
(六)下面通过一个实例来讲解自定义ContentProvider,需要创建两个工程,一个工程用来自定义ContentProvider,然后把uri在程序清单中暴漏出去,那么此工程就相当于一个服务器端,可以供其他的工程来访问其数据;另外一个工程相当于客户端,通过使用ContentResolver 来操作使用ContentProvider 提供的数据。在测试时要先启动服务端,再启动客服端。
1,自定义ContentProvider的工程:
①创建一个继承ContentProvider类:
package com.qf.mycontentprovide; 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.support.annotation.Nullable; /** * 多个应用共用同一个数据库,数据库就是一个db文件 * 数据库支持直接操作db文件,为什么还要用内容提供者? * 答:出于安全的考虑,内容提供者可以根据的自己的意愿选择性的提供数据给客户端使用。 * * 服务器和客户端,安卓有jdbc的api,意味着安卓是可以直接访问数据库的, * 但是实际中我们使用接口访问远程服务器,这个接口提供给数据给我们的同时也是对远程数据库提供了保护, * 可以选择性的返回数据给客户端 */ public class MyContentProvider extends ContentProvider{ private SQLiteDatabase database; private UriMatcher uriMatcher; /** * 安装之后会执行,以后app启动不会执行 * 并且应用没有启动的时候,其他应用也是可以访问自己提供的数据的 * 和数据库的openhelper中的oncreate不一样,数据库是卸载再安装才会执行, * 而MyContentProvider只要重新安装就会执行 */ @Override public boolean onCreate() { DbHelper dbHelper = new DbHelper(getContext()); database = dbHelper.getReadableDatabase(); //获取到一个uri匹配对象 uriMatcher = new UriMatcher(UriMatcher.NO_MATCH); /** * addURI是把authority和path进行拼接 * code是给path设置了一个id */ uriMatcher.addURI("com.qf.mycontentprovide.MyContentProvider","person",1); uriMatcher.addURI("com.qf.mycontentprovide.MyContentProvider","teacher",2); return true; } /** * 操作数据库时,因为数据库有多张表,需要判断操作那张表, * 这时addURI的参数code就有了用武之地 */ @Nullable @Override public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { Cursor cursor = null; int match = uriMatcher.match(uri); switch (match){ //person表 case 1: cursor = database.query("person", projection, selection, selectionArgs, null, null, sortOrder); break; //teacher表 case 2: cursor = database.query("teacher", projection, selection, selectionArgs, null, null, sortOrder); break; default: break; } return cursor; } /** * ContentValues里封装了一个HashMap<String, Object> mValues; * 和普通的map的区别:键只能为string * @param uri 客户端传过来的uri路径 * @param values 客户端封装的数据传过来 * @return */ @Nullable @Override public Uri insert(Uri uri, ContentValues values) { int match = uriMatcher.match(uri); long num = 0; //插入数据受影响的行数 switch (match){ case 1: num = database.insert("person", null, values); break; case 2: num = database.insert("teacher",null,values); break; default: break; } //把受影响的行数拼接到uri后面 Uri uri1 = ContentUris.withAppendedId(uri, num); return uri1; } @Override public int delete(Uri uri, String selection, String[] selectionArgs) { int match = uriMatcher.match(uri); int num = 0; switch (match){ case 1: num = database.delete("person", selection, selectionArgs); break; case 2: num = database.delete("person", selection, selectionArgs); break; } return num; } @Override public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { int match = uriMatcher.match(uri); int num = 0; switch (match){ case 1: num = database.update("person", values, selection, selectionArgs); break; case 2: num = database.update("teacher",values,selection,selectionArgs); break; } return num; } @Nullable @Override public String getType(Uri uri) { return null; } }
package com.qf.mycontentprovide; import android.content.Context; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper; /** * Created by Administrator on 2016/9/20. * 支持内容提供者向外提供数据的数据库 */ public class DbHelper extends SQLiteOpenHelper { public DbHelper(Context context) { super(context, "Person.db", null, 2); } /** * 创建数据库时创建一张person表,并给表初始化一条数据 */ @Override public void onCreate(SQLiteDatabase db) { db.execSQL("create table person(_id integer primary key autoincrement,name text,pass text)"); db.execSQL("insert into person(name,pass)values('张飞','123')"); } /** * 更新数据库时,再创建一张teacher表,并初始化一条数据 */ @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { db.execSQL("create table teacher(_id integer primary key autoincrement,name text,pass text)"); db.execSQL("insert into person(name,pass)values('老张','12334')"); } }
③在清单文件中暴漏此内容提供者:
<span style="color:#464646;"><?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.qf.mycontentprovide"> <application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:supportsRtl="true" android:theme="@style/AppTheme"> <activity android:name=".MainActivity"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <!-- authorities:向外提供数据的接口,即uri,一般为包名+类名 exported:是否支持其他应用访问自己的数据,设置为true --> </span><span style="color:#ff0000;"><provider android:authorities="com.qf.mycontentprovide.MyContentProvider" android:name=".MyContentProvider" android:exported="true"> </provider></span><span style="color:#464646;"> </application> </manifest></span>由于MainActivity和activity_main.xml中不需要写代码,所以就不再给出。
2.再创建一个去共享内容提供者中的数据的工程:
①布局文件activity_main.xml中的代码:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" android:orientation="vertical" tools:context="com.example.client.MainActivity"> <Button android:id="@+id/query_person" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="查询person表" /> <Button android:id="@+id/query_teacher" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="查询teacher表" /> <Button android:id="@+id/insert_person" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="插入数据到person表" /> <Button android:id="@+id/insert_teacher" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="插入数据到teacher表" /> <Button android:id="@+id/delete_person" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="删除person表中的数据" /> <Button android:id="@+id/delete_teacher" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="删除teacher表中的数据" /> <Button android:id="@+id/updata_person" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="修改person表中的数据" /> <Button android:id="@+id/updata_teacher" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="修改teacher表中的数据" /> </LinearLayout>
package com.example.client; import android.content.ContentResolver; import android.content.ContentUris; import android.content.ContentValues; import android.content.UriMatcher; import android.database.Cursor; import android.net.Uri; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.util.Log; import android.view.View; public class MainActivity extends AppCompatActivity implements View.OnClickListener { private ContentResolver contentResolver; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); //初始化控件,并给控件设置监听 findViewById(R.id.query_person).setOnClickListener(this); findViewById(R.id.query_teacher).setOnClickListener(this); findViewById(R.id.insert_person).setOnClickListener(this); findViewById(R.id.insert_teacher).setOnClickListener(this); findViewById(R.id.delete_person).setOnClickListener(this); findViewById(R.id.delete_teacher).setOnClickListener(this); findViewById(R.id.updata_person).setOnClickListener(this); findViewById(R.id.updata_teacher).setOnClickListener(this); //获得ContentResolver对象来操作数据库 contentResolver = getContentResolver(); } @Override public void onClick(View v) { //person表的uri Uri personUri = Uri.parse("content://com.qf.mycontentprovide.MyContentProvider/person"); //teacher表的uri Uri teacherUri = Uri.parse("content://com.qf.mycontentprovide.MyContentProvider/teacher"); switch (v.getId()){ //查询person表 case R.id.query_person: Cursor cursor = contentResolver.query(personUri, new String[]{"name", "pass"}, null, null, null,null); while(cursor.moveToNext()){ String name = cursor.getString(cursor.getColumnIndex("name")); String pass = cursor.getString(cursor.getColumnIndex("pass")); Log.e("name",name); Log.e("pass",pass); } break; //查询teacher表 case R.id.query_teacher: Cursor cursor2 = contentResolver.query(teacherUri, new String[]{"name", "pass"}, null, null, null,null); while(cursor2.moveToNext()){ String name = cursor2.getString(cursor2.getColumnIndex("name")); String pass = cursor2.getString(cursor2.getColumnIndex("pass")); Log.e("name",name); Log.e("pass",pass); } break; case R.id.insert_person: ContentValues values = new ContentValues(); values.put("name","关羽"); values.put("pass","456"); Uri insert = contentResolver.insert(personUri, values); long num = ContentUris.parseId(insert); Log.e("num",num + ""); break; case R.id.insert_teacher: ContentValues values2 = new ContentValues(); values2.put("name","曹操"); values2.put("pass","321"); Uri insert2 = contentResolver.insert(teacherUri, values2); long num2 = ContentUris.parseId(insert2); Log.e("num",num2 + ""); break; case R.id.delete_person: int num3 = contentResolver.delete(personUri, "name = ?", new String[]{"张飞"}); Log.e("num3",num3 + ""); break; case R.id.delete_teacher: int num4 = contentResolver.delete(teacherUri, "name = ?", new String[]{"老张"}); Log.e("num4",num4 + ""); break; case R.id.updata_person: ContentValues values3 = new ContentValues(); values3.put("name","关羽"); values3.put("pass","789"); int num5 = contentResolver.update(personUri, values3, null, null); Log.e("num5",num5 + ""); break; case R.id.updata_teacher: ContentValues values4 = new ContentValues(); values4.put("name","曹操"); values4.put("pass","246"); int num6 = contentResolver.update(personUri, values4, null, null); Log.e("num6",num6 + ""); break; } } }