【android,4】4.SQLite数据库,内容提供者、内容观察者
一、SQList 数据库的介绍:在Android平台上,集成了一个嵌入式关系型数据库—SQLite,SQLite3支持 NULL、INTEGER、REAL(浮点数字)、TEXT(字符串文本)和BLOB(二进制对象)数据类型,虽然它支持的类型只有五种,但实际上sqlite3也接受varchar(n)、char(n)、decimal(p,s) 等数据类型,只不过在运算或保存时会转成对应的五种数据类型。 SQLite最大的特点是你可以把各种类型的数据保存到任何字段中,而不用关心字段声明的数据类型是什么。例如:可以在Integer类型的字段中存放字符串,或者在布尔型字段中存放浮点数,或者在字符型字段中存放日期型值。但有一种情况例外:定义为INTEGER PRIMARY KEY的字段只能存储64位整数, 当向这种字段保存除整数以外的数据时,将会产生错误。另外, SQLite 在解析CREATE TABLE 语句时,会忽略 CREATE TABLE 语句中跟在字段名后面的数据类型信息,如下面语句会忽略 name字段的类型信息: 二、操作数据库:1、创建数据库具体要求: 自定义一个类继承系统的SQLiteOpenHelper类, 重写类的构造方法,参数如下。 数据库创建的目录:/data/当前应用程序/databases/数据库名称。 类中的onCreate 和 onUpgrade方法 使用说明在例中。 public class PersonDBOpenHelper extends SQLiteOpenHelper {
/** * 数据库被创建的构造方法, * 第一个参数 上下文 * 第二个参数 数据库的名称 * 第三个参数 数据库查询结果的游标工厂 一般用系统默认的游标工厂即可 * 第四个参数 数据库的版本号 版本号>=1 * @param context */ publicPersonDBOpenHelper(Context context) { super(context,"person.db",null,3); } /**当数据库第一次被创建的时候 会去执行 * * 当第一次获取数据库的实例的时候 才会执行 并且创建这个数据库 * 一旦数据库创建出来,不会再去执行oncreate方法 */ public void onCreate(SQLiteDatabasedb) { System.out.println("哈哈 数据库被创建了"); //执行sql语句,创建表。 db.execSQL("CREATE TABLE person (personid integer primarykey autoincrement,name varchar(20))");
} /** * 当数据库的版本增长的时候 会被调用 * 一般应用场景 就是我们需要去更改数据库的表结构 */ @Override public voidonUpgrade(SQLiteDatabase db,int oldVersion,int newVersion) { System.out.println("数据库版本变化了"); db.execSQL("ALTER TABLE person ADD phone VARCHAR(12) NULL"); } } 2、实例化数据库对象: //创建数据库类的对象 PersonDBOpenHelper dbhelper = new PersonDBOpenHelper(this); //此方法执行时,但数据不存在时,才会创建数据库,调用onCreate方法 dbhelper.getReadableDatabase(); 三、数据库的增删改查操作: 1、下面是一个dao类,其中的CRUD操作。public class PersonDao { private PersonDBOpenHelper helper; //构造函数传递一个上下文: public PersonDao(Context context) { helper = newPersonDBOpenHelper(context);//创建数据库类的对象。 } //crud 增删改查 //增加操作:public void add(String name,String phone){ if(find(name)){ return; } //得到可写的数据库实例 SQLiteDatabase db =helper.getWritableDatabase(); if(db.isOpen()){//判断数据库是否打开 //执行插入语句:参数(sql语句,sql语句中参数值) db.execSQL("insertinto person (name,phone) values (?,?)", new Object[]{name,phone}); db.close(); //养成习惯使用完毕后 关闭数据库 }
}
//查询的操作public boolean find(String name){ boolean result = false; //获取数据库只读的实例; SQLiteDatabase db = helper.getReadableDatabase(); if(db.isOpen()){ //获取结果集的游标 Cursor curosr =db.rawQuery("select * from person where name=?",new String[]{name}); if(curosr.moveToFirst()){ result = true; } curosr.close(); // 释放掉结果集的游标 db.close(); } return result; }
//更新 update//update person set phone='119' where name='zhangsan' andphone='120' public void update(String name,String phone,String newphone){ SQLiteDatabase db =helper.getWritableDatabase(); if(db.isOpen()){ db.execSQL("update person set phone=? where name=? andphone=?",new Object[]{newphone,name,phone}); db.close(); } }
//删除数据//delete from person where name='zhangsan' public void delete(String name){ SQLiteDatabase db =helper.getWritableDatabase(); if(db.isOpen()){ db.execSQL("delete from person where name=?",newObject[]{name}); db.close(); } }
//获取所有的数据public List<Person> findAll(){ List<Person>persons = new ArrayList<Person>(); SQLiteDatabase db = helper.getReadableDatabase(); if(db.isOpen()){ //获取游标对象, Cursor cursor = db.rawQuery("select * from person",null); while(cursor.moveToNext()){ //获取指定列名对应的值 int id = cursor.getInt(cursor.getColumnIndex("personid")); String name =cursor.getString( cursor.getColumnIndex("name")); String phone =cursor.getString( cursor.getColumnIndex("phone")); Person p = newPerson(id,phone); persons.add(p); } cursor.close(); db.close(); } return persons; }
} 2、比较数据库的可写实例:①、当需要更改数据时:获取可写实例 SQLiteDatabase db =helper.getWritableDatabase(); ②、当查询数据库时,获取只读实例: SQLiteDatabase db = helper.getReadableDatabase(); ③、两个方法的区别: 获取的数据实例相同, 可写实例操作数据库时,会加锁,操作完以后释放锁。同一时刻只能有一个可写实例操作数据库。 可读实例操作数据库时,可有1000千个可读实例同时操作数据库。 三、使用封装的api操作数据库://查询的操作//select * from person where name='zhangsan1' public boolean find(String name){ boolean result = false; SQLiteDatabase db = dbhelper.getReadableDatabase(); if(db.isOpen()){ //参数1:表名, //参数2:指定查询出来的列,为null时,返回所有列的信息, //参数3:指定查询的条件,为null是,返回所有记录。 //参数4:指定第三个参数中占位符的值 //参数5:分组的语句,group by 后面的值。 //参数6:分组的条件 ,having后面的值, //参数7:指定排序. Cursor curosr = db.query("person","name=?", new String[]{name},null); if(curosr.moveToFirst()){ result = true; } curosr.close(); // 释放掉结果集的游标 db.close(); } return result; } //增加操作:public boolean add(String name,int account){ long result=0; if(find(name)){ return false; }
SQLiteDatabase db =dbhelper.getWritableDatabase(); if(db.isOpen()){ //实质为map集合 ContentValues values = newContentValues(); values.put("name",name);//参数(列名,值) values.put("phone",phone); values.put("account",account); //执行插入操作:参数(表名,当插入的所有值为null时防止拼接的sql语句出错的值, 要插入数据的键值对) result = db.insert("person",values); db.close(); //养成习惯使用完毕后 关闭数据库 } //insert 方法的返回值为-1时,表示插入失败。 if(result==-1){ return false; }else{ return true; } } //更新 update//update person set phone='119' where name='zhangsan' andphone='120' public boolean update(String name,String newphone){ boolean result = false; SQLiteDatabase db =dbhelper.getWritableDatabase(); if(db.isOpen()){
ContentValues values = new ContentValues(); values.put("name",name); values.put("phone",newphone); //更行操作: //参数1:表名, //参数2:更新的数据 //参数3: where后面的选择条件 //参数4:选择条件中的值。 int raw = db.update("person",values,"name=? and phone=?",new String[]{name,phone}); //update 方法,返回操作的记录条数。 if(raw>0){ result = true; } db.close(); } return result; } //删除数据//delete from person where name='zhangsan' public boolean delete(String name){ boolean result = false; SQLiteDatabase db =dbhelper.getWritableDatabase(); if(db.isOpen()){ //删除操作,参数(表名,查询条件,查询条件中的值) int raw = db.delete("person",newString[]{name}); if(raw>0){ result = true; } db.close(); } return result; } //获取所有的数据public List<Person> findAll(){ List<Person>persons = new ArrayList<Person>(); SQLiteDatabase db = dbhelper.getReadableDatabase(); if(db.isOpen()){ //查询获取游标对象: //参数1:表名, //参数2:指定查询出来的列,为null时,返回所有列的信息, //参数3:指定查询的条件,为null是,返回所有记录。 //参数4:指定第三个参数中占位符的值 //参数5:分组的语句,group by 后面的值。 //参数6:分组的条件 ,having后面的值, //参数7:指定排序. Cursor cursor =db.query("person",null); while(cursor.moveToNext()){ int id = cursor.getInt(cursor.getColumnIndex("personid")); String name =cursor.getString( cursor.getColumnIndex("name")); String phone = cursor.getString( cursor.getColumnIndex("phone")); Person p = newPerson(id,phone,100); persons.add(p); } cursor.close(); db.close(); } return persons; } 四、sqlite中的事务:android中默认是不开启事务。 使用SQLiteDatabase的beginTransaction()方法可以开启一个事务,程序执行到endTransaction()方法时会检查事务的标志是否为成功,如果程序执行到endTransaction()之前调用了setTransactionSuccessful() 方法设置事务的标志为成功则提交事务,如果没有调用setTransactionSuccessful() 方法则回滚事务。 例: public voidtestTransaction() throws Exception { PersonDbOpenHelperhelper = new PersonDbOpenHelper(getContext()); SQLiteDatabase db =helper.getWritableDatabase(); db.beginTransaction();//开始事务 try { // 王五的账户减去100块钱 db.execSQL("update person set account=account-100 where name='wangwu'"); // 李四2的账户里面增加100块钱 db.execSQL("update person set account=account+100 where name='lisi2'"); db.setTransactionSuccessful(); //判断事务是否执行成功 } finally { db.endTransaction();//结束事务 db.close(); } } 五、内容提供者:ContentProvider1、用于解决不同应用之间共享数据库的问题。当其他程序要访问A程序的数据库,需要通过A程序上的内容提供者类,操作数据库。 内容提供中提供类CRUD 的方法。 2、定义ContentProvider的使用:①、自定义一个内容提供这类,继承ContentProvider类, public class PersonProvider extends ContentProvider { private static final intALL_PERSONS = 1; private static final intPERSON = 2; private static final intWANGWU = 3; private static final intINSERT = 4; private static final intDELETE = 5; private static final intUPDATE = 6;
private static Uribaseuri = Uri.parse("content://cn.itcast.db.personprovider/"); private PersonDao dao; // 创建一个uri的匹配器,如果匹配器没有找到对应的uri的类型 就返回 -1 private static UriMatchermather = new UriMatcher(UriMatcher.NO_MATCH); static { //addURI方法参数: 参数1:uri的主机名,取值为内容提供者在清单文件中配置的authorities属性对应的值, 参数2:匹配路径, 参数:匹配结果 mather.addURI("cn.itcast.db.personprovider","persons",ALL_PERSONS); // cn.itcast.db.personprovider/persons 代表的是返回所有的person的信息 mather.addURI("cn.itcast.db.personprovider","person/#",PERSON); // cn.itcast.db.personprovider/person/5 代表的是在person表中id为5的那个人的信息 mather.addURI("cn.itcast.db.personprovider","wangwu",WANGWU); // cn.itcast.db.personprovider/wangwu 代表的是王五的信息 // 根据业务逻辑定义uri mather.addURI("cn.itcast.db.personprovider","insert",INSERT); // cn.itcast.db.personprovider/insert/ 代表的是往数据库里面添加一个信息 mather.addURI("cn.itcast.db.personprovider","delete",DELETE); // cn.itcast.db.personprovider/delete/ 代表的是往数据库里面删除一个信息 mather.addURI("cn.itcast.db.personprovider","update",UPDATE); // cn.itcast.db.personprovider/update/ 代表的是往数据库里面更新一个信息 } /** * 在PersonProvider 第一次被创建的时候执行 */ @Override public boolean onCreate(){ dao = new PersonDao(getContext()); return false; } // 返回cursor代表的数据的类型 @Override public String getType(Uriuri) { //告诉调用者 返回的数据是一个集合 还是单一的一个条目 // .jpg mime // .MP3 // .rmvb int type =mather.match(uri); switch (type) { case ALL_PERSONS: //vnd.android.cursor.dir/ return"vnd.android.cursor.dir/persons"; case PERSON: return"vnd.android.cursor.item/person"; } return null; } /** * 通过uri 来表示要操作的数据 的路径 */ @Override public Cursor query(Uriuri,String[] projection,String selection, String[] selectionArgs,String sortOrder) { int type = mather.match(uri); switch (type) { // 如果地址匹配 cn.itcast.db.personprovider/persons case ALL_PERSONS: if (selection != null&& selectionArgs != null) { return dao.find(selection,selectionArgs); } else { return dao.getCursor(); } case PERSON: // cn.itcast.db.personprovider/person/# //获取uri中最后的/后面的值,对应:请求的id。 long id =ContentUris.parseId(uri); returndao.findPersonById(id); case WANGWU: // cn.itcast.db.personprovider/wangwu return dao.findWangwu(); default: throw newIllegalArgumentException("uri 不能被识别"); } } @Override public Uri insert(Uri uri,ContentValues values) { if (mather.match(uri) == INSERT) { long id =dao.add(values); //content://cn.itcast.db.personprovider/person/id //内容观察者 getContext().getContentResolver().notifyChange(baseuri,null);
returnContentUris.withAppendedId( Uri.parse("content://cn.itcast.db.personprovider/person/"), id); } else { throw newIllegalArgumentException("uri 不能被识别"); } } @Override public int delete(Uriuri,String[] selectionArgs) { if (mather.match(uri) == DELETE) { boolean result =dao.delete(selection); getContext().getContentResolver().notifyChange(baseuri,null); if (result) { return 1; } else { return -1; } } else { throw newIllegalArgumentException("uri 不能被识别"); } } @Override public int update(Uriuri,ContentValues values, String[] selectionArgs){
if (mather.match(uri) == UPDATE) { if (selectionArgs.length== 3) { getContext().getContentResolver().notifyChange(baseuri,null); boolean result =dao.update(selectionArgs[0],selectionArgs[1], selectionArgs[2]); if (result) { return 1; } else { return -1; } }else{ throw new IllegalArgumentException("更新的参数传递不正确"); } } else { throw newIllegalArgumentException("uri 不能被识别"); } } } ②、contentprovider要在清单文件中声明。 activity 是应用程序的组件 要在清单文件里面声明 contentprovider 也是应用程序的组件 要在清单文件里面声明: 在AndroidManifest.xml文件的application元素中声明。 <application android:icon="@drawable/ic_launcher" android:label="@string/app_name"> <uses-libraryandroid:name="android.test.runner" /> //name 对应的是内容提供者的类路径 <providerandroid:name=".provider.PersonProvider" //指定数据所在的地址。内容提供者的地址,一般为应用程序包名+内容提供者类名。 android:authorities="cn.itcast.db.personprovider" ></provider>
</application> 3、在其他程序中调用上面的内容提供者: // 首先获取内容提供者的解析器 ContentResolver resolver =getContext().getContentResolver(); //定义访问内容提供者的uri 对象 Uri uri = Uri.parse("content://cn.itcast.db.personprovider/haha"); //调用内容提供者类中的query方法 Cursor cursor = resolver.query(uri,null); 六、内容提供者的特点:1. 暴露自己应用程序私有的数据给别的应用程序 2. 屏蔽底层数据存储的细节,调用者不需要关心数据是怎么存储的, 只需要关心操作哪个uri,操作的数据是什么. 3. 内容提供不仅仅可以操作数据库,也可以操作别的文件,sp,网络. 4.内容提供者 还可以根据业务需求,只去增删改查中的某个方法. 七、内容观察者:检测内容提供者中的CRUD 方法是否被调用。1、在内容提供者的CRUD 方法中添加如下代码: private static Uri baseuri =Uri.parse("content://cn.itcast.db.personprovider/"); //notifyChange方法参数1:当内容提供者中的访问被调用时,向该参数指定的uri上发消息 参数2:内容观察者对象。 getContext().getContentResolver().notifyChange(baseuri,null); 2、在另外程序中,获取内容观察者: registerContentObserver方法的参数1:对应观察者绑定的信息。 参数2:为true时表示观察者会观察uri及其子目录的信息的改变 参数3:是往baseuri 上注册的内容观察者对象。 getContentResolver().registerContentObserver(baseuri,true,newMyObserver(new Handler())); //定义一个内容观察者类;继承ContentObserver类。 private class MyObserver extends ContentObserver{ public MyObserver(Handler handler) { super(handler); } //当上面的内容提供者的方crud方法被被调用,就会触发调用该方法。 public void onChange(boolean selfChange) { super.onChange(selfChange); System.out.println("数据发生改变啦"); }
} 八、短信监听器:1、系统的短信的应用在com.android.mms 短信的内容 是存放在一个com.android.providers.telephony 目录下的mmsms.db里。 当mmsms.db 数据库中的内容发生改变时,就获取改变的信息。 在com.android.providers.telephony程序中已经注册了一个内容观察者。只需要获取所绑定的uri。 该uri 为 content://sms/ 。所以只要观察该uri即可做一个短信监听器。 2、注意:短信监听器操作短信时需要读短信和写短信的权限: <uses-permissionandroid:name="android.permission.READ_SMS"/> <uses-permissionandroid:name="android.permission.WRITE_SMS"/> 3、短息监听者的代码: Uri uri =Uri.parse("content://sms/"); //注册观察者 getContentResolver().registerContentObserver(uri,new MyObserver( new Handler())); //定义一个观察者类 private class MyObserver extendsContentObserver{ public MyObserver(Handlerhandler) { super(handler); // TODO Auto-generated constructor stub } @Override public voidonChange(boolean selfChange) { super.onChange(selfChange); System.out.println("短信内容发生改变啦"); //通过内容提供者获取数据。 Query方法参数1:uri类型, 参数2:要查询的列,为null时,表示所有列 参数3:查询条件, 参数4:查询条件里的参数值, 参数5:排序字段 Cursor cursor = getContentResolver().query(Uri.parse("content://sms/"),"date desc"); if(cursor.moveToFirst()){ String body =cursor.getString( cursor.getColumnIndex("body")); System.out.println(body); } } } } 九、利用内容提供者获取联系人数据库:1、联系人的信息是存放在com.android.providers.contacts 应用下的databases/contacts2.db数据库里 2、contacts2.db数据库中三张重要的表: ①、raw_contacts 表: 字段:主键 _id,display_name ②、data表 字段:raw_contacts_id 关联raw_contacts 表中的_id. data1字段 存放联系人的信息。 mimetype_id 关联:mimetype表中的主键_id. ③、mimetype表 :该表为数据类型表, 字段_id 主键,mimetype:存放的是数据类型。 当添加一个联系人时就在raw_contacts表中添加一条记录,_id 主键,display_name字段存放联系人的名称。 data表中添加三条信息 mimetype_id对应mimetype表中的主键。raw_contacts_id 对应raw_contacts表中主键_id. 3、查询表的步骤: ①、查询raw_contacts表 获取_id,通过_id关联data表中raw_contacts_id,查询出data表中的 mimetype_id,data1.再通过mimetype_id 关联mimetype表 的_id获取 mimetype对应的类型。 4、获取数据可中联系人的信息: 注意读取联系人的信息需要读的权限: <uses-permissionandroid:name="android.permission.READ_CONTACTS"/> //由源代码可知raw_contacts表的uri,获取内容提供者。 Uri uri = Uri.parse("content://com.android.contacts/raw_contacts"); //获取结果集。 Cursor cursor = getContext().getContentResolver().query(uri,null); while (cursor.moveToNext()) { //获取id String id =cursor.getString( cursor.getColumnIndex("_id")); String name = cursor.getString(cursor.getColumnIndex("display_name")); //System.out.println(id); System.out.println("姓名"+ name); //System.out.println("--"); //获取data表的uri Uri dataUri =Uri.parse("content://com.android.contacts/data"); //通过条件raw_contacts.id = data.raw_contact_id获取结果集。 Cursor datacursor =getContext().getContentResolver().query( dataUri,"raw_contact_id=?",new String[]{id},null); while(datacursor.moveToNext()) { //判断mimetype 等于vnd.android.cursor.item/phone_v2表示数据为电话号码。 if("vnd.android.cursor.item/phone_v2".equals( datacursor.getString(datacursor.getColumnIndex("mimetype")))){ System.out.println( "电话"+datacursor.getString(datacursor.getColumnIndex("data1"))); //类型email }else if("vnd.android.cursor.item/email_v2".equals( datacursor.getString(datacursor.getColumnIndex("mimetype")))){ System.out.println( "邮箱"+datacursor.getString(datacursor.getColumnIndex("data1"))); }
}datacursor.close(); } cursor.close(); 5、向数据库中插入联系人信息: ①、插入操作:先向raw_contacts表中插入一条记录 ,然后再向data表中插入数据。 public void insertContacts(){ //获取raw_contacts的uri Uri uri =Uri.parse("content://com.android.contacts/raw_contacts"); //创建插入到表中的ContentValue ContentValues values = new ContentValues(); values.put("display_name","zhaoba"); Uri inserturi = getContext().getContentResolver().insert(uri,values); //得到插入的数据 在数据库中的_id long id = ContentUris.parseId(inserturi); //data表的uri Uri dataUri =Uri.parse("content://com.android.contacts/data"); //插入电话号码 ContentValues phonevalues = new ContentValues(); phonevalues.put("data1","7777"); phonevalues.put("raw_contact_id",id); phonevalues.put("mimetype","vnd.android.cursor.item/phone_v2");// getContext().getContentResolver().insert(dataUri,phonevalues);
//插入email ContentValues emailvalues = new ContentValues(); emailvalues.put("data1","77777@itcast.cn"); emailvalues.put("raw_contact_id",id); emailvalues.put("mimetype","vnd.android.cursor.item/email_v2"); getContext().getContentResolver().insert(dataUri,emailvalues);
//插入联系人姓名 ContentValues namevValues = new ContentValues(); namevValues.put("mimetype","vnd.android.cursor.item/name"); namevValues.put("raw_contact_id",id); namevValues.put("data1","老方");
getContext().getContentResolver().insert(dataUri,namevValues); } } 注意:插入联系人需要写联系人的权限. <uses-permissionandroid:name="android.permission.WRITE_CONTACTS"/> (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |