Android 缓存设计与实现方式 |
您所在的位置:网站首页 › 安卓缓存文件名称 › Android 缓存设计与实现方式 |
这是我参与8月更文挑战的第4天,活动详情查看:8月更文挑战 Android 缓存设计与实现方式在初级面试中经常遇到四大组件、五大存储、六大布局。该篇文章主要分析下Android中实现缓存的方式。 一、了解Android的存储方式关于五大存储 大家一般期待的回答是下面这样的。 SharedPreferences SQLite数据库 ContentProvider(内容提供者) 文件存储 网络存储还可以用另外一种方式区分,远端存储,和本地存储。 SharedPreferences存储目录:/data/data//shared_prefs/ 轻量级存储,本地目录在,在正式发布的版本上,只有root才能看到。 什么是轻量? 对其环境依赖的越少,就越轻量,也就是减少耦合的意思。 SQLite存储目录:/data/data//databases/ SQLite 是一个微型数据库,效率高,特别适合存储结构化的数据。Android 中内置了SQLite数据库,它支持SQL语法,但在Android中一般不直接写SQL语句,而是使用第三方库操作对象,达到对数据库增删改查的目的,比如 GreenDao。 ContentProviderContentProvider 是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() // add repository } dependencies { classpath 'com.android.tools.build:gradle:3.1.1' classpath 'org.greenrobot:greendao-gradle-plugin:3.2.2' // add plugin } } In your app modules app/build.gradle file: apply plugin: 'com.android.application' apply plugin: 'org.greenrobot.greendao' // apply plugin dependencies { implementation 'org.greenrobot:greendao:3.2.2' // add library } 复制代码然后在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 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 listStudents = DbHelper.queryAllStudent(); if (listStudents == null) { LogUtils.d("没有数据"); return; } for (int i = 0; i < listStudents.size(); i++) { LogUtils.d("id:" + listStudents.get(i).getId() + "_name:" + listStudents.get(i).getName()); } } 复制代码 |
今日新闻 |
推荐新闻 |
CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3 |