Content Provider内容提供者:android中进程间通信的标准接口。即让当前进程中数据库中数据,可以让其它应用访问到。
作用 : 程序中的数据库中某一张表想要给其他程序访问时,为了安全的访问,就可以用到内容提供者;
举个栗子:A应用相当于一家银行,B应用相当于你自己,你要向银行借钱时,那么就要和银行建立联系,银行的客户经理就相当于provide负责将数据暴露出来,自己就相当于resolve,负责和银行的客户经理接洽,provide与resolve怎么建立通信呢?我们可以找不同的银行借钱,每家银行都有自己的网点,我们根据provide的地址建立连接,如果客户经理同意你的请求,那么你们的通道打通了,客户经理从银行拿到钱,然后通过通道将钱借给你;provide接收客户端resolve的数据请求-->获得数据库的数据-->返回数据;
奉上简单的代码;
创建一个工程:
在这个工程中创建数据库:
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// createBankDB(getBaseContext());
}
private void createBankDB(Context baseContext) {
BankDbHelper helper = new BankDbHelper(baseContext);
helper.getWritableDatabase();
}
}
数据库帮助类
public class BankDbHelper extends SQLiteOpenHelper {
public BankDbHelper(Context context) {
super(context, "bank.db", null, 1);
}
@Override
public void onCreate(SQLiteDatabase db) {
//创建account表和info表;
db.execSQL("create table account(id integer primary key autoincrement, name varchar(20),money folat)");
db.execSQL("create table info(id ,phone)");
} @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { }}
创建对外提供数据的provide,继承ContentProvider实现增删改查方法;在每个方法中打印日志查看是否连接成功;
public class AccountManage extends ContentProvider {
private static final String TAG = "AccountManage";
@Override
public boolean onCreate() {
return false;
}
@Nullable
@Override
public Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable String selection, @Nullable String[] selectionArgs, @Nullable String sortOrder) {
Log.e(TAG, "query: 查询调用" );
return null;
}
@Nullable
@Override
public String getType(@NonNull Uri uri) {
return null;
}
@Nullable
@Override
public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) {
Log.e(TAG, "insert: 增加方法调用");
return null;
}
@Override
public int delete(@NonNull Uri uri, @Nullable String selection, @Nullable String[] selectionArgs) {
Log.e(TAG, "delete: 删除方法调用");
return 0;
}
@Override
public int update(@NonNull Uri uri, @Nullable ContentValues values, @Nullable String selection, @Nullable String[] selectionArgs) {
Log.e(TAG, "update: 修改方法调用" );
return 0;
}
在清单文件中注册provide,
authorities相当于一个provide的地址,不同的程序有可能提供不同的
provide;
public class MainActivity extends AppCompatActivity {
@BindView(R.id.insert)
Button mInsert;
@BindView(R.id.delete)
Button mDelete;
@BindView(R.id.updata)
Button mUpdata;
@BindView(R.id.query)
Button mQuery;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ButterKnife.bind(this);
}
@OnClick({R.id.insert, R.id.delete, R.id.updata, R.id.query})
public void onClick(View view) {
switch (view.getId()) {
case R.id.insert:
break;
case R.id.delete:
break;
case R.id.updata:
break;
case R.id.query:
break;
}
}
}
增:
利用上下文获取到resolve对象:
ContentResolver resolver =getContentResolver();
通过resolve调用内容提供者的增方法
resolver.insert(uri,values);
insert方法中需要两个参数,一个uri,一个ContentValues对象,
ContentValues values = new ContentValues();
//指定内容提供者的地址,就是注册时的
authorities来区分要连接的那个provide,一定要注意格式:"connect://+authorities"
Uri uri = Uri.parse("content://com.contentprovidersdemo.db.bank");
@OnClick({R.id.insert, R.id.delete, R.id.updata, R.id.query})
public void onClick(View view) {
switch (view.getId()) {
case R.id.insert:
ContentResolver resolver =getContentResolver();
ContentValues values = new ContentValues();
Uri uri = Uri.parse("content://com.contentprovidersdemo.db.bank");
resolver.insert(uri,values);
Toast.makeText(this,"insert点击",Toast.LENGTH_LONG).show();
break;
case R.id.delete:
break;
case R.id.updata:
break;
case R.id.query:
break;
}
将两个程序运行起来,点击insert按钮,可以看到输出日志:
11-12 16:31:30.200 32433-32444/? E/AccountManage: insert: 增加方法调用
11-12 16:31:30.250 1612-1717/? E/Cataloger: syncFile:/storage/emulated/0/Mob/comm/locks/.dhlock
11-12 16:31:30.250 1612-1717/? E/Cataloger: need not sync path:/storage/emulated/0/Mob/comm/locks/.dhlock
11-12 16:31:36.300 32433-32443/? E/AccountManage: insert: 增加方法调用
11-12 16:31:40.260 1612-1717/? E/Cataloger: syncFile:/storage/emulated/0/Mob/comm/locks/.dhlock
11-12 16:31:40.260 1612-1717/? E/Cataloger: need not sync path:/storage/emulated/0/Mob/comm/locks/.dhlock
如果报
java.lang.SecurityException: Permission Denial
表名的区分:UriMatcher
resolver发起请求,也是通过uri来区分要操作的哪张表;就像访问网站时我们可以通过/来指定访问的目录,操作表也一样
Uri uri = Uri.parse("content://com.contentprovidersdemo.db.bank/account");
将表名添加到uri的authorities后面以/分开;通过log打印看看:
public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) {
Log.e(TAG, "insert: uri = " +uri.toString());
return null;
}
点击按钮输出:
E/AccountManage: insert: uri = content://com.contentprovidersdemo.db.bank/account
谷歌提供一个用来匹配URI的工具: UriMather
在provide中创建uri匹配器对象并且以静态代码块的方式预设匹配规则
//创建匹配器;参数默认是一个-1的常量匹配不上的时候返回-1
static UriMatcher uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
//预设匹配规则,将要操作的表都可以添加,定义不同的code\
static {
uriMatcher.addURI(authoritity,path,code);
}
第一个参数就是provide中的authorities, path 就是要操作的表名,code是自己预设的一个整数常量;
//创建匹配器;
static UriMatcher uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
private static final int ACCOUNT = 1;
private static final int INFO = 2;
//预设匹配规则\
static {
uriMatcher.addURI("com.contentprovidersdemo.db.bank","account",ACCOUNT);
uriMatcher.addURI("com.contentprovidersdemo.db.bank","info",INFO);
}
@Nullable
@Override
public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) {
Log.e(TAG, "insert: uri = " +uri.toString());
//通过uriMatcher.match(uri)返回的值来判断是哪张表,
if (uriMatcher.match(uri) == INFO){ Log.d(TAG, "操作的是info表"); }else if (uriMatcher.match(uri) == ACCOUNT){ Log.d(TAG, "insert: 操作的是 account 表"); }else { Log.d(TAG, "insert: 没有匹配到"); } return null; }
日志输出:
11-12 22:05:34.978 21992-22002/? E/AccountManage: insert: uri = content://com.contentprovidersdemo.db.bank/account
11-12 23:03:02.484 32743-32754/? E/AccountManage: insert: uri = content://com.contentprovidersdemo.db.bank/account
11-12 23:03:02.484 32743-32754/? D/AccountManage: insert: 操作的是 account 表
11-12 23:03:50.844 1377-1389/? E/AccountManage: insert: uri = content://com.contentprovidersdemo.db.bank/info
11-12 23:03:50.844 1377-1389/? D/AccountManage: 操作的是info表
通过这种方式就可以区分是那张表了!
private BankDbHelper mHelper;
@Override
public boolean onCreate() {
//在创建provide的时候创建数据库帮助类对象;
mHelper = new BankDbHelper(getContext());
return false;
}
@Nullable
@Override
public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) {
Log.e(TAG, "insert: uri = " +uri.toString());
SQLiteDatabase db = mHelper.getWritableDatabase();
if (uriMatcher.match(uri) == INFO){
//插入info表 ,插入是数据有resolve传过来
db.insert("info",null,values);
Log.d(TAG, "插入的是info表");
}else if (uriMatcher.match(uri) == ACCOUNT){
db.insert("account",null,values);
Log.d(TAG, "insert: 插入的是 account 表");
}else if (uriMatcher.match(uri)== UriMatcher.NO_MATCH){
//匹配不到表名的时候抛出一个异常;
throw new IllegalArgumentException("未知表名,请检查表名");
}
return null;
}
@Override
public int delete(@NonNull Uri uri, @Nullable String selection, @Nullable String[] selectionArgs) {
SQLiteDatabase db = mHelper.getWritableDatabase();
if (uriMatcher.match(uri)==INFO){
//要删除的条件和数据都是resolve传递过来的
db.delete("info",selection,selectionArgs);
Log.d(TAG, "delete: 删除是info表的内容");
}else if (uriMatcher.match(uri) == ACCOUNT){
db.delete("account",selection,selectionArgs);
Log.d(TAG, "delete: 删除是account表的内容");
}else if (uriMatcher.match(uri) == UriMatcher.NO_MATCH){
throw new IllegalArgumentException("未知的表名");
}
Log.e(TAG, "delete: 删除方法调用");
return 0;
}
@Override
public int update(@NonNull Uri uri, @Nullable ContentValues values, @Nullable String selection, @Nullable String[] selectionArgs) {
Log.e(TAG, "update: 修改方法调用" );
SQLiteDatabase db = mHelper.getWritableDatabase();
if (uriMatcher.match(uri)==INFO){
//要修改的条件和数据都是resolve传递过来的
db.update("info",values,selection,selectionArgs);
Log.d(TAG, "update: 修改是info表的内容");
}else if (uriMatcher.match(uri) == ACCOUNT){
db.update("account",values,selection,selectionArgs);
Log.d(TAG, "update: 修改是account表的内容");
}else if (uriMatcher.match(uri) == UriMatcher.NO_MATCH){
throw new IllegalArgumentException("未知的表名");
}
return 0;
}
@Nullable
@Override
public Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable String selection, @Nullable String[] selectionArgs, @Nullable String sortOrder) {
Log.e(TAG, "query: 查询调用" );
SQLiteDatabase db = mHelper.getReadableDatabase();
if (uriMatcher.match(uri)==INFO){
Cursor cursor = db.query("info", projection, selection, selectionArgs, null, null, sortOrder);
//注意,这里查询的cursor对象是不能关闭的,要关也得由resolve来关闭,
return cursor;
}else if (uriMatcher.match(uri) == ACCOUNT){
Cursor cursor = db.query("account", projection, selection, selectionArgs, null, null, sortOrder);
return cursor;
}else if (uriMatcher.match(uri) == UriMatcher.NO_MATCH){
throw new IllegalArgumentException("未知的表名");
}
return null;
}
@OnClick({R.id.insert, R.id.delete, R.id.updata, R.id.query})
public void onClick(View view) {
switch (view.getId()) {
case R.id.insert: {
ContentResolver resolver = getContentResolver();
ContentValues values = new ContentValues();
values.put("name", "和珅");
values.put("money", 20000000);
Uri uri = Uri.parse("content://com.contentprovidersdemo.db.bank/account");
resolver.insert(uri, values);
Toast.makeText(this, "insert点击", Toast.LENGTH_LONG).show();
break;
}
case R.id.delete:{
ContentResolver resolver =getContentResolver();
Uri uri = Uri.parse("content://com.contentprovidersdemo.db.bank/account");
resolver.delete(uri,"name=?",new String[]{"和珅"});
Toast.makeText(this, "删除成功", Toast.LENGTH_LONG).show();
}
break;
case R.id.updata:{
Uri uri = Uri.parse("content://com.contentprovidersdemo.db.bank/account");
ContentResolver resolver =getContentResolver();
ContentValues values = new ContentValues();
values.put("money",999999);
resolver.update(uri,values,"name=?",new String[]{"和珅"});
Toast.makeText(this, "修改成功", Toast.LENGTH_LONG).show();
}
break;
case R.id.query:{
Uri uri = Uri.parse("content://com.contentprovidersdemo.db.bank/account");
ContentResolver resolver =getContentResolver();
/**
* resolver.query(uri,
* projection,查询的列 ,null为所有列
* selection, 查询条件, null 为查询所有
* selectionArgs, 查询条件 的值
* sortOrde); 排序 null 为默认排序
*/
Cursor cursor = resolver.query(uri, null, "name=?", new String[]{"和珅"}, null);
if (cursor.moveToFirst()){
float money = cursor.getFloat(cursor.getColumnIndex("money"));
Toast.makeText(this, "和珅的资产 = "+money, Toast.LENGTH_SHORT).show();
}else
{
Toast.makeText(this, "查无此人", Toast.LENGTH_SHORT).show();
}
cursor.close();
}
break;
}
provide :
1.创建ContentProvide 子类;
2.添加UriMatcher 匹配规则;
3.实现子类的曾删改查方法
Resolver :
1.上下文获取resolver对象
2.通过Uri区分访问的provide和具体的表
3.实现增删改查方法