存储目录:
/data/data/<packagename>/shared_prefs/
轻量级存储,本地目录在,在正式发布的版本上,只有root才能看到。
什么是轻量? 对其环境依赖的越少,就越轻量,也就是减少耦合的意思。
SQLite
存储目录:
/data/data/<package name>/databases/
SQLite 是一个微型数据库,效率高,特别适合存储结构化的数据。Android 中内置了SQLite数据库,它支持SQL语法,但在Android中一般不直接写SQL语句,而是使用第三方库操作对象,达到对数据库增删改查的目的,比如 GreenDao。
ContentProvider
ContentProvider 是Android存储数据的方案,底部仍然是使用SqLite。它根据Uri(Universal Resource Identifier)统一资源定位符 确定资源。可以多app使用,比如通讯录可以给多个应用获取。虽然日常开发中对
ContentProvider
使用不多,但和
ContentProvider
的接触却很频繁,比如 选择一张图片,我们首先拿到的却是Uri。
文件存储,分为内部存储和外部存储。
怎么区分内部存储和外部存储?
对用户而言可插拔的SD卡就是外部存储,焊接在手机内部的就是内部存储。
但对于开发者而言并不是这样。
在该应用目录下的存储是内部存储。
反之即使存在手机不可拆卸的存储卡上,也叫外部存储。
内部存储和外部存储有哪些不同?
(1) 在
/data/data/package_name
目录下,在debug环境下,可以通过
AndroidStudio
的
Device File Explorer
查询到该app的存储,如果需要查询其它app的这个目录,则需要Root。
(2) 内部存储在应用卸载的时候会被清除,而外部存储不会。
(3) 操作内部存储目录不需要申请存储权限,而外部存储需要存储权限。这点也能理解,操作自己的应用不需要过多干预提醒。
文件保存在服务器上,通过url操作服务器上的资源。比如apk的版本更新下载apk的场景。
二、拓展的存储方式
GreenDao-操作对象即修改数据
ACache-一个类轻松缓存
ImageLoader/Glide-缓存图片
三、缓存设计
“我想缓存一张图片”
使用三级缓存。网络缓存(速度慢、费流量), 本地缓存,内存缓存。
首次网络加载时缓存到本地存储,缓存到内存;
当再次访问该图片时,
graph TD
再次加载该图片时 --> C{内存中是否存在}
C -->|存在| D[显示图片]
C -->|不存在| E{本地文件是否存在}
E -->|存在| X[显示图片]
E -->|不存在| F[请求网络]
F --> Y[显示图片]
“我想加载新闻内容”
使用ACache
缓存新闻内容,在下次加载时先请求本地的缓存,然后后台请求网络,请求网络数据成功后再去更新页面。
graph TD
A[开始] -->|start| B(加载新闻)
B --> C{本地缓存是否存在}
C -->|是| D[显示缓存的内容]
D --> E
C -->|否| E[后台请求网络数据]
E -->|拿到数据| F[显示/刷新页面]
由于新闻的数据不需要与服务器保持高度统一,所以这里我们将新闻数据放入ACache
中,ACache
也可以轻松的设置过期时间。
“我想在app存一万条人员信息”
当有大量数据时,如果我们把数据用JSON存在SP 或者 文件里,对单条信息的操作却要读取整个文件,这样明显是不值得的。
再看看Sqlite的有点
"适合存储结构化的数据"
那么人员信息就应该依据ID、姓名、性别、年龄等字段在Sqlite中建表。
这样在对单个数据操作时就可以通过ID进行操作。
四、GreenDao实践
明确GreenDao的核心类含义
DaoMaster: DaoMaster保存数据库对象(SQLiteDatabase)并管理特定模式的DAO类(而不是对象)。它有静态方法来创建表或删除它们。它的内部类OpenHelper和DevOpenHelper是SQLiteOpenHelper实现,它们在SQLite数据库中创建模式。
DaoSession:管理特定模式的所有可用DAO对象,您可以使用其中一个getter方法获取该对象。DaoSession还提供了一些通用的持久性方法,如实体的插入,加载,更新,刷新和删除。
XXXDao:数据访问对象(DAO)持久存在并查询实体。对于每个实体,greenDAO生成DAO。它具有比DaoSession更多的持久性方法,例如:count,loadAll和insertInTx。
Entities :可持久化对象。通常, 实体对象代表一个数据库行使用标准 Java 属性(如一个POJO 或 JavaBean )。
1. 创建好输入框按钮等界面
2. 然后添加配置信息配置信息最好根据github上的地址指引去做,因为会经常保持更新。
github.com/greenrobot/…
Add the following Gradle configuration to your Android project. In your root build.gradle file:
buildscript {
repositories {
jcenter()
mavenCentral()
dependencies {
classpath 'com.android.tools.build:gradle:3.1.1'
classpath 'org.greenrobot:greendao-gradle-plugin:3.2.2'
In your app modules app/build.gradle file:
apply plugin: 'com.android.application'
apply plugin: 'org.greenrobot.greendao'
dependencies {
implementation 'org.greenrobot:greendao:3.2.2'
然后在app/build.gradle 中的根目录添加greenDao的配置信息。
greendao {
schemaVersion 1
daoPackage 'com.dao.green.db'
targetGenDir 'src/main/java'
3. 创建实体类
@Entity(nameInDb = "STUDENT")
public class Student {
@Id(autoincrement = true)
@Unique
private Long id;
@Property(nameInDb = "name")
private String name;
@Property(nameInDb = "mark")
private String mark;
创建完成后,编译之后会自动在相应目录下生成master、session、dao文件。
4. 在自定义的Application中初始化
GreenDaopublic class App extends Application {
@Override
public void onCreate() {
super.onCreate();
DbManager.getInstance().initDb(this);
5. 重写MySqliteOpenHelper,防止数据库升级清空本地数据
public class MySqliteOpenHelper extends DaoMaster.DevOpenHelper {
public MySqliteOpenHelper(Context context, String name, SQLiteDatabase.CursorFactory factory) {
super(context, name, factory);
* 需要在实体类加一个字段 或者 改变字段属性等 就需要版本更新来保存以前的数据了
* @param db
* @param oldVersion
* @param newVersion
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
super.onUpgrade(db, oldVersion, newVersion);
MigrationHelper.migrate(db, new MigrationHelper.ReCreateAllTableListener() {
@Override
public void onCreateAllTables(Database db, boolean ifNotExists) {
DaoMaster.createAllTables(db, ifNotExists);
@Override
public void onDropAllTables(Database db, boolean ifExists) {
DaoMaster.dropAllTables(db, ifExists);
}, StudentDao.class);
MigrationHelper.java 来自github:github.com/yuweiguocn/…
6. 封装DbManager、DbHelperDbManager
* @author by T, Date on 2019-10-22.
* note: DB的管理类
public class DbManager {
private static final String DATABASE_NAME = "student_data";
private static DbManager instance;
private DaoSession mDaoSession;
private DaoMaster.DevOpenHelper mDevOpenHelper;
private DaoMaster mDaoMaster;
* 获取单例
* @return
public static DbManager getInstance() {
if (instance == null) {
synchronized (DbManager.class) {
if (instance == null) {
instance = new DbManager();
return instance;
public void initDb(Context context) {
mDevOpenHelper = new MySqliteOpenHelper(context, DATABASE_NAME, null);
mDaoMaster = new DaoMaster(mDevOpenHelper.getWritableDatabase());
mDaoSession = mDaoMaster.newSession();
LogUtils.d("打开了数据库:" + mDevOpenHelper.getDatabaseName());
public DaoSession getDaoSession() {
return mDaoSession;
* 关闭数据库 (思考:关闭数据库的场景在哪里)
public void closeDataBase() {
if (mDevOpenHelper != null) {
LogUtils.d("关闭了数据库" + mDevOpenHelper.getDatabaseName());
closeDaoSession();
closeHelper();
private void closeDaoSession() {
if (mDaoSession != null) {
mDaoSession.clear();
mDaoSession = null;
private void closeHelper() {
if (mDevOpenHelper != null) {
mDevOpenHelper.close();
mDevOpenHelper = null;
DbHelper
* @author by T, Date on 2019-10-22.
* note: 进一步封装,对外直接使用该类
public class DbHelper {
* 插入一条学生信息
* @param student
* @return
public static boolean insertStudentInfo(Student student) {
if (student == null) return false;
StudentDao studentDao = DbManager.getInstance().getDaoSession().getStudentDao();
try {
long index = studentDao.insertOrReplace(student);
LogUtils.d("id:" + index);
return true;
} catch (Exception e) {
return false;
* 查询所有的学生
* @return
public static List<Student> queryAllStudent() {
StudentDao studentDao = DbManager.getInstance().getDaoSession().getStudentDao();
try {
return studentDao.loadAll();
} catch (Exception e) {
return null;
7. Activity中的使用Activity中的两个点击方法
* 保存数据
* @param view
public void Save(View view) {
Student student = new Student()
student.setName(etName.getText().toString())
student.setMark(etMark.getText().toString())
boolean flag = DbHelper.insertStudentInfo(student)
LogUtils.d("插入是否成功:" + flag)
* 查询全部数据
* @param view
public void QueryAll(View view) {
List<Student> listStudents = DbHelper.queryAllStudent()
if (listStudents == null) {
LogUtils.d("没有数据")
return
for (int i = 0
LogUtils.d("id:" + listStudents.get(i).getId() + "_name:" + listStudents.get(i).getName())
复制代码