sharedPreferences使用三分+彩平台开发及缓存机制分析

日常开发中经常使用三分+彩平台开发 Q1157880099 源码地址:haozym.com存储数据,非常简单快捷,下面我们将对此进行分析

一、基本使用
封装工具类方便调用

class SpUtils{
private val context:Context=MyApplication.instance()
private val sp:SharedPreferences=context.getSharedPreferences(KEY_SP,Context.MODE_PRIVATE)

fun putString(key:String,value:String){
    sp.edit().putString(key,value).commit()
}

fun getString(key:String):String{
   return sp.getString(key,"")
}
companion object {
    fun getInstance():SpUtils{
        return SpUtils()
    }

     const val KEY_SP:String="sharedPreferenceKey"
}

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
存储数据

SpUtils.getInstance().putString("onClick","onClick")
1
读取数据

val str=SpUtils.getInstance().getString("onClick")
Log.e("peter","sp获取的数据为-> $str")
1
2
打印结果

E/peter: sp获取的数据为-> onClick
1
可存储数据类型及方法

Editor putString(String key, @Nullable String value);

Editor putStringSet(String key, @Nullable Set<String> values);

Editor putInt(String key, int value);

Editor putLong(String key, long value);

Editor putFloat(String key, float value);

Editor putBoolean(String key, boolean value);

1
2
3
4
5
6
7
8
9
10
11
12
读取数据类型及方法

Map<String, ?> getAll();

String getString(String key, @Nullable String defValue);

Set<String> getStringSet(String key, @Nullable Set<String> defValues);

int getInt(String key, int defValue);

long getLong(String key, long defValue);

float getFloat(String key, float defValue);

boolean getBoolean(String key, boolean defValue);

1
2
3
4
5
6
7
8
9
10
11
12
13
14
清除sp数据方法

Editor clear();
1
二、缓存机制
getSharedPreferences ContextImpl中实现

@Override
public SharedPreferences getSharedPreferences(String name, int mode) {
// At least one application in the world actually passes in a null
// name. This happened to work because when we generated the file name
// we would stringify it to "null.xml". Nice.
if (mPackageInfo.getApplicationInfo().targetSdkVersion <
Build.VERSION_CODES.KITKAT) {
if (name == null) {
name = "null";
}
}

    File file;
    // 同步锁
    synchronized (ContextImpl.class) {
        if (mSharedPrefsPaths == null) {
            mSharedPrefsPaths = new ArrayMap<>();
        }
        // 获取存储数据文件 存储路径 /data/data/包名/shared_prefs/sharedPreferenceKey.xml
        file = mSharedPrefsPaths.get(name);
        if (file == null) {
            file = getSharedPreferencesPath(name);
            mSharedPrefsPaths.put(name, file);
        }
    }
    return getSharedPreferences(file, mode);
}

