Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

How to solve the ANR problem caused by SharedPreferences

2025-02-28 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >

Share

Shulou(Shulou.com)06/03 Report--

This article mainly introduces "how to solve the ANR problems caused by SharedPreferences". In the daily operation, I believe many people have doubts about how to solve the ANR problems caused by SharedPreferences. The editor consulted all kinds of materials and sorted out simple and easy-to-use operation methods. I hope it will be helpful to answer the doubts about "how to solve the ANR problems caused by SharedPreferences". Next, please follow the editor to study!

Problems in SharedPreferences the efficiency of SP is relatively low

1. Read and write mode: direct Istroke O 2. Data format: xml 3. Write method: full update

Because SP uses the xml format to save the data, each update can only replace the updated data in full, which means that if we have 100 pieces of data, if we update only one piece of data, we also need to convert all the data into xml format and then write it to the file through io, which also leads to the low writing efficiency of SP.

ANRpublic boolean commit () {/ / the current thread saves data to mMap MemoryCommitResult mcr = commitToMemory (); SharedPreferencesImpl.this.enqueueDiskWrite (mcr, null); try {/ / if the write operation is performed in singleThreadPool, pauses the main thread through await () until the write operation is complete. / / the synchronization of commit is accomplished here. Mcr.writtenToDiskLatch.await ();} catch (InterruptedException e) {return false;} / * * callback time: * 1\. Commit calls back * 2\ at the end of both memory and hard disk operations. Apply is a callback at the end of the memory operation * / notifyListeners (mcr); return mcr.writeToDiskResult;}

As shown above, 1.commit returns a value indicating whether the modification was successfully committed. The 2.commit submission is synchronized and will not be completed until the disk operation is successful.

So when the amount of data is relatively large, the use of commit is likely to cause ANR.

ANR caused by Apply

Commit is synchronous, and SP also provides asynchronous apply apply, which commits modified data atoms to memory, and then asynchronously commits to hardware disk, while commit commits synchronously to hardware disk. Therefore, when multiple concurrent commit submissions are made, they will wait for the commit being processed to be saved to disk before operation, thus reducing efficiency. While apply is only an atomic submission to the content, the functions that call apply will directly overwrite the previous memory data, which improves a lot of efficiency to a certain extent.

But apply can also cause problems with ANR.

Public void apply () {final long startTime = System.currentTimeMillis (); final MemoryCommitResult mcr = commitToMemory (); final Runnable awaitCommit = new Runnable () {@ Override public void run () {mcr.writtenToDiskLatch.await (); / / wait. }}; / / add awaitCommit to queue QueuedWork QueuedWork.addFinisher (awaitCommit); Runnable postWriteRunnable = new Runnable () {@ Override public void run () {awaitCommit.run (); QueuedWork.removeFinisher (awaitCommit);}}; SharedPreferencesImpl.this.enqueueDiskWrite (mcr, postWriteRunnable);}

Add a Runnable task of awaitCommit to the queue QueuedWork, and the await () method will be called in awaitCommit to wait, and this will be used as a judgment condition in the life cycle of handleStopService, handleStopActivity, and so on, waiting for the task to be executed.

A Runnable write task of postWriteRunnable is added to the queue through the enqueueDiskWrite method, while the write task is executed in a thread

In order to ensure that the asynchronous task is completed in time, when the life cycle is in handleStopService (), handlePauseActivity (), handleStopActivity (), QueuedWork.waitToFinish () will be called and wait for the write task to be completed.

Private static final ConcurrentLinkedQueue sPendingWorkFinishers = new ConcurrentLinkedQueue (); public static void waitToFinish () {Runnable toFinish; while ((toFinish = sPendingWorkFinishers.poll ())! = null) {toFinish.run (); / / equivalent to calling the `mcr.writtenToDiskLatch.await () `method}}

SPendingWorkFinishers is an instance of ConcurrentLinkedQueue. The apply method adds the write task to the sPendingWorkFinishers queue and executes the write task in the thread pool of a single thread. The scheduling of the thread is not controlled by the program, that is, when the life cycle is switched, the task is not necessarily in the execution state.

The toFinish.run () method, which is equivalent to calling the mcr.writtenToDiskLatch.await () method, waits all the time

The waitToFinish () method does one thing, waiting all the time for the write task to finish, and doing nothing else. When there are many write tasks, they will be executed in turn. When the file is very large, the efficiency is very low, so it is not surprising to cause ANR.

So when the amount of data is large, apply will also cause ANR.

GetXXX () causes ANR

Not only write operations, all getXXX () methods are synchronous. When calling the get method in the main thread, you must wait for the SP to load, or it may cause ANR to call the getSharedPreferences () method, and eventually call the SharedPreferencesImpl#startLoadFromDisk () method to start a thread to read data asynchronously.

