Android datastore SharedPreferences or MMKV

Posted by Paul Arnold on Mon, 24 Jan 2022 08:34:38 +0100

1, SharedPreferences

Unlike the way files are stored, if you want to save a relatively small set of key values, you should use the SharedReferences API. The SharedReferences object points to a file containing key value pairs and provides simple read and write methods.

This article starts with SharedReferences and gradually introduces Preference and MMKV.

1.1 get SharedPreferences object

To use SharedPreferences to store data, you first need to get the SharedPreferences object. Android mainly provides two methods to get SharedPreferences objects.

getSharedPreferences() method in Context class

This method receives two parameters:

  • The first parameter is used to specify the name of the SharedPreferences file. If the specified file does not exist, one will be created. The SharedPreferences files are stored in / data / data / < package name > / shared_ In the prefs / directory.

  • The second parameter is used to specify the operation mode. At present, there is only mode_ The private mode is optional. It is the default operation mode and has the same effect as passing in 0 directly. It means that only the current application can read and write this SharedPreferences file.

getPreferences() method in Activity class

This method is very similar to the getSharedPreferences() method in Context, but it only receives an operation mode parameter, because when using this method, the currently active class name will be automatically used as the file name of SharedPreferences.

Note: mode has been deprecated since API level 17_ WORLD_ Readable and MODE_WORLD_WRITEABLE mode. If used, a SecurityException is thrown.

The getSharedPreferences() method in the Context class is used here:

public class SPUtils {
    //File name saved in the phone
    public static final String FILE_NAME = "scc_data";

    private static final SPUtils spUtils = new SPUtils();
    public static SPUtils getInstance() {
        return spUtils;
    }
    private SPUtils() {
    }
    private SharedPreferences getSP() {
        return AppGlobalUtils.getApplication().getSharedPreferences(FILE_NAME,
                Context.MODE_PRIVATE);
    }
}

File saving path: / data / data / com scc. datastorage/shared_ prefs/scc_ data. xml

1.2 write data

Pass the key and value to be written through methods such as putInt() and putString(). Then call apply() or commit() to save the changes.

    private void putSp() {
        SPUtils.getInstance().put("name","Scc");
        SPUtils.getInstance().put("age",20);
    }
    
    /**
     * For the method of saving data, we need to get the specific type of saved data, and then call different saving methods according to the type
     * If only individual types such as String are used, the method can be written separately
     */
    public void put(String key, Object value) {
        if(MStringUtils.isNullOrEmpty(key)){
            return;
        }
        SharedPreferences.Editor editor = getSP().edit();
        if (value instanceof String) {
            editor.putString(key, (String)value);
        } else if (value instanceof Integer) {
            editor.putInt(key, (Integer) value);
        } else if (value instanceof Boolean) {
            editor.putBoolean(key, (Boolean) value);
        } else if (value instanceof Float) {
            editor.putFloat(key, (Float) value);
        } else if (value instanceof Long) {
            editor.putLong(key, (Long) value);
        }
        SharedPreferencesCompat.apply(editor);
    }

1.3 reading data

Call getInt() and getString() to get data from SharedPreference and provide the key of the required value. If the key does not exist, you can also choose to return the default value.

    private void getSp() {
        Log.e("SP","SpString:"+SPUtils.getInstance().getString("name"));
        Log.e("SP","SpInt:"+SPUtils.getInstance().getInt("age"));
    }
    
    //Get String type data
    public String getString(String key) {
        if (MStringUtils.isNullOrEmpty(key)) {
            return "";
        }
        return getSP().getString(key, "");
    }
    //Get Int type data
    public Integer getInt(String key) {
        if (MStringUtils.isNullOrEmpty(key)) {
            return 0;
        }
        return getSP().getInt(key, 0);
    }

1.4 deleting data