   @Override
public SharedPreferences getSharedPreferences(File file, int mode) {
    checkMode(mode);
    if (getApplicationInfo().targetSdkVersion >= android.os.Build.VERSION_CODES.O) {
        if (isCredentialProtectedStorage()
                && !getSystemService(StorageManager.class).isUserKeyUnlocked(
                        UserHandle.myUserId())
                && !isBuggy()) {
            throw new IllegalStateException("SharedPreferences in credential encrypted "
                    + "storage are not available until after user is unlocked");
        }
    }
    SharedPreferencesImpl sp;
    synchronized (ContextImpl.class) {
        final ArrayMap<File, SharedPreferencesImpl> cache = getSharedPreferencesCacheLocked();
        sp = cache.get(file);
        // 缓存数据
        if (sp == null) {
            sp = new SharedPreferencesImpl(file, mode);
            cache.put(file, sp);
            return sp;
        }
    }
    // 设置多进程状态
    if ((mode & Context.MODE_MULTI_PROCESS) != 0 ||
        getApplicationInfo().targetSdkVersion < android.os.Build.VERSION_CODES.HONEYCOMB) {
        // If somebody else (some other process) changed the prefs
        // file behind our back, we reload it.  This has been the
        // historical (if undocumented) behavior.

        // 返回时其他进程在之后修改了prefs,重新加载一边该数据
        sp.startReloadIfChangedUnexpectedly();
    }
    return sp;
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
SharedPreferencesImpl(file, mode)

SharedPreferencesImpl(File file, int mode) {
mFile = file;
mBackupFile = makeBackupFile(file);
mMode = mode;
mLoaded = false;
mMap = null;
// 从磁盘加载数据
startLoadFromDisk();
}

private void startLoadFromDisk() {
// 同步锁
    synchronized (mLock) {
        mLoaded = false;
    }
    // 开启子线程从磁盘加载数据
    new Thread("SharedPreferencesImpl-load") {
        public void run() {
            loadFromDisk();
        }
    }.start();
}

private void loadFromDisk() {
    synchronized (mLock) {
        if (mLoaded) {
            return;
        }
        // 如果备份的文件存在
        if (mBackupFile.exists()) {
            // 删除文件
            mFile.delete();
            // 文件重命名
            mBackupFile.renameTo(mFile);
        }
    }

    // Debugging
    if (mFile.exists() && !mFile.canRead()) {
        Log.w(TAG, "Attempt to read preferences file " + mFile + " without permission");
    }

    Map map = null;
    StructStat stat = null;
    try {
        stat = Os.stat(mFile.getPath());
        if (mFile.canRead()) {
            BufferedInputStream str = null;
            try {
            // 读取xml文件数据   
                str = new BufferedInputStream(
                        new FileInputStream(mFile), 16*1024);// 数据缓冲区为16M  将数据暂时存储到数据缓冲区
                map = XmlUtils.readMapXml(str);
            } catch (Exception e) {
                Log.w(TAG, "Cannot read " + mFile.getAbsolutePath(), e);
            } finally {
                IoUtils.closeQuietly(str);
            }
        }
    } catch (ErrnoException e) {
        /* ignore */
    }

    synchronized (mLock) {
        mLoaded = true;
        if (map != null) {
            mMap = map;
            mStatTimestamp = stat.st_mtime;
            mStatSize = stat.st_size;
        } else {
            mMap = new HashMap<>();
        }
        // 唤醒锁
        mLock.notifyAll();
    }
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
了解了创建sharepreference对象接下来看使用该对象存储数据

编辑数据我们使用Editor实现,而Editor是一个接口,具体实现交给其实现类

EditorImpl

public final class EditorImpl implements Editor {
private final Object mLock = new Object();

    @GuardedBy("mLock")
    private final Map<String, Object> mModified = Maps.newHashMap();

    @GuardedBy("mLock")
    private boolean mClear = false;

    public Editor putString(String key, @Nullable String value) {
        synchronized (mLock) {
            // 将数据存储到hashMap中
            mModified.put(key, value);
            return this;
        }
    }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
commit 将数据写入磁盘,并返回是否写入成功布尔值

public boolean commit() {
long startTime = 0;

        if (DEBUG) {
            startTime = System.currentTimeMillis();
        }
        // 内存缓存结果
        MemoryCommitResult mcr = commitToMemory();
        // 将内存缓存数据结果添加到队列中
        SharedPreferencesImpl.this.enqueueDiskWrite(
            mcr, null /* sync write on this thread okay */);
        try {
            // 等待写入磁盘
            mcr.writtenToDiskLatch.await();
        } catch (InterruptedException e) {
            return false;
        } finally {
            if (DEBUG) {
                Log.d(TAG, mFile.getName() + ":" + mcr.memoryStateGeneration
                        + " committed after " + (System.currentTimeMillis() - startTime)
                        + " ms");
            }
        }
        // 回调数据监听
        notifyListeners(mcr);
        // 将写入磁盘结果成功与否进行返回
        return mcr.writeToDiskResult;
    }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
继续查看commitToMemory() 将数据提交到内存中

// Returns true if any changes were made
private MemoryCommitResult commitToMemory() {
long memoryStateGeneration;
List<String> keysModified = null;
Set<OnSharedPreferenceChangeListener> listeners = null;
Map<String, Object> mapToWriteToDisk;

        synchronized (SharedPreferencesImpl.this.mLock) {
            // We optimistically don't make a deep copy until
            // a memory commit comes in when we're already
            // writing to disk.
            if (mDiskWritesInFlight > 0) {
                // We can't modify our mMap as a currently
                // in-flight write owns it.  Clone it before
                // modifying it.
                // noinspection unchecked
                mMap = new HashMap<String, Object>(mMap);
            }
            mapToWriteToDisk = mMap;
            mDiskWritesInFlight++;

            boolean hasListeners = mListeners.size() > 0;
            if (hasListeners) {
                keysModified = new ArrayList<String>();
                listeners = new HashSet<OnSharedPreferenceChangeListener>(mListeners.keySet());
            }

            synchronized (mLock) {
                boolean changesMade = false;

                if (mClear) {
                    if (!mMap.isEmpty()) {
                        changesMade = true;
                        mMap.clear();
                    }
                    mClear = false;
                }

                for (Map.Entry<String, Object> e : mModified.entrySet()) {
                    String k = e.getKey();
                    Object v = e.getValue();
                    // "this" is the magic value for a removal mutation. In addition,
                    // setting a value to "null" for a given key is specified to be
                    // equivalent to calling remove on that key.

                    // 值为空移除对应的键
                    if (v == this || v == null) {
                        if (!mMap.containsKey(k)) {
                            continue;
                        }
                        mMap.remove(k);
                    } else {
                        if (mMap.containsKey(k)) {
                            Object existingValue = mMap.get(k);
                            if (existingValue != null && existingValue.equals(v)) {
                                continue;
                            }
                        }
                        // 存储数据
                        mMap.put(k, v);
                    }

                    changesMade = true;
                    if (hasListeners) {
                        keysModified.add(k);
                    }
                }

                mModified.clear();

                if (changesMade) {
                    mCurrentMemoryStateGeneration++;
                }

                memoryStateGeneration = mCurrentMemoryStateGeneration;
            }
        }
        return new MemoryCommitResult(memoryStateGeneration, keysModified, listeners,
                mapToWriteToDisk);
    }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
notifyListeners(mcr) 通知监听

private void notifyListeners(final MemoryCommitResult mcr) {
        if (mcr.listeners == null || mcr.keysModified == null ||
            mcr.keysModified.size() == 0) {
            return;
        }
        if (Looper.myLooper() == Looper.getMainLooper()) {
            for (int i = mcr.keysModified.size() - 1; i >= 0; i--) {
                final String key = mcr.keysModified.get(i);
                for (OnSharedPreferenceChangeListener listener : mcr.listeners) {
                    if (listener != null) {
                        // 通过回调将内存提交结果传给sp监听
                        listener.onSharedPreferenceChanged(SharedPreferencesImpl.this, key);
                    }
                }
            }
        } else {
            // Run this function on the main thread.
            ActivityThread.sMainThreadHandler.post(new Runnable() {
                    public void run() {
                        notifyListeners(mcr);
                    }
                });
        }
    }
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
apply

public void apply() {
        final long startTime = System.currentTimeMillis();

        // 获取提交到内存中的数据
        final MemoryCommitResult mcr = commitToMemory();
        final Runnable awaitCommit = new Runnable() {
                public void run() {
                    try {
                        // 直接添加到写入磁盘等待
                        mcr.writtenToDiskLatch.await();
                    } catch (InterruptedException ignored) {
                    }

                    if (DEBUG && mcr.wasWritten) {
                        Log.d(TAG, mFile.getName() + ":" + mcr.memoryStateGeneration
                                + " applied after " + (System.currentTimeMillis() - startTime)
                                + " ms");
                    }
                }
            };

        QueuedWork.addFinisher(awaitCommit);

        Runnable postWriteRunnable = new Runnable() {
                public void run() {
                    awaitCommit.run();
                    QueuedWork.removeFinisher(awaitCommit);
                }
            };
        // 开启子线程写入到磁盘
        SharedPreferencesImpl.this.enqueueDiskWrite(mcr, postWriteRunnable);

        // Okay to notify the listeners before it's hit disk
        // because the listeners should always get the same
        // SharedPreferences instance back, which has the
        // changes reflected in memory.

        // 通知内存数据改变
        notifyListeners(mcr);
    }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
相较于commit方法,apply无返回值,直接开启子线程进行数据写入磁盘操作,线程不安全,但是相较于commit单线程写入效率会比较高

读取数据

@Nullable
public String getString(String key, @Nullable String defValue) {
synchronized (mLock) {
awaitLoadedLocked();
// 通过map键获取值
String v = (String)mMap.get(key);
// 如果值为空显示设置的默认值
return v != null ? v : defValue;
}
}
1
2
3
4
5
6
7
8
9
10
awaitLoadedLocked(); 等待锁,防止多线程问题造成数据错误

private void awaitLoadedLocked() {
if (!mLoaded) {
// Raise an explicit StrictMode onReadFromDisk for this
// thread, since the real read will be in a different
// thread and otherwise ignored by StrictMode.
BlockGuard.getThreadPolicy().onReadFromDisk();
}
while (!mLoaded) {
try {
mLock.wait();
} catch (InterruptedException unused) {
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
总结:
1、sharedPreferences可以存储多种数据类型
2、存储位置位于/data/data/包名/shared_prefs/sharedPreferenceKey.xml
3、sharedPreferences通过xml文件存储数据,获取的时候在通过解析xml文件获取
4、apply方法是异步处理,无返回值;commit方法是同步处理,返回布尔值


作者:VipPeterGee
来源:CSDN
原文:https://blog.csdn.net/sinat_35241409/article/details/88248819
版权声明:本文为博主原创文章,转载请附上博文链接!

猜你喜欢

转载自blog.51cto.com/14288323/2377895
今日推荐