安卓入门之本地存储
Android提供了在本地存储数据的功能,这在需求(比如保存登陆密码)和性能(缓存数据)方面是十分有用的。下面整理安卓中本地数据存储的知识,跟前面一样,我们将对比web中的数据存储来学习(PS:貌似还没有写过localStorage相关的博客呢~)
参考:
- 《第一行代码 Android》
文件存储
在web中,由于安全限制,数据持久化的实现一般是依赖浏览器实现的,从最初的cookie
存储到HTML5中的localStorage
和sessionStorage
,以及Web SQL Databases
等。
在Android中,可以使用系统原生的文件IO功能,因此在硬盘上存储数据变得简单很多。 其具体实现就是:把数据以文件的形式导出到硬盘上,再需要的时候再通过读取文件来恢复数据。
存储数据
public void saveFileData(View view){
String data = "Hello file";
FileOutputStream out = null;
BufferedWriter writer = null;
try {
out = openFileOutput("testData", Context.MODE_PRIVATE);
writer = new BufferedWriter(new OutputStreamWriter(out));
writer.write(data);
}catch (IOException e){
e.printStackTrace();
}finally {
try {
if (writer != null){
Log.i(TAG, "done~~");
writer.close();
}
}catch (IOException e){
e.printStackTrace();
}
}
}
上面的方法新建了一个testData
的文件,并向其中写入了需要保存的数据"Hello file"
,然后打开DDMS
,沿着路径/data/data/应用包名/files
可以查看对应的文件了,将文件导入查看,可以发现实际上就是一个纯文本文件(如果使用DDMS遇见了问题,请参考最后一章,因为我也遇见了很多问题~)。
读取文件数据
纯文本文件意味着,我们读取获得的就是整个文件内容(一个字符串),如果需要获得特定的内容,则需要按照一定的格式存储(比如按行或特定的分隔符啥的),最后使用对应的格式进行解析
public void getFileData(View view){
FileInputStream in = null;
BufferedReader reader = null;
StringBuilder sb = new StringBuilder();
try {
in = openFileInput("testData");
reader = new BufferedReader(new InputStreamReader(in));
String line = "";
while ((line = reader.readLine()) != null){
sb.append(line);
}
}catch (Exception e){
e.printStackTrace();
}finally {
if (reader != null){
try {
// 获取到了文件内容
Log.i(TAG, sb.toString());
reader.close();
}catch (IOException e){
e.printStackTrace();
}
}
}
}
通过StringBuilder
对象逐行拼接文件内容,最后输出的就是一个字符串~
小结
通过纯文本来保存一些简单的文本信息还好(比如保存用户的编辑草稿?),如果保存一些有特定格式的数据,可能就不是很实用了。
PS:Java中的异常处理是强制的,对于程序的健壮性来说是好的,但总归有点不太习惯
SharedPreferences
上面提到,使用纯文本文件来保存的数据,在读取部分特定数据时比较麻烦,在需求中,除了能够保存本地数据之外,更方便的读取数据也是一个很重要的因素。
Android提供了SharedPreferences
,将数据以键值对的形式,以xml文件格式保存在本地,这样,在读取数据的时候只需要提供指定的键值,就可以获得对应的数据了。就接口的用法来将,我感觉SharedPreferences跟localStorage简直一模一样啊~
存储数据
只需要获得SharedPreferences.Editor
实例,然后调用对应的put
接口就可以了,比起冗长的文件流保存和异常处理,这里的代码简单得多。
public void saveLocalData(View view){
// xml文件夹名称为data
SharedPreferences.Editor editor = getSharedPreferences("data", MODE_PRIVATE).edit();
// 存储的主要是java内置类型
editor.putString("name", "txm");
editor.putString("password", "123456");
// editor.commit();
editor.apply();
}
使用put
接口之后,需要调用commit
或apply
进行确认,最终保存数据。关于这两个方法的区别,请移步这里,主要区别在于:
apply
将数据提交到内存,然后异步保存到硬盘,因此效率更高,但是当连续调用apply时,后续调用apply可能覆盖前面apply的数据commit
将数据同步保存到磁盘上,因此效率慢,但是可以返回一个操作状态的布尔值
在执行存储操作之后,打开DDMS,我们可以在data/data/包名/shared_prefs
文件夹下发现对应的xml文件了,可以导出查看具体的文件内容。(同上,如果DDMS出现问题,请参考最后一章)
读取数据
同理,读取数据只需要获得SharedPreferences
实例,然后调用对应的get
接口即可。
public void getLocalData(View view){
SharedPreferences pref = getSharedPreferences("data", MODE_PRIVATE);
// 第二个参数是默认值
String name = pref.getString("name", "");
String password = pref.getString("password", "");
Log.i(TAG, "name: " + name);
Log.i(TAG, "password: " + password);
}
小结
使用xml保存数据的应用范围就广一些了,比如保存登陆密码,用户偏好设置啥的。但是如果本地缓存重复字段的列表数据,可能也不是那么方便,接下来就是数据存储最重要的部分SQLite了。
SQLite
Android系统内置了这个SQLite的关系型数据库,可以很方便的实现数据存储。数据库是一个很广泛的概念,这里只能先整理基础使用方法。
新建数据库
新建数据库需要定义一个继承自SQLiteOpenHelper
的Helper类,然后重写onCreate
和onUpgrade
方法
public class MyDataBaseHelper extends SQLiteOpenHelper{
public static final String TAG = "shymean";
public static final String CREATE_USER = "create table user (id integer primary key autoincrement, name text, password text)";
private Context mContext;
public MyDataBaseHelper(Context context, String name, SQLiteDatabase.CursorFactory factory, int versrion){
super(context, name, factory, versrion);
mContext = context;
}
@Override
public void onCreate(SQLiteDatabase db){
// 第一次创建的运行
db.execSQL(CREATE_USER);
Toast.makeText(mContext, "create success", Toast.LENGTH_SHORT).show();
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion){
// todo
}
}
然后在对应的Activity中实例化一个数据库对象
db = new MyDataBaseHelper(this, "Test.db", null, 1);
dbHelper.getWritableDatabase();
// 然后根据dbHelper操作对应的数据库Test数据库
打开DDMS,修改模拟器权限啥的之后进入data/data/包名/databases
中就可以看见对应的数据库文件Test.db
了(还有一个Test.db-journal
文件,该文件是一个临时的日志文件,用于数据库事务的回滚机制,可以暂时先不用理会)
# 进入数据库
sqlite3 Test.db
# 查看表
.tables
可以看见刚才新增了user
表,除此之外还有一个默认的android_metadata
表,此时就可以执行相关的sql语句了。
CRUD
插入数据
public void addData(View view){
SQLiteDatabase db = dbHelper.getWritableDatabase();
ContentValues values = new ContentValues();
values.put("name", "txmmm");
values.put("password", "123456");
db.insert("user", null, values);
Log.i(TAG, "add data...");
}
更新数据
public void updateData(View view){
SQLiteDatabase db = dbHelper.getWritableDatabase();
ContentValues values = new ContentValues();
values.put("name", "updateTXX");
db.update("user", values, "id = ?", new String[]{"1"});
}
删除数据
public void removeData(View view){
SQLiteDatabase db = dbHelper.getWritableDatabase();
db.delete("user", "id > ?", new String[]{"3"});
}
查询数据
public void queryData(View view){
Log.i(TAG, "get data...");
SQLiteDatabase db = dbHelper.getWritableDatabase();
Cursor cursor = db.query("user", null, null, null, null, null, null);
if (cursor.moveToFirst()){
do {
String name = cursor.getString(cursor.getColumnIndex("name"));
Log.i(TAG, "name: " + name);
}while (cursor.moveToNext());
}
cursor.close();
}
这里的query方法接收的参数表示对应的查询子句
原生sql语句
实际上上面的这些方法都是Android对SQLite进行的封装,SQLite的语法跟其他sql语言的语法基本相似,我们可以使用execSQL
执行原生的数据库操作
db.execSQL("insert into user (name, password) values(?, ?)", new String[]{"txm", "123456"});
是不是很赞?
小结
这里粘了很多代码上来,也许几个月回头看就会觉得这些代码太丑陋了,现在先将就着吧,之前用PHP和NodeJS的时候也封装过一些数据库类,这里学起来也没有那么晦涩。总之,SQLite应该是客户端开发中比较重要的东西,还是得慢慢学习。
DDMS的问题
在刚开始使用AS中模拟器的DDMS的时候遇见了不少问题,这里一并整理。
打开data文件夹是空的?
如果发现根目录下的data文件夹是空,这是因为没有相关目录权限的关系,找到你的Android Studio存放sdk的目录,然后切换到sdk/platform-tools
,打开控制台,输入下面命令
adb shell
su
chmod 777 /data
# 如果只是想修改某个应用包的权限,把*替换成对应文件名即可
chmod 777 /data/data * -R
文件无法导出
修改文件的访问权限就可以在目录中看见文件的存在了,但是却无法查看文件内容,这是因为模拟器权限的缘故
adb root
adb remount
# 出现 remount succeeded则成功
此时选择文件然后右上角的pull a file from the device
即可
小结
这篇文章粘贴了大量的代码,却没有啥干货,先挖些坑后面填吧。
你要请我喝一杯奶茶?
版权声明:自由转载-非商用-保持署名和原文链接。
本站文章均为本人原创,参考文章我都会在文中进行声明,也请您转载时附上署名。