Private final Object mLock = new Object (); private boolean mLoaded = false;private void startLoadFromDisk () {synchronized (mLock) {mLoaded = false;} new Thread ("SharedPreferencesImpl-load") {public void run () {loadFromDisk ();}. Start ();}

As you can see, start a thread to read data asynchronously, and when we are reading a larger data, we have not finished reading it, and then call the getXXX () method.

Public String getString (String key, @ Nullable String defValue) {synchronized (mLock) {awaitLoadedLocked (); String v = (String) mMap.get (key); return v! = null? V: defValue;}} private void awaitLoadedLocked () {. While (! mLoaded) {try {mLock.wait ();} catch (InterruptedException unused) {}}.}

When the wait () method is called in the synchronous method, it waits for the thread opened by the getSharedPreferences () method to finish reading the data before continuing to execute. If it is OK to read a few KB of data, if a large file is read, the main thread is bound to block.

The use of MMKV

MMKV is a key-value component based on mmap memory mapping, and the underlying serialization / deserialization is implemented by protobuf with high performance and strong stability. It has been used on Wechat since mid-2015, and its performance and stability have been verified over time. Recently, it has also been transplanted to the Android / macOS / Win32 / POSIX platform, as well as open source.

Advantages of MMKV

1.MMKV implements the SharedPreferences interface, which can switch 2. 5 seamlessly. Through the mmap memory mapping file, provide a block of memory that can be written at any time, App only writes data to it, and the operating system is responsible for writing back the memory to the file, so there is no need to worry about data loss caused by crash. Protobuf protocol is selected for 3.MMKV data serialization. Pb has a good performance in terms of performance and space consumption. 4.SP is a full update, MMKV is an incremental update, and has performance advantages.

For more details, please refer to the document: github.com/Tencent/MMK.

MMKV principle Why MMKV writes faster IO operations

We know that SP is written based on IO. In order to understand IO, we need to understand that user space and kernel space virtual memory are divided into two parts by the operating system: user space and kernel space. User space is where the user program code runs, and kernel space is where the kernel code runs. For security, they are isolated, and even if the user's program crashes, the kernel is not affected.

File writing process: 1. Call write to tell the kernel the start address and length of the data to be written. 2. The kernel copies the data to the kernel cache. 3. It is called by the operating system to copy the data to disk to complete the write.

MMAP

Linux initializes the contents of a virtual memory region by associating it with objects on a disk, a process called memory mapping (memory mapping).

Mmap the file, which allocates address space in the virtual memory of the process, creating a mapping. After such a mapping is implemented, the memory can be read and written in the way of a pointer, and the system will automatically write back to the corresponding file disk.

MMAP advantage

MMAP only needs a data copy process from the disk to the user's main memory to read and write files, which reduces the number of data copies and improves the efficiency of file reading and writing.

MMAP uses logical memory to map disk files. Operating memory is equivalent to operating files. There is no need to open threads. The speed of operating MMAP is as fast as operating memory.

MMAP provides a block of memory that can be written at any time. App only writes data to it. When the operating system runs out of memory or the process exits, it is responsible for writing back memory to the file. There is no need to worry about data loss caused by crash.

It can be seen that the write speed of MMAP is basically the same as that of memory, which is much higher than that of SP, which is why MMKV writes faster.

Data structure of MMKV write mode SP

SP stores data in XML format, as shown below

But this also causes SP to update all the data if it wants to update the data.

MMKV data structure

The MMKV data structure is as follows

MMKV uses Protobuf to store data, which has less redundant data and less space. At the same time, it can easily append data at the end.

Writing mode

Incremental writes append the data directly to the previous data, regardless of whether the key is duplicated or not. This is more efficient, and you only need to insert a piece of data to update the data.

Of course, this will also bring problems, if you keep adding content incrementally, the file is getting bigger and bigger, what should I do? When the file size is not enough, it needs to be written in full. After removing the duplicate key of the data, if the file size meets the written data size, you can directly update the full write, otherwise you need to expand the capacity. (calculate the file size that may be needed in the future based on the average size of each Kmurv during expansion to prevent frequent full writes)

Three advantages of MMKV

Mmap prevents data loss and improves reading and writing efficiency

Simplify the data, represent the most information with the least amount of data, and reduce the data size.

Incremental updates to avoid full writes of relatively large amounts of data each time.

At this point, the study on "how to solve the ANR problems caused by SharedPreferences" is over. I hope to be able to solve your doubts. The collocation of theory and practice can better help you learn, go and try it! If you want to continue to learn more related knowledge, please continue to follow the website, the editor will continue to work hard to bring you more practical articles!

Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.

Views: 0

*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.

Share To

Development

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report