Call the remove() method to delete the data from SharedPreference and provide the key for the desired value.

    private void removeSp() {
        SPUtils.getInstance().remove("name");
        Log.e("SP","Remove:"+SPUtils.getInstance().getString("name"));
        Log.e("SP","SpInt:"+SPUtils.getInstance().getInt("age"));
    }
    //Remove the corresponding value of a key value
    public void remove(String key) {
        if (!MStringUtils.isNullOrEmpty(key)) {
            SharedPreferences.Editor editor = getSP().edit();
            editor.remove(key);
            SharedPreferencesCompat.apply(editor);
        }
    }

Then let's look at SCC_ data. Content of XML:

Some simple key value pairs can be stored using shared preference, such as login account, password and some basic user information.

2, Jetpack Preferences

By default, Preference uses SharedReferences to save values. The SharedReferences API allows simple key value pairs to be read and written to saved files across application operations. The Preference Library uses a private SharedReferences instance, so only your application can access it.

Since I rarely use Jetpack Preferences here, I won't introduce them here. Those who are interested can go there Check the official documents by yourself, hey hey.

3, MMKV - high performance general key value component based on mmap

MMKV is a key value component based on mmap memory mapping. 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. It has been ported to Android / macOS / Win32 / POSIX platform and is open source.

git address

3.1 MMKV advantages

  • Very efficient: MMKV uses mmap to keep memory synchronization with files. The operating system is responsible for writing memory back to files. There is no need to worry about data loss caused by crash. Use protobuf to encode / decode values and make full use of Android to achieve the best performance.

  • Multi process Concurrency: MMKV supports concurrent read-write access between processes.

  • Easy to use: you can use MMKV at any time. All changes are saved immediately without synchronization or application calls. Similar to SharedReferences, it can be used directly

  • A few files (small): MMKV contains process locks, encoding / decoding helpers, mmap logic, and so on. It's neat.

3.2 performance comparison (excerpted from MMKV official)

We compare MMKV with SharedPreferences and SQLite and repeat the learning operation 1k times. The relevant test code is in. The result becomes Android/MMKV/mmkvdemo / chart.

(the test operation machine is Huawei Mate 20 Pro 128G and Android 10, each group is repeated 1k times, and the time unit is ms)

It can be seen from the single performance process that the shared preferences & SQLite on MMKV has similar or superior performance in reading performance.

Multi process performance: MMMKV far exceeds multiprocesssharedpreferences & SQLite & SQLite in both write performance and read performance. MMKV is the best choice for Android multi process key value storage components.

3.3 Android access guide

3.3.1 import dependency

In your project's app_module corresponds to build Add the following dependencies to gradle:

dependencies {
    implementation 'com.tencent:mmkv:1.2.12'
}

3.3.2 initialization

The use of MMKV is very simple. All changes take effect immediately without calling sync and apply. Initialize MMKV when App starts, and set the root directory of MMKV (files/mmkv /), preferably in Application:

    public void onCreate() {
        super.onCreate();
        //Initialize MMKV
        String rootDir = MMKV.initialize(this);
        Log.e("SP","mmkv root: " + rootDir);
    }

3.3.3 write data

    private void putMMKV() {
        MMKVUtils.getInstance().encode("name","mmkv-Scc");
        MMKVUtils.getInstance().encode("age",40);
    }
    //For the method of saving data, we need to get the specific type of saved data, and then call different saving methods according to the type
    public void encode(String key, Object object) {
        if (object instanceof String) {
            mmkv.encode(key, (String) object);
        } else if (object instanceof Integer) {
            mmkv.encode(key, (Integer) object);
        } else if (object instanceof Boolean) {
            mmkv.encode(key, (Boolean) object);
        } else if (object instanceof Float) {
            mmkv.encode(key, (Float) object);
        } else if (object instanceof Long) {
            mmkv.encode(key, (Long) object);
        } else if (object instanceof Double) {
            mmkv.encode(key, (Double) object);
        } else if (object instanceof byte[]) {
            mmkv.encode(key, (byte[]) object);
        } else {
            mmkv.encode(key, object.toString());
        }
    }
    //Save any Parcelable type
    public void encode(String key, Parcelable parcelable) {
        Log.e("mmkv","Parcelable.start"+parcelable.getClass());
        boolean isTrue = mmkv.encode(key, parcelable);
        Log.e("mmkv","Parcelable.end"+isTrue);
    }

