Android基础学习__第3天__SQLite与数据处理
2013-10-22 20:14
405 查看
1.SQLite
在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字段的类型信息:
CREATE TABLE person (personid integer primary key autoincrement, name varchar(20))
SQLite可以解析大部分标准SQL语句,如:
查询语句:select * from 表名 where 条件子句 group by 分组字句 having ... order by 排序子句
如:select * from person
select * from person order by id desc
select name from person group by name having count(*)>1
分页SQL与mysql类似,下面SQL语句获取5条记录,跳过前面3条记录
select * from Account limit 5 offset 3 或者 select * from Account limit 3,5
插入语句:insert into 表名(字段列表) values(值列表)。如: insert into person(name, age) values(‘传智’,3)
更新语句:update 表名 set 字段名=值 where 条件子句。如:update person set name=‘传智‘ where id=10
删除语句:delete from 表名 where 条件子句。如:delete from person where id=10
SQLiteOpenHelper抽象类:数库帮助类
要使用数据库,就要先创建出数据库,第二步就要创建表结构。使用SQLiteOperHelper类创建数据库,示例如下:public class PersonDBOpenHelper extends SQLiteOpenHelper { public PersonDBOpenHelper(Context context) { //context 上下文 //name 数据库的名称 //factory 数据库查询结果的游标工厂,使用null会使用系统默认 //version 数据库的版本 >=1 super(context, "person.db", null, 3); } /** * 数据库第一次被创建的时候调用的方法 * @param db 被创建的数据库 */ @Override public void onCreate(SQLiteDatabase db) { db.execSQL("create table person (id integer primary key autoincrement , name varchar(20), number varchar(20))"); } @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { } }
注意:实例化PersonDBOpenHelper类是不会创建数据库的,要使用这个类的对象去调用getWritableDatabase()或者getReadableDatabase()方法之后,数据库才会创建。
数据库路径:data/data/包名/databases/数据库名称
使用SQLiteOpenHelper及系统Api对数据库的增删改查
public class PersonDao2 { private PersonDBOpenHelper helper; public PersonDao2(Context context) { helper = new PersonDBOpenHelper(context); } //添加 public boolean add(String name, String phone) { SQLiteDatabase db = helper.getWritableDatabase(); // db.execSQL("insert into person (name,phone) values (?,?)", new Object[]{name,phone}); ContentValues values = new ContentValues(); values.put("name", name); values.put("phone", phone); long id = db.insert("person", null, values); db.close(); return id != -1; } //查找 public boolean find(String name) { SQLiteDatabase db = helper.getReadableDatabase(); //Cursor cursor = db.rawQuery("select * from peron where name =?", new String[]{name}); Cursor cursor = db.query("person", null, "name=?", new String[] { name }, null, null, null); boolean result = cursor.moveToFirst(); cursor.close(); db.close(); return result; } /** * 获取全部的person信息 * @return */ public List<Person> findAll(){ SQLiteDatabase db = helper.getReadableDatabase(); Cursor cursor = db.query("person", null, null, null, null, null, null); List<Person> persons = new ArrayList<Person>(); while(cursor.moveToNext()){ int id = cursor.getInt(cursor.getColumnIndex("id")); String name = cursor.getString(cursor.getColumnIndex("name")); String phone = cursor.getString(cursor.getColumnIndex("phone")); Person person = new Person(name, phone, id); persons.add(person); } cursor.close(); db.close(); return persons; } //更新 public boolean update(String name, String newphone) { SQLiteDatabase db = helper.getReadableDatabase(); // db.execSQL("update person set phone=? where name=?", new Object[]{newphone,name}); ContentValues values = new ContentValues(); values.put("phone", newphone); int result = db.update("person", values, "name=?", new String[] { name }); db.close(); return result>0; } //删除 public boolean delete(String name) { SQLiteDatabase db = helper.getWritableDatabase(); // db.execSQL("delete from person where name=?", new Object[]{ name}); int result = db.delete("person", "name=?", new String[]{name}); db.close(); return result>0; } }
使用系统Api的好处就是不用记住sql操作语句也能对数据库进行操作,而且使用系统Api可以更清楚的看到对数据库的各种操作的结果。
数据库的事务
使用SQLiteDatabase的beginTransaction()方法可以开启一个事务,程序执行到endTransaction() 方法时会检查事务的标志是否为成功,如果程序执行到endTransaction()之前调用了setTransactionSuccessful() 方法设置事务的标志为成功则提交事务,如果没有调用setTransactionSuccessful() 方法则回滚事务。使用例子如下(银行转账):public void testTransection(){ PersonSQLiteOperHelper helper = new PersonSQLiteOpenHelper(getContext()); SQLiteDatabase db = helper.getWritableDatabase(); db.beginTransaction(); try{ db.execSQL("update person set account=account-1000 where name =?",new Object[]{"zhangsan"}); db.execSQL("update person set account=account+1000 where name =?",new Object[]{"wangwu"}); //标记数据库事务执行成功 db.setTransactionSuccessful();////调用此方法会在执行到endTransaction() 时提交当前事务,如果不调用此方法会回滚事务 }catch(Exception e){ }finally{ //由事务的标志决定是提交事务,还是回滚事务 db.endTransection(); db.close(); } }
上面两条SQL语句在同一个事务中执行,如果其他一条没有执行成功或者没有执行,整个事务都不会执行,所以不会出现银行转账的时候出现A给B转钱,A扣了钱,B却没收到钱的尴尬。
2.ListView入门
通过ListView将Person数据库的数据呈现出来。步骤:1.调用listview的setAdapter()的方法。
2.里面传入一个adatper的适配器,覆写getCount()方法,覆写getView(int postion ,View convertView , ViewGroup parent)方法
关键代码:
public class MainActivity extends Activity { pirvate List<Person> persons; private ListView lv; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); PersonDao dao = new PersonDao(this); persons = dao.findAll(); lv = (ListView) findViewById(R.id.lv); lv.setAdapter(new MyAdapter()); } //默认实现类 simplexxx defaultxxx basexxx private class MyAdapter extends BaseAdapter{ private static final String TAG = "MyAdapter"; //返回条目数量 public int getCount(){ return persons.size;//条目个数 == 集合的size } public Object getItem(int postion){ return null; } public long getItemId(int postion){ return 0; } public View getView(int position ,View convertView , ViewGroup parent){ Log.i(TAG,"返回view对象,位置:"+postion); TextView tv = new TextView(); tv.setTextSize(20); tv.setTextColor(Color.BLACK); //得到某个位置对应的person对象 Person person = persons.get(postion); tv.setText(person.toString()); return tv; } } }
3.数据适配器
通过adapter格式化数据显示,原理就是将要显示数据的格式用布局文件设定好,然后通过adapter与ListView关联进行显示,下面将示例将person对象信息显示出来布局代码:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="60dip" android:gravity="center_vertical" android:orientation="horizontal" > <TextView android:id="@+id/tv_id" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="id" android:layout_marginLeft="20dip" android:textColor="#ff0000" android:textSize="18sp" /> <LinearLayout android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="20dip" android:orientation="vertical" > <TextView android:id="@+id/tv_name" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="名字" /> <TextView android:id="@+id/tv_number" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="电话" /> </LinearLayout> </LinearLayout>
重写View getView(int postion ,View convertView , ViewGroup parent)方法
public View getView(int position ,View convertView , ViewGroup parent){ //得到某个位置的person对象 Person person = person.get(position); View view = View.inflate(MainActivity.this,R.layout.list_item,null); //一定要在view对象里面寻找孩子的id TextView tv_id = (TextView) view.findViewById(R.id.tv_id); tv_id.setText("id:"+person.getId()); TextView tv_name = (TextView) view.findViewById(R.id.tv_name); tv_id.setText("id:"+person.getName()); TextView tv_number = (TextView) view.findViewById(R.id.tv_number); tv_id.setText("id:"+person.getNumber()); return view; }
ArrayAdapter
用于显示比较简单的文本public class MainActivity extends Activity { pirvate static String[] names = {"功能1","功能2","功能3","功能4","功能5"} @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ListView lv = (ListView) findViewById(R.id.lv); //设置listview的数据适配器 lv.setAdapter(new ArrayAdapter<String>(this, R.layout.item, R.id.tv, names)); } }
xml文件:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" > <TextView android:id="@+id/tv" android:layout_width="match_content" android:layout_height="wrap_content" android:textSize="20sp" android:text="文本" /> </LinearLayout>
SimpleAdapter
可以显示比较复杂的列表,包括每行显示图片、文字等,但不能对列表进行后期加工(在java代码中加工),也是只是单纯的负责显示(当然可以设计复杂点的布局来显示复杂列表),例如,每行显示不同背景等。public class MainActivity extends Activity { private ListView lv; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); lv = (ListView) findViewById(R.id.lv); // 定义一个list集合 集合里面存放的是 每一个条目 item item数据是一个map的类型 List<Map<String, Object>> data = new ArrayList<Map<String, Object>>(); Map<String, Object> map1 = new HashMap<String, Object>(); map1.put("icon", R.drawable.ic_menu_login); map1.put("name", "我是第一个功能"); Map<String, Object> map2 = new HashMap<String, Object>(); map2.put("icon", R.drawable.ic_menu_manage); map2.put("name", "我是第二个功能"); Map<String, Object> map3 = new HashMap<String, Object>(); map3.put("icon", R.drawable.ic_menu_myplaces); map3.put("name", "我是第三个功能"); Map<String, Object> map4 = new HashMap<String, Object>(); map4.put("icon", R.drawable.ic_menu_notifications); map4.put("name", "我是第四个功能"); Map<String, Object> map5 = new HashMap<String, Object>(); map5.put("icon", R.drawable.ic_menu_paste); map5.put("name", "我是第五个功能"); data.add(map1); data.add(map2); data.add(map3); data.add(map4); data.add(map5); lv.setAdapter(new SimpleAdapter(this, data, R.layout.layout_item, new String[] { "icon", "name" }, new int[]{R.id.iv,R.id.tv})); } }
xml文件:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" android:gravity="center_vertical" android:orientation="horizontal" > <ImageView android:id="@+id/iv" android:layout_width="wrap_content" android:layout_height="wrap_content"/> <TextView android:id="@+id/tv" android:layout_width="wrap_content" android:layout_height="wrap_content" /> </LinearLayout>
显示效果:
4.ContendProvider(内容提供者)
ContentProvider 在android中的作用是对外共享数据,也就是说你可以通过ContentProvider把应用中的数据共享给其他应用访问,其他应用可以通过ContentProvider 对你应用中的数据进行添删改查。使用ContentProvider对外共享数据的好处是统一了数据的访问方式。对外共享数据处理步骤:
第1步:需要继承ContentProvider类并重写相关方法
第2步:在AndroidManifest.xml使用<provider>对该ContentProvider进行配置,为了能让其他应用找到该ContentProvider , ContentProvider 采用了authorities(主机名/域名)对它进行唯一标识,你可以把ContentProvider看作是一个网站(想想,网站也是提供数据者),authorities 就是他的域名:
<manifest .... >
<application android:icon="@drawable/icon" android:label="@string/app_name">
<provider android:name=".PersonContentProvider" android:authorities="cn.itcast.providers.personprovider"/>
</application>
</manifest>
内容提供者的写法:(将自己的应用的数据提供一个访问方式让别的应该也能访问)
public class PersonDBProvider extends ContentProvider { //定义一个uri的匹配器,用于匹配uri 如果路径不满足条件返回-1 private static UriMatcher mather = new UriMatcher(UriMatcher.NO_MATCH);//如果发现路径不正确 不匹配 返回-1 private static final int INSERT = 1; private static final int DELETE = 2; private static final int UPDATE = 3; private static final int QUERY = 4; private PersonDBOpenHelper helper; //定义uri的匹配规则 static{ //添加一组匹配规则 mather.addURI("com.itheima.db.personprovider", "insert", INSERT); mather.addURI("com.itheima.db.personprovider", "delete", DELETE); mather.addURI("com.itheima.db.personprovider", "update", UPDATE); mather.addURI("com.itheima.db.personprovider", "query", QUERY); } // content://com.itheima.db.personprovider/insert 添加 // content://com.itheima.db.personprovider/delete 删除 // content://com.itheima.db.personprovider/update 更新 // content://com.itheima.db.personprovider/query 查找 /** * 当内容提供者被创建的时候调用适合数据的初始化 */ @Override public boolean onCreate() { helper = new PersonDBOpenHelper(getContext()); return false; } //查询数据库 @Override public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { int result = mather.match(uri); if(result==QUERY){ //返回查询的结果集 SQLiteDatabase db = helper.getReadableDatabase(); Cursor cursor = db.query("person", projection, selection, selectionArgs, null, null, sortOrder); return cursor; }else if(result == ID){ int postion = uri.getPath().lastIndexOf("/"); String newid = uri.getPath().substring(postion+1); System.out.println(uri.getPath()); SQLiteDatabase db = helper.getWritableDatabase(); Cursor cursor = db.query("person", projection, "id=?", new String[]{newid}, null, null, sortOrder); return cursor; } else{ throw new IllegalArgumentException("路径不匹配,不能执行查询操作"); } } @Override public String getType(Uri uri) { int result = mather.match(uri); if(result==OK){//口令正确 return "vnd.android.cursor.dir/person"; }else if(result == ID){ return "vnd.android.cursor.item/person"; } return null; } @Override public Uri insert(Uri uri, ContentValues values) { int result = mather.match(uri); if(result==INSERT){ SQLiteDatabase db = helper.getWritableDatabase(); db.insert("person", null, values); db.close(); }else{ throw new IllegalArgumentException("路径不匹配,不能执行插入操作"); } return null; } @Override public int delete(Uri uri, String selection, String[] selectionArgs) { int result = mather.match(uri); if(result==DELETE){ SQLiteDatabase db = helper.getWritableDatabase(); db.delete("person", selection, selectionArgs); db.close(); }else{ throw new IllegalArgumentException("路径不匹配,不能执行删除操作"); } return 0; } @Override public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { int result = mather.match(uri); if(result==UPDATE){//口令正确 SQLiteDatabase db = helper.getWritableDatabase(); db.update("person", values, selection, selectionArgs); db.close(); }else{ throw new IllegalArgumentException("路径不匹配,不能执行更新操作"); } return 0; } }
写内容提供者的时候,cursor所对应的数据库是不用关闭的
通过内容提供者读取其他程序数据:
public void click(View view){ //得到手机中间人 ContentResolver resolver = getContentResolver(); Uri uri = Uri.parse("content://com.itheima.db.personprovider/query"); Cursor cursor = resolver.query(uri,null,null,null,null); while(cursor.moveToNext()){ String name = cursor.getString(cursor.getColumnIndex("name")); String id = cursor.getString(cursor.getColumnIndex("id")); System.out.println("name="+name+" : "+"id="+id); } cursor.close(); }
短信的备份
首先查看源代码,查看packages/providers/TelephoneProvider文件夹中的AndroidManifest.xml的清单文件,再查看源码文件,可以得到短信的内容提供者的Uripublic void readSms(View view) { ContentResolver resolver = getContentResolver(); Uri uri = Uri.parse("content://sms/"); // 全部短信的uri Cursor cursor = resolver.query(uri, null, null, null, null); while (cursor.moveToNext()) { String address = cursor.getString(cursor.getColumnIndex("address")); long date = cursor.getLong(cursor.getColumnIndex("date")); String body = cursor.getString(cursor.getColumnIndex("body")); String type = cursor.getString(cursor.getColumnIndex("type")); System.out.println("地址:" + address + " 时间" + new Date(date) + " 内容:" + body); if ("1".equals(type)) { System.out.println("接收到的短信"); } else { System.out.println("发送的短信"); } System.out.println("------------------"); } cursor.close(); }
记得添加android.permission.WRITE_SMS权限
向系统短信应用插入一条短信记录
public void click(View view){ new Thread(){ public void run() { try { Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } //插入一条短信 ContentResolver resolver = getContentResolver(); Uri uri = Uri.parse("content://sms/"); ContentValues values = new ContentValues(); values.put("address", "95533"); values.put("date", System.currentTimeMillis()); values.put("type", 1); values.put("body", "尊敬的用户,你的尾号为558的建行卡,收到汇款人民币888888元,活期余额人民币99999999.52元"); resolver.insert(uri, values); }; }.start(); }
5.ContentObserver(内容观察者)
观察(捕捉)特定Uri引起的数据库的变化,继而做一些相应的处理要想实现一个内容观察者,必须在ContentResolver的实例中注册一个ContentObserver:
resovler.registContentObserver(Uri uri,notifyForDescendents,observer),参数解释如下:
uri 需要观察的Uri(需要在UriMatcher里注册,否则该Uri也没有意义了)
notifyForDescendents 这是一个布尔类型的数值,表示是否模糊匹配该uri
observer ContentObserver的派生类实例
使用内容观察者监听短信变化
public class MainActivety extends Activity{ @Override protected void onCreate(Bundle savedInstanceState){ super.onCreadte(savedInstanceState); setContentView(R.layout.activety_main); ContentResolver resolver = getContentResolver(); Uri uri = Uri.parse("content://sms/"); resolver.registerContentObserver(uri,true,new MyObserver(new Handler())); } private class MyObserver extends ContentObserver{ public MyObserver(Handler handler){ super(handler); } //当内容观察者观察到是数据库的内容变化了,调用这个方法 //观察到消息邮箱里面有一条数据库内容变化的通知 @Override public void onChange(boolean selfChange){ super.onChange(selfchange); Toast.makeText(MainActivity.this,"数据库的内容发生改变了",1).show(); } } }
实现原理:当内容发生改变的时候,底层会调用ContentResolver的notifyChange(uri,observer)方法,发送一个数据库改变的消息。我们也可以在实际开发中,将自己的数据库改变的消息通过些原理发送出去。
相关文章推荐
- android的基础学习:SQLite存储方式以及数据库操作
- Android学习第3天,test、sqlite、listview、dialog
- Android学习之SQLite基础
- Android 学习笔记之 SQLite基础用法
- android基础学习之sqlite
- Android基础学习之SQLiteHelper(数据库帮助类)
- Android平台学习基础(2)-SQLite基本操作
- Android学习之sqlite基础
- android 基础学习(6)-----sqlite3查看表结构
- Android自定义View基础学习
- android基础学习之RSA加密解密
- 【幻化万千戏红尘】qianfeng-Android-Day03-RadioButton及RadioGroup的用法、CheckBox、ProgressBar基础学习:
- [Android新手学习笔记35]-Storage-SQLite
- Android基础之AdapterView系列学习
- Android学习日志——第3天
- Android入门之Android SQLite基础
- Android学习之使用SQLite实现简单的(CRUD)增删改查
- android基础学习(6)-------AlertDialog.Builder提示对话框
- Android绘图基础Paint和Canvas介绍-android学习之旅(六十一)
- Android基础学习-----创建第一个Android项目HelloWorld(二)