Implementation of Storage Optimization for Android Optimization

  • 2021-09-16 08:01:22
  • OfStack

Interchange data format

Protocal Buffers introduced by Google is a lighter and more efficient storage structure, but it consumes a lot of memory.

FlatBuffers is also introduced by Google, which focuses on performance and is suitable for mobile terminals. It takes up more storage than Protocal.

SharePreferences Optimization

When the SharedPreferences file has not been loaded into memory, calling the getSharedPreferences method initializes the file and reads it into memory, which can easily lead to longer time consumption. The commit or apply methods of Editor differ in synchronous and asynchronous writing and whether a return value is required. Using the apply method can greatly improve performance without requiring a return value. The commitToMemory () in the SharedPreferences class locks the SharedPreference object, the put () and getEditor () methods lock the Editor object, and even lock a write lock when writing to disk. Therefore, the best optimization method is to avoid frequent reading and writing SharedPreferences and reduce unnecessary calls. For batch operation of SharedPreferences, it is best to get one editor for batch operation, and then call apply method.

Bitmap decoding

4.4 The above decodeFile does not use cache internally, which is inefficient. To use decodeStream, the incoming file stream is BufferedInputStream. decodeResource also has performance problems, so use decodeResourceStream.

Database optimization

1. Use StringBuilder instead of String

2. Fewer result sets and fewer fields are returned when querying

When querying, only the required fields and result sets are taken. More result sets consume more time and memory, and more fields will lead to more memory consumption.

3. Use less cursor. getColumnIndex

The time consumption of cursor. getColumnIndex is almost the same as that of cursor. getInt, based on observations during performance tuning. You can remember the index of a column with static variable when building a table, and call the corresponding index directly instead of every query.

4. Asynchronous Threads

Table query may not take much time when there is little data in Android, It will not cause ANR, but when it is larger than 100ms, it will also make users feel delay and jamming, which can be put into threads to run. However, sqlite has limitations in concurrency, and multithreading control is troublesome. At this time, you can use a single thread pool to perform db operations in tasks, and return results through handler to interact with UI threads, which will not affect UI threads, but also prevent abnormalities caused by concurrency.

5. SQLiteOpenHelper maintains one singleton

Because SQLite's support for multithreading is not perfect, if two threads operate the database at the same time, because the database is occupied by another thread, an exception of "Database is locked" will be reported in this case. Therefore, using singleton schema in database management class can ensure that no matter which thread gets the database object, it is the same one.

The best way is to manage all database operations from 1 to the same thread queue, while the business layer uses cache synchronization, which can completely avoid the asynchronous and deadlock problems caused by multi-threaded database operation.

6. Initialization in Application

Create a database using Context of Application and close it at the end of the Application life cycle. Initialize the database first in the process of application startup, so as to avoid the long operation time caused by initialization after entering the application.

7. Use less AUTOINCREMENT

After adding AUTOINCREMENT to the primary key, it can be guaranteed that the primary key is strictly incremented, but it cannot be guaranteed to add 1 every time, because after the insertion fails, the failed line number will not be multiplexed, which will cause the primary key to be spaced, and then INSERT will take more than one time.

This AUTOINCREMENT keyword increases the burden on CPU, memory, disk space, and disk I/O, so try not to use it unless necessary. It is usually not necessary.

Affairs

The two major benefits of using transactions are atomic commit and better performance:

Atomic Commit: Means that all modifications within the same transaction are either completed or not made. If a modification fails, it will be automatically rolled back so that all modifications will not take effect. Better performance: Sqlite creates 1 transaction for each insert and update operation by default and commits immediately after each insert and update. So if you insert data 100 times in a row, you're actually creating a transaction, executing a statement, and committing the process is repeated 100 times. If you explicitly create a transaction, this process is only done once, and the performance can be greatly improved by this one-time transaction. Especially when the database is located on the sd card, the time can be saved by about two orders of magnitude.

There are three main methods: beginTransaction, setTransactionSuccessful and endTransaction.

SQLiteStatement

Using Android system provides SQLiteStatement to insert data, the performance has a definite improvement, and also solves the problem of SQL injection.


SQLiteStatement statement = dbOpenHelper.getWritableDatabase().compileStatement("INSERT INTO EMPERORS(name, dynasty, start_year) values(?,?,?)"); 
statement.clearBindings();
statement.bindString(1, "Max"); 
statement.bindString(2, "Luk"); 
statement.bindString(3, "1998"); 
statement.executeInsert();