You will find that it is basically the same as the SharedReferences above. Of course, MMKV storage supports far more data types than SharedReferences. Supported data types

  • The following Java language base types are supported:

    • boolean,int,long,float,double,byte[]

  • The following Java classes and containers are supported:

    • String,Set<String>

    • Any Parcelable type

3.3.4 reading data

    private void getMMKV() {
        Log.e("SP","MMKVString:"+MMKVUtils.getInstance().decodeString("name"));
        Log.e("SP","MMKVInt:"+MMKVUtils.getInstance().decodeInt("age"));
    }
    public String decodeString(String key) {
        return mmkv.decodeString(key, "");
    }
    public Integer decodeInt(String key) {
        return mmkv.decodeInt(key, 0);
    }    

3.3.5 removing data

    private void removeMMKV() {
        MMKVUtils.getInstance().removeValueForKey("name");
        Log.e("SP","Remove:"+MMKVUtils.getInstance().decodeString("name"));
        Log.e("SP","MMKVInt:"+MMKVUtils.getInstance().decodeInt("age"));
    }
    //Remove a key pair
    public void removeValueForKey(String key) {
        mmkv.removeValueForKey(key);
    }

    //Remove multiple key pairs at the same time
    public void removeValuesForKeys(String[] strings) {
        mmkv.removeValuesForKeys(strings);
    }

    //Clear all key s
    public void clearAll() {
        mmkv.clearAll();
    }

3.3.6 add data extension

  • Different businesses need different storage. You can create your own instances separately.

  • If the business needs multi process access, the flag bit mmkv is added during initialization MULTI_ PROCESS_ MODE

3.4 Huawei mobile phones should use Tencent MMKV framework with caution

Problem Description:

Replace the native SharedPreference with MMKV. On some Huawei phones, the configuration file will be lost inexplicably, and the whole application will be like reinstallation.

Huawei mobile phones use Tencent MMKV framework with caution

Then you can customize the file storage path.

Default path: / data / data / com scc. Datastorage (your package name) / files/mmkv

    //Custom file path.
    String dir = getFilesDir().getAbsolutePath()+"mmkv-z2";
    String dirPath = MMKV.initialize(this,dir);
    
    //mmkv source code
    public static String initialize(Context context, String rootDir) {
        MMKVLogLevel logLevel = MMKVLogLevel.LevelInfo;
        return initialize(context, rootDir, (MMKV.LibLoader)null, logLevel);
    }

3.5 SharedPreferences migration

  • MMKV provides the importFromSharedPreferences() function to easily migrate data.

  • MMKV also implements SharedPreferences and SharedPreferences The two interface s of editor only need two or three lines of code during migration, and other CRUD operation codes do not need to be changed.

    private void importSharedPreferences() {
        //SharedPreferences preferences = getSharedPreferences("myData", MODE_PRIVATE);
        MMKV preferences = MMKV.mmkvWithID("myData");
        //Migrate old data
        {
            SharedPreferences old_man = getSharedPreferences("myData", MODE_PRIVATE);
            preferences.importFromSharedPreferences(old_man);
            old_man.edit().clear().commit();
        }
        //Same as before
        SharedPreferences.Editor editor = preferences.edit();
        editor.putBoolean("bool", true);
        editor.putInt("int", Integer.MIN_VALUE);
        editor.putLong("long", Long.MAX_VALUE);
        editor.putFloat("float", -3.14f);
        editor.putString("string", "hello, imported");
        HashSet<String> set = new HashSet<String>();
        set.add("W"); set.add("e"); set.add("C"); set.add("h"); set.add("a"); set.add("t");
        editor.putStringSet("string-set", set);
        //There is no need to call {commit()
        //editor.commit();
    }

3.6 macOS / Win32 / POSIX platform

Other platforms can access the official documents by themselves.

4, Related links

Android data full solution processing

Mmkv git address

Jetpack Preferences

Android data storage (I) - file storage

Android data store (III) - SQLite database instance

Topics: Java Android