android在数据存储和访问方面有四个,分别是SharedPreferences、文件存储、SQLite和ContentProvider。这篇 博客只写SQLite,另外三,个人感觉不太难,花点时间看下,就能明白。学习时用到了SQLite,有例子,所以好讲点。废话不多 说,先上代码,在代码中分步讲解
一. 要使用SQLite,写一个帮助类(DBHelper)继承SQLiteOpenHelper是必须的,不要问为什么(它是abstract的),不知道,大家都这么写的。
public class DBHelper extends SQLiteOpenHelper {
private static final String SQLNAME="dataBase.db";//创建数据库名,后缀名(.db)可以有,可以没有
private static final int VERSION=1;
//这个构造函数必须有,后面会用到(步骤"三"中init()),里面参数主要是上下文,数据库名,游标(基本上用不到),数据库版本号
public DBHelper(Context context) {
super(context, SQLNAME, null, VERSION);
}
//onCreate()函数是覆写SQLiteOpenHelper这个超类的,也是必须有
@Override
public void onCreate(SQLiteDatabase db) {
/**表中的字段主要有一下几种类型:null,integer,real(浮点数字),text(字符串文本) ,varchar,char,decimal(k,v)等数据类型
这些类型的长度不是固定的,例如表aerobic中time text(10),其实不止这么长,可以填更长,
还有一点是,虽然知道类型是text或者是varchar,但是可以往这个字段上存入任何类型,例如往varchar字段上存boolean类型都行
在dataBase.db中创建三个表,每个表中的id是integer primary key这个没什么好讲的,主键嘛,id也有写成integer primary key autoincrement
这两个有什么不同。我也没发现,刚测试了一下,都一样,如果有谁知道的,麻烦告知下
*/
String aerobic="create table aerobic (id integer primary key,time text(10),journey varchar(10),pulmonary varchar(10))";
db.execSQL(aerobic);
}
//这个方法,是更新数据库版本用,基本上用不到
@Override
public void onUpgrade(SQLiteDatabase arg0, int arg1, int arg2) {
}
}
二. 写一个操作类,这个操作类是对DBHelper进行操作使用
/**
这里解释getWritableDatabase()和getReadableDatabase()的区别。
它们都可以获取SQLiteDatabase的一个实例,这个实例使用来操作数据库用的,例子中是db
区别:getWritableDatabase()是以读写的方式打开数据库,如果磁盘空间没满的话(这里是android系统的手机或平板中的内存),那么就正常对数据库访问
如果倒霉的话,磁盘空间满了,那么只能读而不能写了,如果再次使用它实例化SQLiteDatabase的db去往数据库写东西,那么很不幸,就会报错
getReadableDatabase()也是读写方式(这里的读写是指这个方法里面有getWritableDatabase(),查看源代码会发现,它里面有getWritableDatabase()方法)打开
数据库,如果磁盘空间满了,就会打开失败,但是它会以读的方式访问数据库
注意:在这个类中,有add()、query()等方法,在这些方法中,我有时用db.execSQL()语句,有时用的是SQLiteDatabase自带的方法,为的是操作方便,还望谅解
*/
public class DBHelperOperate{
private DBHelper helper;
private SQLiteDatabase db;
public DBHelperOperate(Context context)
{
helper=new DBHelper(context);
}
//3.3.5
//三个参数是自定义的,分别是,表名,要插入的字段,添加进数据库的内容
public void add(String table,String nullColumnHack,ContentValues values)
{
//这里没有db.close(),因为在手机或者平板上我们使用SQLite时,并不会牵扯到多用户访问这个数据库,所以可以一直保持链接,这对提高效率很有用
db=helper.getWritableDatabase();
db.insert(table, nullColumnHack, values);
}
//这个方法给注释了,因为在开发时,没用到删除数据库操作,而是最后直接删除表,再创建表
/*public void delete(String sql,Object[] objs)
{
db=helper.getWritableDatabase();
db.execSQL("delete from aerobic where id=?", objs);
}*/
//3.2
//这里可能会看不懂,查询的结果是存放到lists集合当中,Information是自定义的一个类,是javabean类,因为我在做开发时,用到了android绘图,因为需要才这么做的
//这个方法中用到了Cursor,顾名思义,是个光标,是用来操作表的,cursor.moveToNext()是将光标先移到表的最前端,然后再一个一个读每条数据
public ArrayList<Information> query(String table,String key)
{
db=helper.getWritableDatabase();
Cursor cursor=db.query(table, null, null, null,
null, null, null, null);
Information informationData=null;
ArrayList<Information> lists=new ArrayList<Information>();
while(cursor.moveToNext()){
//cursor.getDouble()是取出某个字段的值,但是是那个字段的值?(里面的cursor.getColumnIndex("journey")就是具体到读哪个字段)
//取出以后还是以javabean的方式存到Infroamtion中
double jounery=cursor.getDouble(cursor.getColumnIndex("journey"));
double purposeVal=cursor.getDouble(cursor.getColumnIndex(key));
String time=cursor.getString(cursor.getColumnIndex("time"));
informationData=new Information(jounery,purposeVal,time);
lists.add(informationData);
}
return lists;
}
//3.3.4
//更新数据库,看不懂没关系,会有一个类专门来操作这个类中的诸多方法,参数都是在那个类中传进来的
public boolean update(String table,ContentValues values,String whereClause,String[] whereArgs)
{
db=helper.getWritableDatabase();
db.update(table, values, whereClause, whereArgs);
return false;
}
//3.3.3
public List<Double> find(String sql,String time,String key)
{
List<Double> lists=new ArrayList<Double>();
db=helper.getWritableDatabase();
Cursor cursor=db.rawQuery(sql,
new String[]{time});
while(cursor.moveToNext())
{
lists.add(cursor.getDouble(cursor.getColumnIndex("journey")));
lists.add(cursor.getDouble(cursor.getColumnIndex(key)));
}
return lists;
}
//分页,将数据库中某个表从某条数据从哪到哪取出,这个我真开发过程中没用到,不过这里会大致介绍下,limit 3,6 的意思是:从某个表中取出数据,但是要忽略
//前面的3条后再取6条数据
public List<Map<String, String>> getScrollData(int min,int max)
{
//List<String> lists=new ArrayList<String>();
List<Map<String, String>> lists=new ArrayList<Map<String,String>>();
Map<String, String> maps=new HashMap<String, String>();
db=helper.getWritableDatabase();
//Cursor cursor=db.rawQuery(sql, objs);
Cursor cursor=db.rawQuery("select * from aerobic limit ?,?",
new String[]{String.valueOf(min),String.valueOf(max)});
while(cursor.moveToNext())
{
String time=cursor.getString(cursor.getColumnIndex("time"));
String journey=cursor.getString(cursor.getColumnIndex("journey"));
maps.put("time", time);
maps.put("journey", journey);
lists.add(maps);
}
return lists;
}
//3.1.1
//获取数据库中某个表中的数据条数
public long getCount(String sql)
{
db=helper.getWritableDatabase();
Cursor cursor=db.rawQuery(sql, null);
if(cursor.moveToNext())
{
return cursor.getLong(0);
}
return 0;
}
//3.1.2
//删除表,在注释的delete方法中,说了不删除单个数据,只删整个表
public void deleteSql(String sql)
{
db=helper.getWritableDatabase();
db.execSQL(sql);
}
//3.1.2
//创建deleteSql()中删除的表
public void createNew(String sql)
{
db=helper.getWritableDatabase();
db.execSQL(sql);
}
}
三.具体实现,上面的代码肯定看的很糊涂,因为根本不知道那里用到数据库了,那么下面你再坚持下,就知道了
public class AerobicMethod extends Activity{
private String sqlGetCount="select count(id) from aerobic";
private String sqlDeleteTable="drop table if exists aerobic";
private String sqlCreateTable="create table aerobic (id integer primary key,time text(10),journey varchar(10),pulmonary varchar(10))";
private String sqlFind="select * from aerobic where time=?";
private DBHelperOperate dbHelperOperate;
public ArrayList<Information> dataList=null;
private long start;//一进入这个activity时的时间,结合savaData()中end使用,就是要存放到表中,time的值,具体看savaData()和saveAerobicMethod()
private double journey;//要存放到表中,pulmonary键的值
private double pulmonary=0.0;//要存放到表中,journey键的值
private String sysTime=null;
//3.1
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.aerobic);
//3.1
init();
//3.1.1判断表“aerobic”中的数据项,是否大于31,如果是则删除原有表,再重新创建同样的表,目的:更新一个月的数据
if(dbHelperOperate.getCount(sqlGetCount)>31)
{
//3.1.2这里看到了吧,我是先判断,标中的数据是否达到一个数值,达到了就删除整个表,接着再重新创建表
dbHelperOperate.deleteSql(sqlDeleteTable);
dbHelperOperate.createNew(sqlCreateTable);
}
}
//3.1
private void init(){
start=System.currentTimeMillis();
//注意一、二、三这个3个步骤中类的构造方法,这里一初始化DBHelperOperate的一个实例时,就会调用DBHelperOperate类的构造方法,
//DBHelperOperate的构造方法又会调用DBHelper类的构造方法,好了数据库dataBase.db创建了,在这个类中,只要调用DBHelperOperate类中的方法,
//DBHelperOperate类中的方法只要有getWritableDatabase()或getReadableDatabase()是,就会自动调用DBHelper类中的onCreate()方法来创建表了
//这里初始化了操作数据库的一个实例,就是步骤“二”中类DBHelperOperate的一个实例
dbHelperOperate=new DBHelperOperate(this);
}
//3.2
@Override
protected void onStart() {
super.onStart();
/*因为考虑到读数据库是个耗时的操作,所以以消息队列的方式读数据库
* */
Message m=new Message().obtain(mHandler, PublicUtils.DB_QUNEY);
mHandler.sendMessage(m);
}
//3.3
@Override
protected void onDestroy() {
//3.3.1
saveData();
//3.3.2更新数据库,先找到当天日期(time)键对应的数据,将数据更新
String[] whereArgs={sysTime};
//这个ContentValues就是存放到数据库中的值,有点像Map
ContentValues values=new ContentValues();
//3.3.3这里是先查看数据库是否有数据,如果list.size()>0说明数据库中已经有数据了,不要再添加,只要
//更新数据就行
List<Double> list=dbHelperOperate.find(sqlFind,sysTime,"pulmonary");
if(list.size()>0)
{
values.put("journey", list.get(0)+journey);
values.put("pulmonary", list.get(1)+pulmonary);
//3.3.4
dbHelperOperate.update("aerobic", values, "time=?", whereArgs);
}else{
//3.3.5
saveAerobicMethod();
}
super.onDestroy();
}
//保存要存放到数据库的数据,例如,路程(journey)、血氧量(pulmonary)
//3.3.1
public void saveData()
{
long end=System.currentTimeMillis();
double dTime=(end-start)/3600000.0;
int iTime=(int) (dTime*100);
double c=iTime/100.0;
double dJourney=dataWhat*c;
int iJourney=(int) (dJourney*100);
journey=iJourney/100.0;
SimpleDateFormat formatter=new SimpleDateFormat("yyyy-MM-dd");
Date date=new Date(end);
sysTime=formatter.format(date);
}
//3.3.5当退出此模块,调用此方法,将数据保存到数据库中
public void saveAerobicMethod()
{
ContentValues values=new ContentValues();
values.put("time", sysTime);
values.put("journey", journey);
values.put("pulmonary", pulmonary);
//3.3.5
dbHelperOperate.add("aerobic", "id",values);
}
//3.2
Handler mHandler=new Handler(){
@Override
public void handleMessage(Message msg) {
switch(msg.what){
//3.2
case PublicUtils.DB_QUNEY:
dataList=
dbHelperOperate.query("aerobic","pulmonary");
break;
}
}
};
}
四,最后还有一个javabean。很简单,看出了这个类继承了Parcelable,为什么要这样做,在上面说过,因为要用到android绘图(就是条形图、折线图)
绘 图需要数据,我把AerobicMethod类中取得的数据放到dataList中后,需要跳转到另一个activity中,我要传值,并且是以list 集合的方式传过去,具体Parcelable怎么用的,不是本文的主题这里就不说了,只要知道Information是一个javaBean即可
/**例如:
Intent intent=new Intent(AerobicMethod.this,AerobicChart.class);
intent.putParcelableArrayListExtra("aerobicData", dataList);
startActivity(intent);
*/
public class Information implements Parcelable{
private double journey;
private double purposeVal;
private String time;
public Information(double journey, double purposeVal, String time) {
super();
this.journey = journey;
this.purposeVal = purposeVal;
this.time = time;
}
public String getTime() {
return time;
}
public void setTime(String time) {
this.time = time;
}
public double getPurposeVal() {
return purposeVal;
}
public void setPurposeVal(double purposeVal) {
this.purposeVal = purposeVal;
}
public double getJourney() {
return journey;
}
public void setJourney(double journey) {
this.journey = journey;
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeDouble(this.journey);
dest.writeDouble(this.purposeVal);
dest.writeString(this.time);
}
public static final Parcelable.Creator<Information> CREATOR=new Creator<Information>() {
@Override
public Information createFromParcel(Parcel source) {
return new Information(source.readDouble(), source.readDouble(), source.readString());
}
@Override
public Information[] newArray(int size) {
return new Information[size];
}
};
}