android 数据存储Ⅱ
本章继续讲解在Android开发中,数据的存储与管理。涉及知识点:SQLite,SwipeRefreshLayout控件刷新。
1.功能需求
练习使用SQLite
- 做一个登录界面,数据库字段包含用户名、密码以及是否登录中的状态
- 模拟登录成功后,将登录的用户名及登录状态(登录中)写入数据库,并跳转新页面,有退出按钮
- 当点击退出时,将当前用户登录中的状态清除
- 下一次登录时,显示上一次登录的用户名
- 保存所有用户登录的历史,但一个用户名只保留一条记录
2.软件实现
图1
图2
图3
图4
图5
简要说明:通过左侧滑动导航界面(图5),可以选择登陆页面(图1)和登陆列表页面(图2)。在登陆成功后,会跳转到登录列表界面。通过选择退出,可清除当前用户的登录状态。登录列表,下拉界面可以刷新页面信息(图3)。
3.相关知识
(1)SQLite简介
SQLite,是一款轻型的数据库,是遵守ACID的关系型数据库管理系统,它包含在一个相对小的C库中。它是D.RichardHipp建立的公有领域项目。它的设计目标是嵌入式的,而且目前已经在很多嵌入式产品中使用了它,它占用资源非常的低,在嵌入式设备中,可能只需要几百K的内存就够了。
SQLite由以下几个部分组成:SQL编译器、内核、后端及附件。SQLite通过利用虚拟机和虚拟数据库引擎(VDBE),是调试、修改和扩展SQLite的内核变得更加方便。所有SQL语句都被编译成易读的、可以在SQLite虚拟机中执行的程序集。SQLite的整体结构图如下:
(2)SQLite性能
- 在数据存储方面,袖珍型的SQLite可以支持高达2TB大小的数据库,每个数据库都是以单个文件的形式存在,这些数据都是以B-Tree的数据结构形式存储在磁盘上。
- 在事务处理方面,SQLite通过数据库级上的独占性和共享锁来实现独立事务处理。这意味着多个进程可以在同一时间从同一数据库读取数据,但只有一个可以写入数据。在某个进程或线程想数据库执行写操作之前,必须获得独占锁。在获得独占锁之后,其他的读或写操作将不会再发生。
- SQLite采用动态数据类型,当某个值插入到数据库时,SQLite将会检查它的类型,如果该类型与关联的列不匹配,SQLite则会尝试将该值转换成该列的类型,如果不能转换,则该值将作为本身的类型存储,SQLite称这为“弱类型”。但有一个特例,如果是INTEGER PRIMARY KEY,则其他类型不会被转换,会报一个“datatype missmatch”的错误。
(3)SQLite支持字段类型
SQLite在数据类型存储方面,目前支持如下类型字段: VARCHAR(10),NVARCHAR(15),TEXT,INTEGER,FLOAT,BOOLEAN,CLOB,BLOB,TIMESTAMP,NUMERIC(10,5),VARYING CHARACTER (24),NATIONAL VARYING CHARACTER(16)。
(4)SQLite操作数据规则
Android项目中,数据库生成后,存储在/data/data/[PACKAGE_NAME]/databases目录下。
1.添加、更新和删除数据时,可使用如下通用语句
- db.executeSQL(String sql);
- db.executeSQL(String sql, Object[] bindArgs);//sql语句中使用占位符,然后第二个参数是实际的参数集
2.专用sql操作语句
- db.insert(String table, String nullColumnHack, ContentValues values);
- db.update(String table, Contentvalues values, String whereClause, String whereArgs);
- db.delete(String table, String whereClause, String whereArgs);
- db.rawQuery(String sql, String[] selectionArgs);
- db.query(String table, String[] columns, String selection, String[] selectionArgs, String groupBy, String having, String orderBy);
- db.query(String table, String[] columns, String selection, String[] selectionArgs, String groupBy, String having, String orderBy, String limit);
- db.query(String distinct, String table, String[] columns, String selection, String[] selectionArgs, String groupBy, String having);
以上方法的第一个参数是表示要操作的表名;
insert中的第二个参数表示如果插入的数据每一列都为空的话,需要指定此行中某一列的名称,系统将此 列设置为NULL,不至于出现错误;
insert中的第三个参数是ContentValues类型的变量,是键值对组成的Map,key代表列 名,value代表该列要插入的值;
update的第二个参数也很类似,只不过它是更新该字段key为最新的value值,
第三个参数 whereClause表示WHERE表达式,比如“age > ? and age < ?”等,最后的whereArgs参数是占位符的实际参数值;
delete方法的参数和update一样;
db.query传入各种参数表示查询,他们同时返回一个Cursor对象,代表数据集的游标。
除以上基本sql操作语句外,SQLite还支持事务操作,如以下操作使用:
1 public void TestTransaction(List<Person> persons) { 2 db.beginTransaction(); //开始事务 3 try { 4 for (Person person : persons) { 5 db.execSQL("INSERT INTO person VALUES(null, ?, ?, ?)", new Object[]{person.name, person.age, person.info}); }
db.setTransactionSuccessful(); //设置事务成功完成 6 } finally { 7 db.endTransaction(); //结束事务 8 } 9 }
(4)SQLite通用sql语句与专用模块操作对比
- db.executeSQL(String sql),利用这种方式,对于熟练写sql语句的程序员,可以很快速的写sql语句。加快开发速度。但是,这样的sql语句耦合性太高,不灵活,一旦数据库字段修改,对应业务也需要跟着改。
- 利用专用的操作语句,相对来说,不是直接写sql语句,需要传入不同的表,字段等信息,相对来说麻烦,但它的优点是降低了系统业务模块与数据库之间的耦合性,在数据库设计方面,如果规划的好,在做数据库字段,修改,新增时,可以不用或者只修改很少一部分业务逻辑代码。
(5)SwipeRefreshLayout简介
各种下拉控件,下拉操作在Android开发中,已经是层出不穷。google终于忍不住推出了自己的下拉组件SwipeRefreshLayout,它很轻巧,很灵活,操作很简单。
swipeRefreshLayout这个类是在放在 android-support-v4.jar里面的。SwipeRefreshLayout组件只接受一个子组件:即需要刷新的那个组件。它使用一个侦听机制来通知拥有该组件的监听器有刷新事件发生,换句话 说我们的Activity必须实现通知的接口。该Activity负责处理事件刷新和刷新相应的视图。一旦监听者接收到该事件,就决定了刷新过程中应处理 的地方。如果要展示一个“刷新动画”,它必须调用setRefrshing(true)
,否则取消动画就调用setRefreshing(false)
。
(6)SwipeRefreshLayout使用
1.布局文件中加入SwipeRefreshLayout控件,把需要刷新的视图放在控件里面,如刷新一个Fragment视图:
<android.support.v4.widget.SwipeRefreshLayout android:id="@+id/sr" android:layout_width="match_parent" android:layout_height="match_parent"> <RelativeLayout android:layout_width="match_parent" android:layout_height="match_parent" <FrameLayout android:id="@+id/fl_content" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_above="@id/line" /> </RelativeLayout> </android.support.v4.widget.SwipeRefreshLayout>
2.后台代码中操作如下:
sr = (SwipeRefreshLayout) findViewById(R.id.sr); sr.setColorSchemeResources(android.R.color.holo_blue_bright, android.R.color.holo_green_light, android.R.color.holo_orange_light, android.R.color.holo_red_light); sr.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() { @Override public void onRefresh() { appLoginDetailFragment(); //需要刷新时的操作 sr.setRefreshing(false); } });
4.项目核心代码解析
Android数据库操作项目Demo结构图如下所示:
(1)数据库处理
数据库处理放在db包,dbhelper是建立数据库的基本操作,包括建表语句;clssqlite表示提供数据库查询,打开等通用操作模板。
(2)登录处理
登录处理要求:模拟登录成功后,将登录的用户名及登录状态(登录中)写入数据库,保存所有用户登录的历史,但一个用户名只保留一条记录。所以在登录处理时,需要先检查数据表中是否含该登录账户的信息,核心代码如下:
1 private void init() 2 { 3 try { 4 db = new clssqlite(); 5 db.openDB(getActivity()); 6 login_on.setOnClickListener(new View.OnClickListener() { 7 @Override 8 public void onClick(View arg0) { 9 SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); // 这里的格式可以自己设置 10 // format()方法是用来格式化时间的方法 11 Date datenow = new Date(); 12 String returnTime = format.format(datenow); 13 String account = operator.getText().toString(); 14 String passwords = password.getText().toString(); 15 //检测是否含有该账户 16 int loginstatus = db.checklogin("select count(*) as data from LoginData where account='"+account+"' "); 17 try {Thread.sleep(400); 18 } catch (InterruptedException e) { 19 e.printStackTrace();} 20 String sql = "insert into LoginData (account,password,loginstatus,logintime) values ('" + account + "','" + passwords + "',1,'" + returnTime + "') "; 21 if (loginstatus > 0) 22 sql = "update LoginData set loginstatus=1 , logintime='" + returnTime + "' where account='"+account+"' "; 23 db.openDB(getActivity()); 24 if(db.executeSQL(sql)>=0) 25 { 26 //保存登录名 27 result.setText("登录成功"); 28 account_save.edit().putString("account", account).commit(); 29 ((MainActivity) mContext).appLoginDetailFragment(); 30 ((MainActivity) mContext).setToolbarTitle("登录列表"); 31 }else { 32 result.setText("登录失败"); 33 }} 34 }); 35 }catch (Exception ex) 36 { 37 result.setText("登录异常");} 38 }
(3)显示上一次登录的用户名
这个知识利用上一章的SharedPreferences存储即可,在登录成功后,根据代码account_save.edit().putString("account", account).commit();完成用户名保存。
(4)退出清空登录状态
在退出操作时,需要先验证用户是否已经登录了,若登录了,更新数据表中的登录状态字段即可。核心代码如下:
1 public boolean onOptionsItemSelected(MenuItem item) { 2 int id = item.getItemId(); 3 //退出处理 4 if (id == R.id.action_loginoutmode) { 5 db.openDB(this); 6 String account=LoginFragment.account_save.getString("account","00000"); 7 int loginstatus = db.checklogin("select count(*) as data from LoginData where account='"+account+"' and loginstatus=1 "); 8 if(loginstatus>0) 9 { 10 String sql="update LoginData set loginstatus=0 where account='"+account+"' "; 11 db.openDB(this); 12 db.executeSQL(sql); 13 appLoginDetailFragment(); 14 Toast.makeText(getApplicationContext(), "退出登录成功", Toast.LENGTH_SHORT).show(); 15 } 16 else 17 { 18 Toast.makeText(getApplicationContext(), "还未登录", Toast.LENGTH_SHORT).show(); 19 } 20 } 21 return super.onOptionsItemSelected(item); 22 }
(5)登录信息展示
登录状态展示是把数据表中存储的字段信息,展示在界面上。其中,对于页面刷新操作,当listview拉动到顶部时,触发刷新事件,相关代码如下:
1 listView.setOnScrollListener(new AbsListView.OnScrollListener() { 2 @Override 3 public void onScrollStateChanged(AbsListView view, int scrollState) { 4 } 5 @Override 6 public void onScroll(AbsListView view, int firstVisibleItem, 7 int visibleItemCount, int totalItemCount) { 8 if (listView != null && listView.getChildCount() > 0) { 9 boolean enable = (firstVisibleItem == 0) && (view.getChildAt(firstVisibleItem).getTop() == 0); 10 //调用刷新控件 11 ((MainActivity) mContext).setSwipeRefreshEnable(enable); 12 } 13 } 14 });
5.项目源代码下载
本项目源代码在360云盘上,开发环境为 Android Studio 2.0 beta 7。
https://yunpan.cn/cYamkZG3sq9jf 访问密码 f3d5。文件名称:android数据库存储操作demo。
喜欢请赞赏一下啦^_^
微信赞赏
支付宝赞赏