【android,4】4.SQLite数据库,内容提供者、内容观察者

来源:互联网 发布:淘宝棒球服外套女 编辑:程序博客网 时间:2024/06/05 03:43

一、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, name, 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",null, "name=?",

new String[]{name}, null, null, null);

            if(curosr.moveToFirst()){

                result = true;

            }

            curosr.close(); // 释放掉结果集的游标

            db.close();

        }

        return result;

    }

//增加操作:

    public boolean add(String name,String phone,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", null, 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 phone,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", "name=?", 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, null, null, null, null, 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, name, 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();

        }

 

    }

 

五、内容提供者:ContentProvider

1、用于解决不同应用之间共享数据库的问题。当其他程序要访问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 selection, 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 selection,

            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, null, null, 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,true, 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/"),null, null, null, "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, null, null, 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, null,"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"/>

0 0
原创粉丝点击