第一行代码读书笔记
持久化技术
- 文件存储
- SharedPreference存储
- 数据库存储
文件存储
- 不对存储的内容进行任何格式化处理
- 适合用于存储一些简单的文本数据或二进制数据
数据存储到文件中
Context
类提供了openFileOutput()
方法,用于将数据存储到指定的文件中- 接收两个参数
- 第一个参数是文件名
不可以包含路径,所有文件默认存储到/data/data/<package name>/files/
目录下 - 第二个参数是文件的操作模式,两种可选
MODE_PRIVATE
:默认的操作模式,存在同名文件时,写入的内容覆盖原文件的内容
MODE_APPEND
:如存在同名文件,追加在源文件内容后,不存在则创建新文件
- 第一个参数是文件名
- 返回一个
FileOutputStream
对象 FileOutputStream
->OutputStreamWriter
->BufferedWriter
通过BufferWriter
将文本内容写入到文件中
public void save(String inputText) {
FileOutputStream out = null;
BufferedWriter writer = null;
try {
out = openFileOutput("data", Context.MODE_PRIVATE);
writer = new BufferedWriter(new OutputStreamWriter(out));
writer.write(inputText);
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (writer != null) {
writer.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
- Tools->Android->Android Device Monitor查看/导出文件
从文件中读取数据
Context
类提供了openFileInput()
方法,用于从文件中读取数据- 只接收一个参数:要读取的文件名
系统会自动到/data/data/<package name>/files/
目录下加载文件 - 返回一个
FileInputStream
对象 FileInputStream
->InputStreamReader
->BuffereedReader
- 可以通过
BufferedReader
一行行读取,把文本内容读取到StringBuilder对象中
public String load() {
FileInputStream in = null;
BufferedReader reader = null;
StringBuilder content = new StringBuilder();
try {
in = openFileInput("data");
reader = new BufferedReader(new InputStreamReader(in));
String line = "";
while ((line = reader.readLine()) != null) {
content.append(line);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (reader != null) {
try {
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return content.toString();
}
- 将文件中文本加载到EditText中
String inputText = load();
if (!TextUtils.isEmpty(inputText)) {
edit.setText(inputText);
edit.setSelection(inputText.length());
Toast.makeText(this, "Restoring succeeded", Toast.LENGTH_SHORT).show();
}
setText()
:将字符串填充到EditText里SetSelection()
:将输入光标移动到文本的末尾位置TextUtils.isEmpty()
:当传入的字符串等于null或者等于空字符串时都会返回true;
SharedPreferences存储
- 使用键值对来存储数据的
- 支持多种不同的数据类型存储
将数据存储到SharedPreferences中
- 获取SharedPreferences对象的3种方式
Context
类的getSharedPreferences()
方法- 接收两个参数
第一个参数用于指定SharedPreferences文件的名称,如指定文件不存在则创建一个
SharedPreferences文件都存放在/data/data/<package name>/shared_prefs/
目录下 第二个参数用于指定操作模式,目前只有MODE_PRIVATE
可选(默认模式,和传入0效果相同)
表示只有当前的应用程序才可以对SharedPreferences文件进行读写(其余模式废弃)
- 接收两个参数
Activity
类中的getPreferences()
方法- 只接收一个操作模式参数
- 自动将当前活动的类名作为SharedPreferences的文件名
PreferenceManager
类中的getDefaultSharedPreferences()
方法- 静态方法
- 接收一个
Context
参数 - 自动使用当前应用程序的包名作为前缀来命名
SharedPreferences
文件
- 向SharedPreferences文件中存储数据
- 调用
SharedPreferences
对象的edit()
方法获取一个SharedPreferences.Editor
对象 - 向
SharedPreferences.Editor
对象中添加数据
(添加布尔型putBool(),添加字符串putString(),etc.) - 调用
apply()
方法将添加的数据提交
SharedPreferences.Editor editor = getSharedPreferences("data", MODE_PRIVATE).edit(); editor.putString("name", "Tom"); editor.putInt("age", 29); editor.putBoolean("married", false); editor.apply();
- 调用
- 从SharedPreferences文件中读取数据
- SharedPreferences对象中提供了一系列的
get
方法,每种都对应了SharedPreferences.Editor的一种put
方法(E.g. getBoolean(), getString()) - 这些
get
方法接收两个参数- 第一个参数是键
- 第二个参数是默认值,即传入的键找不到对应的值时返回的默认值
SharedPreferences pref = getSharedPreferences("data", MODE_PRIVATE); String name = pref.getString("name", ""); int age = pref.getInt("age", 0); boolean married = pref.getBoolean("married", false);
- SharedPreferences对象中提供了一系列的
SQLite数据库存储
- 轻量级的关系型数据库
- 支持标准SQL语法,遵循数据库的ACID事务
创建数据库
SQLiteOpenHelper
帮助类- 抽象类
- 两个抽象方法
onCreate()
和onUpgrade()
,创造、升级数据库的逻辑 - 两个实例方法,都可以打开或创建(/升级)一个现有的数据库,不同:当数据库不可写入的时候
getReadableDatabase()
返回的对象以只读方式打开getWritableDatabase()
将出现异常
- 两个构造方法可供重写,一般使用参数少一点的构造方法即可
- 该构造方法接收4个参数
- 第一个参数是Context
- 第二个参数是数据库名
- 第四个参数表示当前数据库的版本号,可用于对数据库进行升级操作
- 构建出
SQLiteOpenHelper
实例后,再调用它的getReadableDatabase()
或getWritableDatabase()
就能创建数据库;
数据库文件存放在/data/data/<package name>/databases/
目录下;
此时重写的onCreate()
方法也会执行(数据库已存在的时候不会执行)
public class MyDatabaseHelper extends SQLiteOpenHelper { public static final String CREATE_BOOK = "create table Book (" + "id integer primary key autoincrement, " + "author text, " + "price real, " + "pages integer, " + "name text)"; private Context mContext; public MyDatabaseHelper(Context context, String name, SQLiteDatabase.CursorFactory factory, int version) { super(context, name, factory, version); mContext = context; } @Override public void onCreate(SQLiteDatabase db) { db.execSQL(CREATE_BOOK); Toast.makeText(mContext, "Create succeeded", Toast.LENGTH_SHORT).show(); } @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { } }
使用adb shell查看数据库
- Android SDK自带的调试工具,存放在sdk的platform-tools目录下
(Windows系统下一般为C:\Users\<user name>\AppData\Local\Android\Sdk\platform-tools
)
把adb的路径配置到环境变量 - 打开命令行,输入
adb shell
,进入安卓设备的控制台 cd /data/data/<package name>/databases/
进入到存放数据库文件的目录下
ls
查看该目录下的文件- 两个数据库文件:
BookStore.db
是我们创建的数据库文件
BookStore.db-journal
是为了让数据库能够支持事务而产生的临时日志文件(通常大小为0字节)
- 两个数据库文件:
sqlite3 <database name>
打开数据库
.table
查看数据库内有哪些表(android_metadata
表是每个数据库中都会自动生成的)
.schema
查看建表语句
.exit
或.quit
退出数据库- 查询:
select * from Book
升级数据库
- 升级更新数据库:
onUpgrade()
- 如何执行
onUpgrade()
:在SQLiteOpenHelper的构造函数中传入更高的版本号dbHelper = new MyDatabaseHelper(this, "BookStore.db", null, 2);
添加数据
- 借助
getReadableDatabase()
或getWritableDatabase()
返回的SQLiteDatabse
对象进行CRUD
操作 SQLiteDatabse
提供了insert()
方法- 接收3个参数
第一个参数是要添加数据的表名
第二个参数用于未指定添加数据的情况下给某些可为空的列自动赋值NULL,一般直接传入null
即可
第三个参数是一个ContentValues
对象,它提供了一系列put()
,用于向ContentValues
中添加数据,将表中的每个列名和相应的待添加数据传入即可SQLiteDatabase db = dbHelper.getWritableDatabase(); ContentValues values = new ContentValues(); values.put("name", "The Da Vinci Code"); values.put("author", "Dan Brown"); values.put("pages", 454); values.put("price", 16.96); db.insert("Book", null, values); values.clear(); values.put("name", "The Lost Symbol"); values.put("author", "Dan Brown"); values.put("pages", 510); values.put("price", 19.95); db.insert("Book", null, values);
更新数据
SQLiteDatabse
提供了update()
方法- 接收4个参数
第一个参数是表名
第二个参数是ContentValues
对象
第三、四个参数用于约束更新某一行或某几行的数据,不指定则默认更新所有行- 第三行对应SQL的
where
部分,表示更新所有name等于?的行
?
是一个占位符,可以通过第四个参数提供的字符串数组为第三个中的每个占位符指定相应的内容
SQLiteDatabase db = dbHelper.getWritableDatabase(); ContentValues values = new ContentValues(); values.put("price", "10.99"); db.update("Book", values, "name = ?", new String[] { "The Da Vinci Vode" });
- 第三行对应SQL的
删除数据
SQLiteDatabse
提供了update()
方法- 接收3个参数
第一个参数是表明 第二、三个参数用于约束删除某一个或某几行的数据,不指定则默认删除所有行SQLiteDatabase db = dbHelper.getWritableDatabase(); db.delete("Book", "page > ?", new String[] { "500" });
查询数据
SQLiteDatabse
提供了query()
方法- 最短的方法重载也需要传入7个参数
- 第一个参数是表名
- 第二个参数是用于指定查询哪几列,不指定则默认查询所有列
- 第三、四个参数用于约束查询某一行或某几行的数据,不指定则默认查询所有行
- 第五个参数用于指定需要去group by的列,不指定则表示不对查询结果进行group by操作
- 第六个参数用于对group by之后的数据进行进一步的过滤,不指定则表示不进行过滤
- 第七个参数用于指定查询结果的排序方式,不指定则表示使用默认的排序方式 | query()方法参数 | 对应SQL部分 | 描述 | |-----------------|---------------------------|-----------------------------| | table | from table_name | 指定查询的表名 | | columns | select column1, column2 | 指定查询的列名 | | selection | where column = value | 指定where的约束条件 | | selectionArgs | - | 为where中的占位符提供具体的值 | | groupBy | group by column | 指定需要group by的列 | | having | having column = value | 对group by后的结果进一步约束 | | orderBy | order by column1, column2 | 指定查询结果的排序方式 |
SQLiteDatabase db = dbHelper.getWritableDatabase(); Cursor cursor = db.query("Book", null, null, null, null, null, null); if (cursor.moveToFirst()) { do { String name = cursor.getString(cursor.getColumnIndex("name")); String author = cursor.getString(cursor.getColumnIndex("author")); int pages = cursor.getInt(cursor.getColumnIndex("pages")); double price = cursor.getDouble(cursor.getColumnIndex("price")); } while (cursor.moveToNext()); } cursor.close();
使用SQL操作数据库
- 添加数据
db.execSQL("insert into Book (name, author, page, price) values(?, ?, ?, ?)", new String[] { "The Da Vinci Code", "Dan Brown", "454", "16.96" }); db.execSQL("insert into Book (name, author, page, price) values(?, ?, ?, ?)", new String[] { "The Lost Symbol", "Dan Brown", "510", "19.95" });
- 更新数据
db.execSQL("update Book set price = ? where name = ?", new String[] { "10.99", "The Da Vinci Code" });
- 删除数据
db.execSQL("delete from Book where page > ?", new String[] { "500" });
- 查询数据
db.rawQuery("select * from Book", null);
使用LitePal操作数据库
- LitePal是一款开源的Android数据库框架
采用对象关系映射(ORM)的模式将一些常用数据库功能进行封装(将面向对象的语言和面向关系的数据库之间建立映射关系)
配置LitePal
- 大多数开源项目会将版本提交到jcenter上,只需在app/build.gradle文件中声明该开源库的引用即可
- 编辑app/build.gradle文件,在dependencies闭包中添加:
compile 'org.litepal.android:core:1.3.2'
- 配置litepal.xml文件
在app.src.main目录下新建文件夹,创建assets目录,在asset目录下新建litepal.xml文件
<?xml version="1.0" encoding="utf-8"?> <litepal> <dbname value="BookStore" ></dbname> <version value="1" ></version> <list> </list> </litepal>
<dbname>
指定数据库名,<version>
指定数据库版本号,<list>
指定所有的映射模型
- 配置AndroidManifest.xml,在标签中添加属性
android:name="org.litepal.LitePalApplication"
创建和升级数据库
- 声明一个Java类对应数据库中的表,类中的每一个字段分别对应了表中的每一个列,每个字段生成相应的getter和setter方法
- 将Java类添加到映射模型类型表中,修改litepal.xml:
<list> <mapping class="<package name>.<class name>"></mapping> </list>
- 只要进行任意一次数据库操作,类对应的数据库就会自动创建出来
E.g.Connector.getDatabase()
(自动生成table_schema表供LitePal内部使用) - 升级:修改类的内容并将版本号加1(不需要drop之前的表)
- 进行CRUD操作时,模型类需要继承自
DataSupport
添加数据
- 创建出模型类的实例,将所有要存储的数据设置好,调用save()方法
更新数据:
- 对已存储的对象重新设值,重新调用save()方法
model.isSave()
:对象是否已经存储- 已经调用过model.save()添加数据
或model对象是通过LitePal提供的查询API查询出来的时
model.isSave()
返回true
- 新建一个实例,用setter设置要更新的值,调用该实例的
updateAll()
方法Book book = new Book(); book.setPrice(14.95); book.setPress("Anchor"): book.updateAll("name = ? and author = ?", "The Lost Symbol", "Dan Brown");
- 可以指定一个约束条件,不指定则更新所有数据
- 不可以用setter设置到默认值,LitePal不会对用setter直接设置成默认值的updateAll()更新
setToDefault()
:设置默认值Book book = new Book(); book.setToDefault("pages"); book.updateAll();
删除数据
- 直接调用已存储对象的
delete()
方法 DataSupport.deleteAll()
第一个参数用于指定删除那张表中的数据
后面的参数用于指定约束条件DataSupport.deletaAll(Book.class, "price < ?", "15");
- 如果不指定约束条件,默认删除表中所有数据
查询数据
- 查询表中的所有数据
List<Book> books = DataSupport.findAll(Book.class);
- 查询表中的第一条数据
Book firstBook = DataSupport.findFirst(Book.class);
- 查询表中的最后一条数据
Book LastBook = DataSupport.findLast(Book.class);
- 连缀查询:
select()
用于指定查询哪几列数据List<Book> books = DataSupport.select("name", "author").find(Book.class);
where()
用于指定查询的约束条件List<Book> books = DataSupport.where("pages > ?", "400").find(Book.class);
order()
用于指定结果的排序方式List<Book> books = DataSupport.order("price desc").find(Book.class);
- desc表示降序,asc或不写表示升序
limit()
用于指定查询结果的数量 E.g.只查表中的前三条数据List<Book> books = DataSupport.limit(3).find(Book.class);
offset()
用于指定查询结果的偏移量 E.g. 查询表中的第2-4条数据List<Book> books = DataSupport.limit(3).offset(1).find(Book.class);
- 任意连缀组合
- 调用
DataSupport.findBySQL()
进行原生SQL查询
第一个参数指定SQL语句,后面的参数指定占位符的值
返回一个Cursor对象Cursor c = DataSupport.findBySQL("select * from Book where pages > ? and price < ?", "400", "20");