SQLiteStatement can only insert data from 1 table, and the last data should be cleared before inserting.

Index

Indexes are like catalogues of books, which can quickly find the number of pages. Indexes in databases can help find data quickly without scanning the whole table. Appropriate indexes can greatly improve the efficiency of database query.

Advantages: It greatly accelerates the speed of database retrieval, including single table query, linked table query, grouping query and sorting query. It is often one to two orders of magnitude of performance improvement, and it grows with the order of magnitude of data.

Disadvantages:

Indexes are expensive to create and maintain. Indexes take up physical space and increase with the amount of data. Indexes need to be maintained when adding, deleting and modifying the database, so the performance of adding, deleting and modifying will be affected.

Classification

1. Create indexes directly and indirectly

Direct creation: Use sql statement to create, Android can be created in onCreate of SQLiteOpenHelper or onUpgrade directly excuSql statement, such as CREATE INDEX mycolumn_index ON mytable (myclumn) Indirect creation: Defines a primary key constraint or a uniqueness key constraint. Indexes can be created indirectly. The primary key defaults to a uniqueness index.

2. General index and uniqueness index

General index: CREATEINDEXmycolumn_indexONmytable(myclumn) Uniqueness index: Ensure that all data in the index column is uniqueness. It can be used for both clustered and non-clustered indexes. The statement is CREATE UNIQUE COUSTERED INDEX myclumn_cindex ON mytable(mycolumn)

3. Single index and composite index

Single index: An index building statement contains only a single field, as in the normal index and uniqueness index creation examples above. Composite index: Also called composite index, which contains multiple fields in the index building statement at the same time, such as CREATEINDEXname_indexONusername(firstname,lastname), Where firstname is the leading column.

4. Clustered index and non-clustered index (clustered index, clustered index)

Clustered index: Physical index, which is the same as the physical order of the base table, and the order of data values is always arranged in order, such as CREATE CLUSTERED INDEX mycolumn_cindex ON mytable(mycolumn) WITH ALLOW_DUP_ROW , of which WITH ALLOW_DUP_ROW Represents a clustered index that allows duplicate records Non-clustered index: CREATEUNCLUSTEREDINDEXmycolumn_cindexONmytable(mycolumn), The index defaults to a non-clustered index

Usage scenario

When the data update frequency of a field is low and the query frequency is high, there is often a range query (>, <, =,>=, <=) Or order by , group by Indexes are recommended when occurring. And the greater the selectivity (the number of only one value in a field/the total number), the more advantageous it is to build an index Multiple columns are often accessed at the same time, and each column contains duplicate values, so you can consider building a composite index

Usage rules

For composite indexes, take the most frequently used column as the leading column (the first field in the index). If the leading column is not in the query condition at the time of query, the composite index will not be used. Such as CREATEINDEXmycolumn_indexONmytable(myclumn)0 , select * from student where class = 2 The index is not used, select * from dept where grade = 3 Indexes are used Avoid evaluating index columns. Any evaluation of where clause columns that cannot be compiled and optimized will invalidate the index when querying select * from student where tochar(grade)='2 Avoid using NULL for comparison values When querying multiple tables, we should pay attention to selecting the appropriate table as the inner table. Join conditions should fully consider the table with index and the table with more rows. The selection of inner and outer surfaces can be determined by the formula: the number of matching rows in the outer table * the number of times of searching every time in the inner table, and the minimum product is the best scheme. Before the actual multi-table operation is actually executed, the query optimizer lists several possible join schemes according to the join conditions and finds the best scheme with the least system overhead Query column and index column order 1 Replace EXISTS clause with multi-table join Put the condition with the largest number of filtered records first Good at using stored procedures, which makes sql more flexible and efficient (Sqlite does not support stored procedures)

Other general optimizations

Frequently used data is cached after reading, so as to avoid "write amplification" caused by repeated reading and writing. Sub-threads read and write data When ObjectOutputStream serializes the disk, it saves each object in memory to the disk. When saving the object, each data member will bring one I/O operation. Encapsulate an output stream ByteArrayOutputStream or BufferedOutputStream above ObjectOutputStream, first write the serialized information to the buffer area, and then write it to the disk once again; Accordingly, replace ObjectInputStream with ByteArrayInputStream or BufferedInputStream. Choose the size of buffer Buffer reasonably. Too small leads to more I/O operations, and too large leads to longer application time. For example, 4-8 KB.

Related articles: