一、创建
新建:新建一个继承自ContentProvider的类
重写:重写6个方法,分别是create(),insert(),delete(),update(),query(),getType()
注册:需要在Manifest中注册provider。这样当外部程序访问本程序数据时,可以精确知道用的是哪个provider
android:authorities="com.example.administrator.sqliteexecsql.provider"
android:name="com.example.administrator.sqliteexecsql.DBProvider"
android:exported="true"/>
name 是继承自ContentProvider的类名
authorities 是自己起的一个名字,作为外部程序使用ContentProvider的识别.格式最好写com.example.provider
exported 设置为true表示允许外部程序使用provider,设为false则外部程序不能使用
调用:ContentResolver resolver = getContentResolver();之后根据设置的uri来具体调用ContentProvider中的方法
URI: content://com.example.provider/person/10
content:// ----->固定写法
com.example.provider即是你在清单文件中注册provider时的authorities
person-------->path
10-------------->pweson id
二、查(从比较难的查开始,其他的增删改则不在话下了)
//查询方法,需要传入uri,可以传入列名,查询条件,Where后面占位符的值,以及排序方式
@Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
SQLiteDatabase db =myHelper.getReadableDatabase();
/* Cursor c = db.query("person",null,null,null,null,null,null); 查询所有,所有列,无条件,无排序方式等。
如果那边在调用Cursor query()方法时,需要有条件,则这边db.query()中需要加上相应的参数
例如,那边要查balance>? orderBy为 “balance DESC”,那么这边就要
Cursor c = db.query("person",null,selection,selectionArgs,null,null,sortOrder);
但是,另一个问题出现了,就是这里写死了查person表,如果数据库里有多个表,如何指定查哪张表呢?
在外部应用在使用本程序的数据时,查哪张表,别人可以在Uri指定path,本程序要做对应处理,如何处理?
分析,外部应用,指定查哪张表的uri:content://com.example.provider/person 后面的person(path)即为表名
我们只要在本程序中可以拿到后面的表名就可以设置逻辑判断,分析结束
解决方法:UriMatcher 对象的addURI("authorities",path,code)方法,给每个表一个数字编码
具体代码如下:
matcher.addURI(authorities,"person",0);
matcher.addURI(authorities,"student",1);
matcher.addURI(authorities,"person/#",2);
UriMatcher matcher = new UriMatcher(UriMatcher.NO_MATCH);
authorities就写Manifest中配置的。下面两行将person表的编码设为0,student表的编码设为1
设完之后如何判断?matcher.match(uri),可以返回code
判断查哪张表的逻辑已经清晰了(UriMatcher-->matcher.addURI(,,,)-->matcher.match(uri)-->得到code)
现在又另外一个问题,我想查当前表的具体哪一个id,当然可以再外部程序调用时加上where条件
外部程序中Cursor c =resolver.query("person",null,"id=?",new String[]{"10"},null);
但是太麻烦。我想把id=?去掉,即selection为null,不直接写条件
想直接在Uri的person后面在加上一个id作为新的path
例如content://com.example.provider/person/10查person表中id为10 的元素
如何知道匹配uri后面加上person同时又跟上了id?--->matcher.addURI("","person/#",2);
方法:addURI()的第二个参数写成person/#
如何得到id ?--->>long id = ContentUris.parseId(uri);
得到id之后?在本方法中将case 2方法写在case 0之前,不加break,将selection 改为selection ="id="+id;
之后顺序执行,便可以返回一个Cursor对象
问题又来了,我们在最开始查balance>?时,外部程序写的是:
Cursor c = resolver.query("person",null,"balance>?",new String[]{5000+""},"balance DESC");
也就是说此时传入的条件selection已经不为空了,此时有两个条件 balance>?AND id=10
所以,要先判断selection是否为空(是不是只有id等于多少这一个条件)
selection =selection==null?"id="+id:selection +"AND id="+id
总结:addURI(,"person/#",)-->long id =ContentUris.parseId(uri)-->selection==null?
另外一个细节,我们一般不写case 0 ,case 1等等,可以写成
private static final int PERSON =0; case PERSON:....*/
switch (matcher.match(uri)){
case PERSON_ID:
long id = ContentUris.parseId(uri);
selection =selection==null?"id="+id:selection+"AND id="+id;
case PERSON:
return db.query("person",null,selection,selectionArgs,null,null,sortOrder);
case STUDENT:
//查学生表
return null;
default:
throw new RuntimeException("URI不能识别");
}
}
}
三、增删改
1.增
在MyContentProvider中写的方法:
//插入方法,需要传进来两个参数
//db.indert会返回一个long 类型的id
//ContentUris.withAppendedId(uri,id);
//将得到的id和原来的uri凑在一起,返回一个uri(一个后面带有原来的path再加上)
@Override
public Uri insert(Uri uri, ContentValues values) {
SQLiteDatabase db = myHelper.getWritableDatabase();
long id= db.insert("person","id",values);
return ContentUris.withAppendedId(uri,id);
}
在外部程序调用的方法:
case R.id.insert:
ContentValues values = new ContentValues();
values.put("name","lee");
values.put("balance",10000);
uri1= resolver.insert(uri1,values);//调用DBProvider中的insert方法,插入并返回一个Uri
Toast.makeText(MainActivity.this,uri1+"",Toast.LENGTH_LONG).show();
break;
2.删
在MyContentProvider中写的方法:
//int类型,返回的是删除的个数,如果是带有id的id的Uri,如果删除了,返回1
//不带id的uri,则返回实际删除了多少个
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
SQLiteDatabase db = myHelper.getWritableDatabase();
switch (matcher.match(uri)){
case PERSON_ID:
long id = ContentUris.parseId(uri);
selection = selection==null?"id="+id:selection+"AND id="+id;
case PERSON:
return db.delete("person",selection,selectionArgs);
default:
throw new RuntimeException("Uri cannot be identified");
}
在外部程序中调用的方法:
case R.id.delete:
//resolver.delete返回的是删除掉的个数,删除一个返回1,没找到需要删除的返回0,删除掉用带id的uri只能删除一个,下面换一个uri
//将path指定为person,删除大于5000的,此时便返回删除的个数
int deletedCount= resolver.delete(uri1,"balance >?",new String[]{5000+""});
Toast.makeText(MainActivity.this,deletedCount+"",Toast.LENGTH_LONG).show();
break;
3.改
在MyContentProvider中写的方法:
//返回的是改变成功的个数
@Override
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
SQLiteDatabase db =myHelper.getWritableDatabase();
switch (matcher.match(uri)) {
case PERSON_ID:
long id = ContentUris.parseId(uri);
selection = selection == null ? "id=" + id : selection + "AND id=" + id;
case PERSON:
return db.update("person", values, selection, selectionArgs);
default:
throw new RuntimeException("Uri cannot be identified");
}
}
在外部程序中调用的方法:
case R.id.update:
ContentValues values1 = new ContentValues();
values1.put("name","lee");
values1.put("balance",30000);
int b = resolver.update(uri1,values1,"balance",new String[]{5000+""});//将金额小于5000的人名字全改为lee,金额全改为30000(只是举个例子)
Toast.makeText(MainActivity.this,b+"",Toast.LENGTH_LONG).show();
}
至此增删改查全都有了,getType()和几个小细节会再稍后更
1.Type
调用getType,传入一个Uri,会返回这个uri对应的MIME类型
有固定的格式:
vnd.android.cursor.dir/vnd.authority.path(如果uri不带id只带path)
vnd.android.cursor.item/vnd.authority.path(如果uri带有id)
2.DBProvider中的onCreate()方法什么时候执行?
第一次启东时执行,启动后常驻后台,除非杀死,否则不会再执行
3.在外部程序调用生成resolver时,该往哪里写?
只能写在方法里,要么在MainActivit的onCreate()写一次,要么,每次在测试每个方法时都生成一次。不能在方法外部,直接写
ContentResolver resolver = getContext().getContentResolver();
因为执行过程是:.class-->.dex---->.apk--->安装---->开启进程(开启主线程)----->创建MainActivity对象----->setContext()---->onCreate()---->getContext()
所以再方法外部写resolver会得到空指针异常
下面是全部代码:
MyContentProvider:
package com.example.administrator.sqliteexecsql;
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.provider.SyncStateContract;
import android.util.Log;
/**
* Created by Administrator on 2015/7/24 0024.
*/
public class DBProvider extends ContentProvider{
private static final int PERSON =0;
private static final int STUDENT =1;
private static final int PERSON_ID=2;
DBOpenHelper myHelper;
UriMatcher matcher;
@Override
public boolean onCreate() {
//一些初始化的工作在这里面写
//注意,实例化DBOpenHelper时,传递的参数为数据库的名字,不是表名
//建表的工作在DBOpenHelper的OnCreate()方法里。
matcher = new UriMatcher(UriMatcher.NO_MATCH);
String authorities = "com.example.administrator.sqliteexecsql.provider";
matcher.addURI(authorities,"person",0);
matcher.addURI(authorities,"student",1);
matcher.addURI(authorities,"person/#",2);
myHelper = new DBOpenHelper(getContext(),"person.db",null,8);
return true;
}
@Override
public String getType(Uri uri) {
return null;
}
//插入方法,需要传进来两个参数
//db.indert会返回一个long 类型的id
//ContentUris.withAppendedId(uri,id);
//将得到的id和原来的uri凑在一起,返回一个uri(一个后面带有原来的path再加上)
@Override
public Uri insert(Uri uri, ContentValues values) {
SQLiteDatabase db = myHelper.getWritableDatabase();
long id= db.insert("person","id",values);
return ContentUris.withAppendedId(uri,id);
}
//int类型,返回的是删除的个数,如果是带有id的id的Uri,如果删除了,返回1
//不带id的uri,则返回实际删除了多少个
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
SQLiteDatabase db = myHelper.getWritableDatabase();
switch (matcher.match(uri)){
case PERSON_ID:
long id = ContentUris.parseId(uri);
selection = selection==null?"id="+id:selection+"AND id="+id;
case PERSON:
return db.delete("person",selection,selectionArgs);
default:
throw new RuntimeException("Uri cannot be identified");
}
}
//返回的是改变成功的个数
@Override
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
SQLiteDatabase db =myHelper.getWritableDatabase();
switch (matcher.match(uri)) {
case PERSON_ID:
long id = ContentUris.parseId(uri);
selection = selection == null ? "id=" + id : selection + "AND id=" + id;
case PERSON:
return db.update("person", values, selection, selectionArgs);
default:
throw new RuntimeException("Uri cannot be identified");
}
}
//查询方法,需要传入uri,可以传入列名,查询条件,Where后面占位符的值,以及排序方式
@Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
SQLiteDatabase db =myHelper.getReadableDatabase();
// Cursor c = db.query("person",null,null,null,null,null,null); 查询所有,所有列,无条件,无排序方式等。
//如果那边在调用Cursor query()方法时,需要有条件,则这边db.query()中需要加上相应的参数
//例如,那边要查balance>? orderBy为 “balance DESC”,那么这边就要
// Cursor c = db.query("person",null,selection,selectionArgs,null,null,sortOrder);
//但是,另一个问题出现了,就是这里写死了查person表,如果数据库里有多个表,如何指定查哪张表呢?
//在外部应用在使用本程序的数据时,查哪张表,别人可以在Uri指定path,本程序要做对应处理,如何处理?
//分析,外部应用,指定查哪张表的uri:content://com.example.provider/person 后面的person(path)即为表名
//我们只要在本程序中可以拿到后面的表名就可以设置逻辑判断,分析结束
//解决方法:UriMatcher 对象的addURI("authorities",path,code)方法,给每个表一个数字编码
//具体代码如下:
// matcher.addURI(authorities,"person",0);
// matcher.addURI(authorities,"student",1);
//matcher.addURI(authorities,"person/#",2);
//UriMatcher matcher = new UriMatcher(UriMatcher.NO_MATCH);
//authorities就写Manifest中配置的。下面两行将person表的编码设为0,student表的编码设为1
//设完之后如何判断?matcher.match(uri),可以返回code
//判断查哪张表的逻辑已经清晰了(UriMatcher-->matcher.addURI(,,,)-->matcher.match(uri)-->得到code)
//现在又另外一个问题,我想查当前表的具体哪一个id,当然可以再外部程序调用时加上where条件
//外部程序中Cursor c =resolver.query("person",null,"id=?",new String[]{"10"},null);
//但是太麻烦。我想把id=?去掉,即selection为null,不直接写条件
//想直接在Uri的person后面在加上一个id作为新的path
//例如content://com.example.provider/person/10查person表中id为10 的元素
//如何知道匹配uri后面加上person同时又跟上了id?--->matcher.addURI("","person/#",2);
//方法:addURI()的第二个参数写成person/#
//如何得到id ?--->>long id = ContentUris.parseId(uri);
//得到id之后?在本方法中将case 2方法写在case 0之前,不加break,将selection 改为selection ="id="+id;
//之后顺序执行,便可以返回一个Cursor对象
//问题又来了,我们在最开始查balance>?时,外部程序写的是:
// Cursor c = resolver.query("person",null,"balance>?",new String[]{5000+""},"balance DESC");
//也就是说此时传入的条件selection已经不为空了,此时有两个条件 balance>?AND id=10
//所以,要先判断selection是否为空(是不是只有id等于多少这一个条件)
//selection =selection==null?"id="+id:selection +"AND id="+id
//总结:addURI(,"person/#",)-->long id =ContentUris.parseId(uri)-->selection==null?
//另外一个细节,我们一般不写case 0 ,case 1等等,可以写成
//private static final int PERSON =0; case PERSON:。。。。
switch (matcher.match(uri)){
case PERSON_ID:
long id = ContentUris.parseId(uri);
selection =selection==null?"id="+id:selection+"AND id="+id;
case PERSON:
return db.query("person",null,selection,selectionArgs,null,null,sortOrder);
case STUDENT:
//查学生表
return null;
default:
throw new RuntimeException("URI不能识别");
}
}
}
外部程序调用:
package com.example.administrator.visitprovider;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.database.Cursor;
import android.net.Uri;
import android.support.v7.app.ActionBarActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;
public class MainActivity extends ActionBarActivity implements View.OnClickListener{
private Button query;
private Button insert;
private Button delete;
private Button update;
Uri uri = Uri.parse("content://com.example.administrator.sqliteexecsql.provider/person/10");
Uri uri1 = Uri.parse("content://com.example.administrator.sqliteexecsql.provider/person");
ContentResolver resolver;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
resolver =getApplicationContext().getContentResolver();
query = (Button) findViewById(R.id.query);
insert = (Button) findViewById(R.id.insert);
delete = (Button) findViewById(R.id.delete);
update = (Button) findViewById(R.id.update);
query.setOnClickListener(this);
insert.setOnClickListener(this);
delete.setOnClickListener(this);
update.setOnClickListener(this);
// resolver.insert(uri,new ContentValues());
}
//共四个按钮,分别是增删改查,访问上个程序的数据库,并且更改数据
@Override
public void onClick(View v) {
switch (v.getId()){
case R.id.query:
Cursor c = resolver.query(uri,null,"balance>?",new String[]{5000+""},"balance DESC");
while (c.moveToNext()){
Person p = new Person(c.getInt(0),c.getString(1),c.getInt(2));
System.out.println(p.toString());
}
break;
case R.id.insert:
ContentValues values = new ContentValues();
values.put("name","lee");
values.put("balance",10000);
uri1= resolver.insert(uri1,values);//调用DBProvider中的insert方法,插入并返回一个Uri
Toast.makeText(MainActivity.this,uri1+"",Toast.LENGTH_LONG).show();
break;
case R.id.delete:
//resolver.delete返回的是删除掉的个数,删除一个返回1,没找到需要删除的返回0,删除掉用带id的uri只能删除一个,下面换一个uri
//将path指定为person,删除大于5000的,此时便返回删除的个数
int deletedCount= resolver.delete(uri1,"balance >?",new String[]{5000+""});
Toast.makeText(MainActivity.this,deletedCount+"",Toast.LENGTH_LONG).show();
break;
case R.id.update:
ContentValues values1 = new ContentValues();
values1.put("name","lee");
values1.put("balance",30000);
int b = resolver.update(uri1,values1,"balance",new String[]{5000+""});
Toast.makeText(MainActivity.this,b+"",Toast.LENGTH_LONG).show();
}
}
}