上一篇文章Android持久化存储的几种方式已经讲诉了Android开发中文件存储、网络存储和SharedPreferences这三种持久化存储的方式,以及大致讲了什么是ContentProvider和它的使用场景。那么这篇文章我们就来具体讲诉一下如何使用ContentProvider与sqlit搭配,实现与一个APP对另一个APP的数据增删改查。
首先我们使用SQLiteOpenHelper这个类来创建一个数据库,并创建一个储存个人信息的People_Message的表。
public class MyDatabaseHelper extends SQLiteOpenHelper {
//sql语句,用来创建表。
public static final String People_Message="create table people("
+"num integer primary key autoincrement,"
+"name text,"
+"sex text)";
Context mContext;
public MyDatabaseHelper(Context context, String name, SQLiteDatabase.CursorFactory factory, int version) {
//调用父类构造方法,创建数据库。第一个参数是context。第二个参数是数据库的名字,第三个参CursorFactory指定在执行查询时获得一个游标实例的工厂类,设置为null,代表使用系统默认的工厂类,第四个参数是数据库的版本号,不能小于一。
super(context, name, factory, version);
mContext=context;
}
@Override
public void onCreate(SQLiteDatabase db) {
//创建表,执行sql语句
db.execSQL(People_Message);
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
//如果需要升级数据库,会调用该方法。可以用来删除表或者添加表。
}
}
上面是一个继承了SQLiteOpenHelper类的类。在构造方法中会调用父类构造方法,创建一个数据库。然后在onCreate中使用 db.execSQL()这个方法执行sql语句,进行表创建。
然后我们需要在主线程中创建一个MyDatabaseHelper对象,这样就创建好了数据库和表(如果数据库和表已经存在,将不会创建)。
mMyDatabaseHelper=new MyDatabaseHelper(getApplicationContext(),"people_message",null,1);
然后我们需要得到一个SQLiteDatabase 对象,这个对象可以用来操作数据库。
//得到可以读写数据库的SQLiteDatabase
db=mMyDatabaseHelper.getWritableDatabase();
//得到只读数据库的SQLiteDatabase
//db=mMyDatabaseHelper.getReadableDatabase();
上面代码可以,我们可以通过刚才创造的SQLiteOpenHelper对象来获取SQLiteDatabase。其中有两种获取方式,一个可以对数据库进行写入,另一个只读。
SQLiteDatabase这个对象中有几个方法,是对修改数据库的几种方式进行了包装。
从数据库中查询数据
db.query(String table,String[] columns,String selection,String[] selectionArgs,String groupBy,String having,String orderBy,String limit)
参数意义:
table:表名
columns:需要得到的列名数组
selection:条件字句,相当于where
selectionArgs:条件字句,条件的数组
groupBy:分组列名
having:分组条件
orderBy:排序列名
limit:查询限制
往数据库中插入数据
db.insert(String table,String nullColumnHack,ContentValues values)
参数意义:
table:表名
nullColumnHack:空列的默认值
values:插入的数据,key-value类型的集合
把数据库中指定的数据删除
db.delete(String table,String whereClause,String[] whereArgs)
参数意义:
table:表名
whereClause:删除条件
whereArgs:删除条件值数组
修改数据库中指定的数据
db.update(String table,ContentValues values,String whereClause, String[] whereArgs)
参数意义:
table:表名
values:修改的数据,key-value类型的集合
whereClause:修改条件
whereArgs:修改条件值数组
执行具体的sql语句
db.execSQL(“sql语句”)
db.XXXX的方法还有许多,如果上面的不能满足你的要求,你可以看看源码,选择自己需要的方法。
上述方法中都有很多参数,除了db.execSQL()中的参数是你需要执行的sql语句外,其他的参数都有很多,这里不细讲,使用的时候大家可以再学习。
上面我们已经建立了一个数据库并且创建了一个表。下面就是需要对表进行操作了。这里我们使用ContentProvider来对表进行操作。
我们创建一个类继承与ContentProvider,并在AndroidManifast中进行注册。
public class MyContentProvider extends ContentProvider
{
public MyContentProvider()
{
}
@Override
public int delete(Uri uri, String selection, String[] selectionArgs)
{
// Implement this to handle requests to delete one or more rows.
throw new UnsupportedOperationException("Not yet implemented");
}
@Override
public String getType(Uri uri)
{
// TODO: Implement this to handle requests for the MIME type of the data
// at the given URI.
throw new UnsupportedOperationException("Not yet implemented");
}
@Override
public Uri insert(Uri uri, ContentValues values)
{
// TODO: Implement this to handle requests to insert a new row.
throw new UnsupportedOperationException("Not yet implemented");
}
@Override
public boolean onCreate()
{
// TODO: Implement this to initialize your content provider on startup.
return false;
}
@Override
public Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder)
{
// TODO: Implement this to handle query requests from clients.
throw new UnsupportedOperationException("Not yet implemented");
}
@Override
public int update(Uri uri, ContentValues values, String selection,
String[] selectionArgs)
{
// TODO: Implement this to handle requests to update one or more rows.
throw new UnsupportedOperationException("Not yet implemented");
}
}
上面代码可以看见,除了无参构造函数之外,还会自动生成几个重写方法,这几个方法就是上面我们所说的增删改查几个方法,我们可以在对应函数中就可以自己编写对数据库的操作了。
我们发现上面的几个方法中第一个参数类型都是Uri,这个参数代表要操作的数据。我们先来简单说一下Uri的构造,理解一下Uri具体的用法。
uri及基本结构是 [scheme:][//authority][path][?query][#fragment] 这个五个部分,但是除了[scheme:][//authority]必须有之外,其他的每一个部分都可以不需要。但是顺序不能改变。
拿一个例子来说
uri=http://www.java2s.com:8080/yourpath/fileName.htm?stove=10&path=32&id=4#harvic
对于这个uri来说,我们来解析一下它的各部分结构。
[scheme:]=http 在//之前都属于[scheme:]
[//authority]=www.java2s.com:8080 主机名 //之后[path]之前
[path]=yourpath/fileName.htm 具体的路径 主机名之后[?query]之前都属于[path]
[?query]=?stove=10&path=32&id=4 ?号之后 #之前
[#fragment]=#harvic #之后都属于[#fragment]
(上面只是对Uri结构的简单的介绍,如果详细了解可以点击Uri详解之——Uri结构与代码提取查看)
上面就是Uri的具体结构,我们通过具体的Uri就可以访问我们需要访问的数据了,对于ContentProvider来说,用来访问它的Uri有具体的格式。
[scheme:]=content
[//authority]=主机名,在AndroidManifest文件中可以对具体ContentProvider的authorities属性进行修改和查看,这里我们上面定义ContentProvider的authorities为”test_contentProvider”。
<provider
android:name=".contentProvider.MyContentProvider"
android:authorities="test_contentProvider"
android:enabled="true"
android:exported="true">
provider>
[path]就是我们表的名字,在这个事例程序中就是”people”。
所以这个ContentProvider的指定Uri就是
Uri PEO=Uri.parse("content://test_contentProvider/people");
那么我们来看看完整的MyContentProvider类应该是怎样的
public class MyContentProvider extends ContentProvider
{
MyDatabaseHelper mMyDatabaseHelper;
SQLiteDatabase db;
UriMatcher sMatcher;
Uri PEO=Uri.parse("content://test_contentProvider/people");
public MyContentProvider()
{
}
//用于匹配传入的URI,如果不匹配返回-1
public int getUriMatcher(Uri uri){
return sMatcher.match(uri);
}
@Override
public int delete(Uri uri, String selection, String[] selectionArgs)
{
//删除数据
if(getUriMatcher(uri)==1){
db.delete("people",selection,selectionArgs);
getContext().getContentResolver().notifyChange(PEO,null);
return 1;
}
return 0;
}
@Override
public String getType(Uri uri)
{
// TODO: Implement this to handle requests for the MIME type of the data
// at the given URI.
throw new UnsupportedOperationException("Not yet implemented");
}
@Override
public Uri insert(Uri uri, ContentValues values)
{
//判断传入的URI是否匹配,匹配的话将数据插入数据库
if (getUriMatcher(uri)==1){
db.insert("people",null,values);
getContext().getContentResolver().notifyChange(PEO,null);
}
return null;
}
@Override
public boolean onCreate()
{
//打开数据库,得到db
mMyDatabaseHelper=new MyDatabaseHelper(getContext(),"data",null,1);
//得到可以读写数据库的SQLiteDatabase
db=mMyDatabaseHelper.getWritableDatabase();
//UriMatcher.NO_MATCH表示不匹配任何Uri的返回码-1
sMatcher=new UriMatcher(UriMatcher.NO_MATCH);
//添加一个匹配规则:第一个参数contentProvider的authority值,第二个参数数据表名,第三个参数 匹配后返回值
sMatcher.addURI("test_contentProvider","people",1);
return false;
}
@Override
public Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder)
{
//查询数据
if (getUriMatcher(uri)==1){
Cursor cursor=db.query("people",null,selection,selectionArgs,null,null,sortOrder);
return cursor;
}
return null;
}
@Override
public int update(Uri uri, ContentValues values, String selection,
String[] selectionArgs)
{
//更新数据
if (getUriMatcher(uri)==1){
db.update("people",values,selection,selectionArgs);
getContext().getContentResolver().notifyChange(PEO,null);
return 1;
}
return 0;
}
1.首先我们在onCreate构造函数中,创建或打开要操作的数据库,得到用来操作数据库的db。
2.接着我们为new出一个UriMatcher对象,给这个对象添加一个URi(可以同时添加多个,只要返回码不一样),这个Uri就是用来访问当前MyContentProvider的指定Uri,通过对ContentProvider时传入的URI进行校验,判断是否可以能够匹配,根据匹配的返回码,得知具体需要操作的数据。
3.在增删改查的四个方法中,进行具体的数据操作,这篇文章主要是对数据库的增删改查操作,所以直接使用db.xxxx()方法来进行操作。
4.在增删改完成之后,如果对数据进行了修改,使用getContext().getContentResolver().notifyChange(PEO,null)这段代码,对指定的URI添加一个通知,通知当前URI的数据有修改,便于ContentObserver进行监听。
上面就构造出了一个自定义的ContentProvider。它的指定Uri”content://test_contentProvider/people”。
我们初始化数据库,向数据库中插入十条数据,便于显示和查询。
public class PeopleActivity extends AppCompatActivity
{
MyDatabaseHelper mMyDatabaseHelper;
SQLiteDatabase db;
MyContentProvider mMyContentProvider;
ListView mListView;
List mPeopleBeens;
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_people);
mListView=(ListView)findViewById(R.id.list);
mPeopleBeens=new ArrayList<>();
mMyDatabaseHelper=new MyDatabaseHelper(getApplicationContext(),"data",null,1);
//得到可以读写数据库的SQLiteDatabase
db=mMyDatabaseHelper.getWritableDatabase();
PeopleBeen p;
for(int i=0;i<10;i++){
p=new PeopleBeen();
p.setNum(10+i);
p.setName("王"+i);
p.setSex(((i%3==0)?"女":"男"));
ContentValues values = new ContentValues();
values.put("num", p.getNum());
values.put("name", p.getName());
values.put("sex",p.getSex());
db.insert("people",null, values);
}
Cursor cursor=db.query("people",null,null,null,null,null,null);
while (cursor.moveToNext()){
PeopleBeen pp=new PeopleBeen();
pp.setNum(cursor.getInt(cursor.getColumnIndex("num")));
pp.setName(cursor.getString(cursor.getColumnIndex("name")));
pp.setSex(cursor.getString(cursor.getColumnIndex("sex")));
mPeopleBeens.add(pp);
}
//mMyContentProvider=new MyContentProvider();
mListView.setAdapter(new ArrayAdapter(this, android.R.layout.simple_expandable_list_item_1, getData()));
}
private List getData(){
List data = new ArrayList();
for(PeopleBeen p:mPeopleBeens){
data.add(p.getNum()+" 名字:"+p.getName()+" 性别:"+p.getSex());
}
return data;
}
@Override
protected void onDestroy()
{
super.onDestroy();
db.close();;
}
}
现在我们新建一个项目,在另一个项目中使用这个ContentProvider。
public class MainActivity extends AppCompatActivity implements View.OnClickListener
{
EditText ed_num;
EditText ed_name;
EditText ed_sex;
Button bt_cx;
Button bt_cr;
Button bt_xg;
Button bt_sc;
TextView tv_jg;
ContentResolver mContentResolver;
PersonOberserver personOberserver;
Uri PEO=Uri.parse("content://test_contentProvider/people");
Handler mHandler=new Handler(){
@Override
public void handleMessage(Message msg)
{
super.handleMessage(msg);
Toast.makeText(getApplication(),"修改成功",Toast.LENGTH_LONG).show();
}
};
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ed_num=(EditText)findViewById(R.id.et_num);
ed_name=(EditText)findViewById(R.id.et_name);
ed_sex=(EditText)findViewById(R.id.et_sex);
bt_cx=(Button)findViewById(R.id.bt_cx);
bt_cr=(Button)findViewById(R.id.bt_cr);
bt_xg=(Button)findViewById(R.id.bt_xg);
bt_sc=(Button)findViewById(R.id.bt_sc);
tv_jg=(TextView)findViewById(R.id.tv_jg);
bt_cx.setOnClickListener(this);
bt_cr.setOnClickListener(this);
bt_xg.setOnClickListener(this);
bt_sc.setOnClickListener(this);
//初始化ContentResolver,用来与ContentProvider建立联系
mContentResolver=getContentResolver();
//初始化ContentOberserver,用来监听指定URI数据的改变。
personOberserver=new PersonOberserver(mHandler);
//注册监听,当指定URI发生改变时,personOberserver中的onChange()会得到执行。
mContentResolver.registerContentObserver(PEO,true,personOberserver);
}
@Override
public void onClick(View view)
{
switch (view.getId()){
case R.id.bt_cr:
//插入数据
ContentValues values = new ContentValues();
values.put("num", Integer.parseInt(ed_num.getText().toString()));
values.put("name", ed_name.getText().toString());
values.put("sex",ed_sex.getText().toString());
mContentResolver.insert(PEO,values);
break;
case R.id.bt_cx:
//根据编号查询数据
tv_jg.setText("");
Cursor cursor = mContentResolver.query(PEO, null, "num=?", new String[]{ed_num.getText().toString()}, null);
while (cursor.moveToNext()){
tv_jg.setText("编号:"+cursor.getInt(cursor.getColumnIndex("num"))+" 姓名:"+cursor.getString(cursor.getColumnIndex("name"))+" 性别:"+cursor.getString(cursor.getColumnIndex("sex")));
}
break;
case R.id.bt_sc:
//根据编号删除数据
int result=mContentResolver.delete(PEO,"num=?",new String[]{ed_num.getText().toString()});
break;
case R.id.bt_xg:
//根据编号修改数据
ContentValues values_xg = new ContentValues();
values_xg.put("name", ed_name.getText().toString());
values_xg.put("sex", ed_sex.getText().toString());
mContentResolver.update(PEO,values_xg,"num=?",new String[]{ed_num.getText().toString()});
break;
}
}
@Override
protected void onDestroy()
{
super.onDestroy();
mContentResolver.unregisterContentObserver(personOberserver);
}
}
public class PersonOberserver extends ContentObserver
{
Handler mHandler;
public PersonOberserver(Handler handler)
{
super(handler);
mHandler=handler;
}
@Override
public void onChange(boolean selfChange)
{
Log.e("dd","dd");
Message message=new Message();
mHandler.sendMessage(message);
}
}
我们看上面的代码,首先在onCreate周期函数中,初始化了ContentResolver,ContentResolver叫做内容解析者,当外部应用需要对ContentProvider中的数据进行添加、删除、修改和查询操作时,可以使用ContentResolver 类来完成。ContentResolver 中也拥有insert(),delete(),update(),query()这四种方法,里面的参数和数据库操作时使用的增删改查方法的参数差不多。这四种方法对应的就是ContentProvider的四种数据操作方法
接着初始化了PersonOberserver对象,这个对象是一个可以用来监听特定URI数据变化的对象。我们用registerContentObserver方法来给ContentResolver注册一个PersonOberserver,这样,当指定URI的数据发生改变时,PersonOberserver中的onChange方法就会得到调用。
我们为四个button绑定了各自的监听事件,在监听事件中,我们使用了mContentResolver.xxxxx()方法来进行增删改查,它们的参数中都有一个URI参数,而这个URI参数就是找到指定ContentProvider的标识。有了这个URI,我们才能通过ContentResolver去执行指定ContentProvider中的方法,并得到相应的数据。