/Harmony

Multi-process SharedPreference implementation without ContentProvider

Primary LanguageKotlinApache License 2.0Apache-2.0

CircleCI GitHub Maven Central Harmony API Maven Central Crypto API

Working on multiprocess Android apps is a complex undertaking. One of the biggest challenges is managing shared data between the multiple processes. Most solutions rely on one process to be available for another to read the data, which can be quite slow and could potentially lead to ANRs.

Harmony is a thread-safe, process-safe, full SharedPreferences implementation. It can be used in place of SharedPreferences everywhere.

Features

Download

The latest release is available on Maven Central.

Gradle

implementation 'com.frybits.harmony:harmony:1.2.2'
// implementation 'com.frybits.harmony:harmony-crypto:0.1.1' // For Encrypted SharedPreferences

Usage

Creating Harmony SharedPreferences

Kotlin

// Getting Harmony SharedPreferences
val prefs: SharedPreferences = context.getHarmonySharedPreferences("PREF_NAME")

Java

// Getting Harmony SharedPreferences
SharedPreferences prefs = Harmony.getSharedPreferences(context, "PREF_NAME")

OR

Creating Encrypted Harmony SharedPreferences (Requires harmony-crypto library)

Kotlin

// Getting Encrypted Harmony SharedPreferences
val prefs: SharedPreferences = context.getEncryptedHarmonySharedPreferences(
            "PREF_NAME",
            MasterKeys.getOrCreate(MasterKeys.AES256_GCM_SPEC),
            EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
            EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
        )

Java

// Getting Encrypted Harmony SharedPreferences
SharedPreferences prefs = EncryptedHarmony.getSharedPreferences(
            context, 
            "PREF_NAME",
            MasterKeys.getOrCreate(MasterKeys.AES256_GCM_SPEC),
            EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
            EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
          )

Once you have this SharedPreferences object, it can be used just like any other SharedPreferences. The main difference with Harmony is that any change made to "PREF_NAME" using apply() or commit() is reflected across all processes.

NOTE: Changes in Harmony do not reflect in Android SharedPreferences and vice-versa!

Performance

The following are comparison performance tests of some popular multiprocess preference libraries. Each test measures the time it takes to insert 1000 items individually into the preference library (Write), the time it takes to read each 1000 items individually (Read), and how long it took for each item to be available in an alternate process (IPC). Each test was run 10 times. All values in the table below are the average time for a single item to be inserted, read, and available in the alternate process.

Tests were broken into two separate categories:

  • Asynchronous writing (if applicable)
  • Synchronous writing

Logic for tests can been seen in the TestRunner.kt file.

Notes

  • All tests were performed on a Samsung Galaxy S9 (SM-G960U) running Android 10
  • Times are for single item operation.

Asynchronous Tests

Library Read (avg) Write (avg) IPC (avg) 1
SharedPreferences 0.0006 ms 0.066 ms N/A
Harmony 0.0008 ms 0.024 ms 102.018 ms
MMKV 2 0.009 ms 0.051 ms 93.628 ms 3
Tray 2 2.895 ms 8.225 ms 1.928 s

Synchronous Tests

Library Read (avg) Write (avg) IPC (avg) 1
SharedPreferences 0.001 ms 9.214 ms N/A
Harmony 0.003 ms 4.626 ms 13.579 ms
MMKV 2 0.010 ms 0.061 ms 86.100 ms 3
Tray 2 2.805 ms 8.168 ms 1.773 s

1 IPC is the time it took for the item to be available in a secondary process. SharedPreferences doesn't support IPC.

2 These libraries don't support asynchronous writes. All tests were synchronous writes by default.

3 MMKV doesn't support a change listener, so a while-loop in a separate thread was used to determine how soon the data was available in the separate process. See MMKVRemoteTestRunnerService.kt for implementation details.

Special thanks

This section is to give a special thanks to individuals that helped with getting this project where it is today.

  • JD - For the batching idea, reviewing the code, and all around bouncing of ideas to improve this project.
  • @orrinLife360 - For helping review some of the more critical improvements.
  • @imminent - For all the Kotlin insight and helping review many of the changes on this project.
  • @bipin360 - For pushing me on this project when I was unsure about it.

Finally, a very special thank you to @life360 for integrating this project and providing incredibly valuable feedback that I was able to use to improve Harmony.

License

   Copyright 2020 Pablo Baxter

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.