This is the fourth day of my participation in the August More text Challenge. For details, see:August is more challenging
Android cache design and implementation
Four components, five stores, and six layouts are often encountered in the initial interview. This article focuses on the way caching is implemented in Android.
First, understand the Android storage method
About the Big Five
The expected response is something like this.
- SharedPreferences
- SQLite database
- ContentProvider
- File storage
- Network storage
There’s another way to distinguish between remote storage and local storage.
- SharedPreferences
Storage directory: /data/data/
What is lightweight? The less dependent it is on its environment, the lighter it is, which means less coupled.
- SQLite
/data/data/
- ContentProvider
ContentProvider is Android’s solution for storing data, still using SqLite at the bottom. It determines resources based on the Universal Resource Identifier (Uri). It can be used by multiple apps. For example, the address book can be obtained by multiple apps. Although we don’t use contentProviders much in daily development, we have frequent contact with contentProviders. For example, when we select an image, the first thing we get is the Uri.
- File storage
File storage, divided into internal storage and external storage.
- How to distinguish internal storage from external storage?
For users, a pluggable SD card is external storage, and what’s welded into the phone is internal storage.
But not for developers.
The storage in the application directory is internal storage.
On the contrary, even if there is a mobile phone non-removable memory card, also called external storage.
- What are the differences between internal and external storage?
(1) In the /data/data/package_name directory, in the debug environment, the Device File Explorer of AndroidStudio can be used to query the storage of the app. If you want to query the directory of other apps, you need to Root. (2) Internal storage is cleared when the application is uninstalled, but external storage is not. (3) You do not need to apply for storage permission for an internal storage directory, but you do need to apply for storage permission for an external storage directory. It makes sense, too, to operate your own app without much intervention.
- Network storage
Files are stored on the server, and resources on the server are operated through urls. For example, the apK version is updated and the APK is downloaded.
Second, the storage method of expansion
- GreenDao- To manipulate objects is to modify data
- ACache- a class that can be easily cached
- ImageLoader/Glide- Cache images
3. Cache design
The scene of a
“I want to cache an image.”
Use level 3 cache. Network cache (slow and costly), local cache, memory cache.
Cache to local storage when first network loading, cache to memory; When the image is accessed again,
Graph TD load again the picture for a little while - > C {memory exists} -- > C | D | existence [images] C - > | no | E {local file exists} E - > there | | X [images] E - > | no | F [network] F -- -- > Y[Display image]
Scenario 2
“I want to load the news.”
Use ACache to cache news content, in the next load first request the local cache, and then the background request network, network data request after success to update the page.
Graph of TD A [to] -- > | start | B (load) B - - > C {local cache exists} -- > C | | D/display the contents of the cache is D -- -- -- -- > > E C whether | | E request data network [background] E - > | | to get the data F[Display/refresh page]
Since the news data does not need to be highly unified with the server, we put the news data into the ACache, and the ACache can also easily set the expiration time.
Scenario 3
“I want to save 10,000 personal information on the app”
When we have a lot of data, if we put the data in A SP or a file in JSON, it is obviously not worthwhile to read the entire file to operate on a single piece of information.
Take a look at the advantages of Sqlite
“Suitable for storing structured data”
The person information should then be tabulated in Sqlite based on the fields ID, name, gender, age, and so on.
This allows you to operate on individual data by ID.
4. GreenDao Practice
Define the core class meaning of GreenDao
-
DaoMaster: DaoMaster holds database objects (SQLiteDatabase) and manages DAO classes (not objects) for a particular schema. It has static methods to create tables or delete them. Its inner classes OpenHelper and DevOpenHelper are SQLiteOpenHelper implementations that create schemas in the SQLite database.
-
DaoSession: Manages all available DAO objects for a particular pattern, which you can obtain using one of the getter methods. DaoSession also provides some common persistence methods, such as entity insertion, loading, updating, refreshing, and deleting.
-
XXXDao: Data access objects (DAO) persist and query entities. For each entity, greenDAO generates a DAO. It has more persistence methods than DaoSession, such as count, loadAll, and insertInTx.
-
Entities: Entities are persistent objects. Typically, an entity object represents a database with standard Java properties (such as a POJO or JavaBean).
practice
1. Create interfaces such as input boxes and buttons
2. Then add configuration information. The best way to do this is to follow the github address guide as it will be updated frequently.
Github.com/greenrobot/…
Pick:
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 In your plugin}} 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' / / the add library}Copy the code
Then add greenDao configuration information to the root directory of app/build.gradle.
DaoPackage 'com.dao.green.db' targetGenDir 'SRC /main/ Java'}Copy the code
3. Create an entity class
@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; / / noteCopy the code
After the creation is complete, the master, session and DAO files will be generated automatically in the corresponding directory after compilation.
4. Initialize in the user-defined Application
GreenDaopublic class App extends Application { @Override public void onCreate() { super.onCreate(); // Open the database dbManager.getInstance ().initdb (this); }}Copy the code
5. Rewrite MySqliteOpenHelper to prevent database upgrade from emptying local data
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); Migrate (db, migrationHelper.migrate (db, 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); }}Copy the code
Migrationhelper.java from github: github.com/yuweiguocn/…
6. Package DbManager and DbHelperDbManager
/** * @author by T, Date on 2019-10-22. * note: Public class DbManager {private static final String DATABASE_NAME = "student_data"; private static final String DATABASE_NAME = "student_data"; private static DbManager instance; private DaoSession mDaoSession; private DaoMaster.DevOpenHelper mDevOpenHelper; private DaoMaster mDaoMaster; 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 (" opened the database: "+ mDevOpenHelper. GetDatabaseName ()); } public DaoSession getDaoSession() { return mDaoSession; Public void closeDataBase() {if (mDevOpenHelper! = null) {LogUtils. D (" shut down the database "+ mDevOpenHelper. GetDatabaseName ()); } closeDaoSession(); closeHelper(); } private void closeDaoSession() { if (mDaoSession ! = null) { mDaoSession.clear(); mDaoSession = null; } } private void closeHelper() { if (mDevOpenHelper ! = null) { mDevOpenHelper.close(); mDevOpenHelper = null; }}}Copy the code
DbHelper
/** * @author by T, Date on 2019-10-22. * note: Further encapsulation, */ 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; }}}Copy the code
7. Use the two click methods in the Activity
/** * Save data ** @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(" Insert successful: "+ flag); ** @param view */ public void QueryAll(view view) {List<Student> listStudents = DbHelper.queryAllStudent(); If (listStudents == null) {logutils.d (" no data "); return; } for (int i = 0; i < listStudents.size(); i++) { LogUtils.d("id:" + listStudents.get(i).getId() + "_name:" + listStudents.get(i).getName()); }}Copy the code