内容提供器,同样作为|Android四大组件,在学习Android的过程中对于它是最陌生的。它的作用是是实现应用程序之间交换数据,ContentProvider是不同应用程序之间进行数据交换的标准API,当一个应用程序需要把自己的数据暴露给其他程序使用时,该应用程序就可通过提供ContentProvider来实现,其他应用程序可通过ContentResolver来操作ContentProvider暴露的数据,包括增加数据,删除数据,修改数据,查询数据等。
关于ContentProvider,其概念没必要深究,只要知道它的作用就好了,如何使用我们边做边说。
首先作为四大组件之一,同Service、BroadcastReceiver相似的需要自行创建类来继承ContentProvider,重写方法后需要在配置文件中配置至少3个属性:
android:name=".FirstContentProvider"包名+类名
android:authorities="com.briup.hasagi"主机名,该属性相当于访问拥有该ContentProvider的应用程序的密钥,只有知道它才能访问它的数据库
android:exported="true"是否允许外界程序访问,默认是false
为了方便数据访问,我们需要在该程序中创建数据库。
重写onCreate():该方法在ContentProvider创建后被调用,我们在该方法中创建数据库,该数据库中包括两个表(通常在主方法中创建)
重写getType():该方法用于返回当前Uri所代表的数据的MIME类型,
如果该Uri对应的数据可能包括多条记录,那么MIME类型字符串应用以vnd.android.cursor.dir/开始,如果该Uri对应的数据只包括一条记录,那么MIME类型字符串应用以vnd.android.cursor.item/开始,我们不做这个测试
重写insert(Uri,ContentValuevalues):根据Uri插入values对应的数据
重写delete(Uri,Stringselection,String[] selectArgs):根据Uri删除selection条件匹配的全部记录
重写update():根据Uri修改select条件所匹配的全部记录
重写query():根据Uri查询出select条件所匹配的全部记录
增删改查的方法都要传入Uri参数,该参数即是访问原数据库的密钥,这条域名中可以包含所要操作的表名和相关条件。我们发现,虽然ContentProvider提供了外界访问数据的窗口,但是真正操作数据库的却是ContentProvider。那么将所要进行的操作逻辑写在各方法中,这个应用程序就可以被访问了。操作方法都是SQLite数据操作方法。
这里我们建一个新的应用程序作为访问前面应用程序的外界应用。在该应用程序中并不是要调用ContentProvider来操作数据,Android提供了一个ContentResolver对象,可以通过Context.getContentResolver()方法获取,该对象中有对应的增删改查方法,能够与ContentProvider中的增删改查方法一一对应,我们只要在外界程序中调用这些方法就能完成对原程序数据库的操作。
ContentResolver的方法均需要穿入一个Uri参数,该参数即统一资源标识符,其结构一般为协议://主机.端口号:应用程序路径,ContentProvider的协议固定为content,主机名我们在原程序中已经配置,那么在这里我们需要传入的Uri应为”content://com.briup.hasagi”,我们这里有两张表所以还需要增加访问表:”content://com.briup.hasagi/date1”,还可以增加修改或删除的条件:”content://com.briup.hasagi/date1/1”。
在源程序中如何辨别外界程序要访问的是哪个表呢,需要借助UriMatcher(Uri辨识器),首先必须预先通过addURI()方法为辨识器配置预选Uri,在操作方法中通过match()方法分辨Uri,获取表名和查询条件。
第一个应用程序:
该程序即是原生创建的,所有配置都是默认的,创建后只需添加以下类
ContentProvider类
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;
public class FirstContentProvider extends ContentProvider {
private MySQLiteOpenHelper helper;
private static UriMatcher uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
// Uri检测器
static {// 设置预匹配域名
uriMatcher.addURI("com.briup.hasagi", "date1", 1);// 主机名,资源路径,唯一标识
uriMatcher.addURI("com.briup.hasagi", "date2", 2);
uriMatcher.addURI("com.briup.hasagi", "date1/#", 3);// 资源路径后跟#表示任意数字
}
// ContentProvider初始化时调用
@Override
public boolean onCreate() {
Log.i("briup", "onCreate");
helper = new MySQLiteOpenHelper(getContext());
return false;
}
// 删除
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
SQLiteDatabase db = helper.getWritableDatabase();
int d = 0;// 统计处理条数
if (uriMatcher.match(uri) == 3) {
// content://com.briup.hasagi/date/1
long id = ContentUris.parseId(uri);
d = db.delete("date1", "_id=?", new String[] { id + "" });
}
db.close();
return d;
}
// 插入
@Override
public Uri insert(Uri uri, ContentValues values) {
Log.i("briup", "insert");
SQLiteDatabase db = helper.getReadableDatabase();
// 检测传入Uri
if (uriMatcher.match(uri) == 1) {// 插入到date
// content://com.briup.hasagi/date1
db.insert("date1", null, values);
} else if (uriMatcher.match(uri) == 2) {// 插入到datetwo
// content://com.briup.hasagi/date2
db.insert("date2", null, values);
} else {// 如果没有匹配将抛出非法参数异常
throw new IllegalArgumentException("参数匹配异常:Uri不匹配");
}
db.close();
return null;
}
// 查询
@Override
public Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
Log.i("briup", "query");
SQLiteDatabase db = helper.getReadableDatabase();
Cursor cursor;
if (uriMatcher.match(uri) == 1)
cursor = db.query("date1", projection, selection, selectionArgs,
null, null, sortOrder);
else if (uriMatcher.match(uri) == 2)
cursor = db.query("date2", projection, selection, selectionArgs,
null, null, sortOrder);
else
throw new IllegalArgumentException("参数匹配异常:Uri不匹配");
// db.close();
return cursor;
}
// 更新
@Override
public int update(Uri uri, ContentValues values, String selection,
String[] selectionArgs) {
Log.i("briup", "update");
SQLiteDatabase db = helper.getReadableDatabase();
int u = 0;// 统计处理条数
if (uriMatcher.match(uri) == 3) {
// content://com.briup.hasagi/date/1
long id = ContentUris.parseId(uri);
u = db.update("date1", values, "_id=?", new String[] { id + "" });
}
db.close();
return u;
}
@Override
public String getType(Uri uri) {
// TODO Auto-generated method stub
return null;
}
}
SQLiteDateBaseOpenHelper类
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
public class MySQLiteOpenHelper extends SQLiteOpenHelper {
public MySQLiteOpenHelper(Context context) {
super(context, "date.db", null, 1);
}
@Override
public void onCreate(SQLiteDatabase db) {
String createSql1 = "create table date1(_id integer primary key,name,age)";
db.execSQL(createSql1);
String createSql2 = "create table date2(_id integer primary key,name,age)";
db.execSQL(createSql2);
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
}
}
配置文件如下
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.briup.contentprovide"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk
android:minSdkVersion="17"
android:targetSdkVersion="24" />
<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@android:style/Theme.Holo.Light" >
<activity
android:name=".MainActivity"
android:label="@string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<provider
android:name=".FirstContentProvider"
android:authorities="com.briup.hasagi"
android:exported="true" >
</provider>
<!-- authorities主机名 exported是否允许外界访问 -->
</application>
</manifest>
第二个应用程序
布局文件如下
<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:orientation="vertical"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="com.briup.contentresolverapp.MainActivity" >
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="insert"
android:text="增" />
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="delete"
android:text="删" />
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="update"
android:text="改" />
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="query"
android:text="查" />
</LinearLayout>
MainActivity类文件如下
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.Toast;
/**
* 通过该应用程序访问之前的应用程序数据
* @author Administrator
*
*/
public class MainActivity extends Activity {
private static Uri uri1 = Uri.parse("content://com.briup.hasagi/date1");// 协议://主机.端口号:应用程序路径
private static Uri uri2 = Uri.parse("content://com.briup.hasagi/date2");
private static int id = 1;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
//两个表中均插入
public void insert(View v) {
ContentResolver contentResolver = getContentResolver();
ContentValues values = new ContentValues();
values.put("_id", id++);
values.put("name", "盖伦");
values.put("age", "30");
contentResolver.insert(uri1, values);
ContentValues values2 = new ContentValues();
values2.put("_id", id++);
values2.put("name", "德莱厄斯");
values2.put("age", "30");
contentResolver.insert(uri2, values2);
}
//删除date表中数据
public void delete(View v) {
ContentResolver contentResolver = getContentResolver();
//在预定域名后加任意数字,该数字通常当作id使用
int d = contentResolver.delete(Uri.parse("content://com.briup.hasagi/date1/"+(id-2)), null, null);
Toast.makeText(this, "删除 "+d+" 条数据", Toast.LENGTH_SHORT).show();
}
//更新date表中的数据
public void update(View v) {
ContentResolver contentResolver = getContentResolver();
ContentValues values = new ContentValues();
values.put("name", "卡特");
values.put("age", "28");
int u = contentResolver.update(Uri.parse("content://com.briup.hasagi/date1/"+(id-2)), values, null, null);
Toast.makeText(this, "更新 "+u+" 条数据", Toast.LENGTH_SHORT).show();
}
//查询两个表中所有数据
public void query(View v) {
ContentResolver contentResolver = getContentResolver();
StringBuffer buffer = new StringBuffer();
//date表
Cursor cursor = contentResolver.query(uri1,
new String[] { "name", "age" }, null, null,
"_id");
buffer.append("date1:\n");
while (cursor.moveToNext())
buffer.append(cursor.getString(0) + "\t" + cursor.getString(1)
+ "\n");
//datetwo表
Cursor cursor2 = contentResolver.query(uri2,
new String[] { "name", "age" }, null, null,
"_id");
buffer.append("date2:\n");
while (cursor2.moveToNext())
buffer.append(cursor2.getString(0) + "\t" + cursor2.getString(1)
+ "\n");
Toast.makeText(this, buffer, Toast.LENGTH_SHORT).show();
}
}
效果如下:
两个表中添加数据并查询
修改date1的数据并查询
删除date1的数据并查询
LogCat输出结果