SharedPreferences是以键值对的方式保存数据的,数据存放在xml文件中。
获取SharedPreferences
Context类的getSharedPreferences()方法。参数一:指定文件名称;参数二:指定操作模式,Android4.2之后只有MODE_PRIVATE一种模式。
Activity类的getPreferences()方法。文件名称默认是当前Activity的类名,接受一操作模式参数。
PreferenceManager类中的getDefaultSharedPerferences()方法。这是一个静态方法,接受一个Context参数,并自动使用当前应用程序的包名作为前缀命名文件名。
SharedPreferences的使用 SharedPreferences对象本身不支持存储和修改,只能获取数据,存储修改是通过SharedPreferences.edit()获取内部接口Editor对象实现。
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 1 )写入数据: SharedPreferences sharedPreferences= getSharedPreferences("data" ,Context.MODE_PRIVATE); SharedPreferences.Editor editor = sharedPreferences.edit(); editor.putString("name" , “Tom”); editor.putInt("age" , 28 ); editor.putBoolean("marrid" ,false ); editor.commit(); 2 )读取数据: SharedPreferences sharedPreferences= getSharedPreferences("data" , Context .MODE_PRIVATE); String userId=sharedPreferences.getString("name" ,"" ); 3 )删除指定数据 editor.remove("name" ); editor.commit(); 4 )清空数据 editor.clear(); editor.commit();
关键点
没有xml时会创建文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 public SharedPreferences getSharedPreferences (String name, int mode) { File file; synchronized (ContextImpl.class) { if (mSharedPrefsPaths == null ) { mSharedPrefsPaths = new ArrayMap <>(); } file = mSharedPrefsPaths.get(name); if (file == null ) { file = getSharedPreferencesPath(name); mSharedPrefsPaths.put(name, file); } } return getSharedPreferences(file, mode); }
SharedPreferences加载文件使用异步加载的方式。
1 2 3 4 5 6 private void startLoadFromDisk () { synchronized (this ) { mLoaded = false ; } new Thread ("SharedPreferencesImpl-load" ) { public void run () { loadFromDisk(); } }.start(); }
读文件的时候会等待一个锁,调用线程会被阻塞。因此,如果文件太大可能会导致ANR。
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 public String getString (String key, @Nullable String defValue) { synchronized (this ) { awaitLoadedLocked(); String v = (String)mMap.get(key); return v != null ? v : defValue; } } private void awaitLoadedLocked () { if (!mLoaded) { BlockGuard.getThreadPolicy().onReadFromDisk(); } while (!mLoaded) { try { mLock.wait(); } catch (InterruptedException unused) { } } if (mThrowable != null ) { throw new IllegalStateException (mThrowable); } }
写文件实际上会创建一个新的Map用于记录要修改的内容。
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 public Editor edit () { synchronized (this ) { awaitLoadedLocked(); } return new EditorImpl (); } public final class EditorImpl implements Editor { private final Map<String, Object> mModified = Maps.newHashMap(); private boolean mClear = false ; public Editor putString (String key, @Nullable String value) { synchronized (this ) { mModified.put(key, value); return this ; } } public Editor remove (String key) { synchronized (this ) { mModified.put(key, this ); return this ; } } public Editor clear () { synchronized (this ) { mClear = true ; return this ; } } }
commit和apply的差异 commit行是isFromSyncCommit,直接将runnable run了起来。是同步的过程。
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 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 public boolean commit () { MemoryCommitResult mcr = commitToMemory(); SharedPreferencesImpl.this .enqueueDiskWrite(mcr, null ); try { mcr.writtenToDiskLatch.await(); } catch (InterruptedException e) { return false ; } notifyListeners(mcr); return mcr.writeToDiskResult; } private MemoryCommitResult commitToMemory () { MemoryCommitResult mcr = new MemoryCommitResult (); synchronized (SharedPreferencesImpl.this ) { if (mDiskWritesInFlight > 0 ) { mMap = new HashMap <String, Object>(mMap); } mcr.mapToWriteToDisk = mMap; mDiskWritesInFlight++; boolean hasListeners = mListeners.size() > 0 ; if (hasListeners) { mcr.keysModified = new ArrayList <String>(); mcr.listeners = new HashSet <OnSharedPreferenceChangeListener>(mListeners.keySet()); } synchronized (this ) { if (mClear) { if (!mMap.isEmpty()) { mcr.changesMade = true ; mMap.clear(); } mClear = false ; } for (Map.Entry<String, Object> e : mModified.entrySet()) { String k = e.getKey(); Object v = e.getValue(); 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); } mcr.changesMade = true ; if (hasListeners) { mcr.keysModified.add(k); } } mModified.clear(); } } return mcr; } private void enqueueDiskWrite (final MemoryCommitResult mcr, final Runnable postWriteRunnable) { final Runnable writeToDiskRunnable = new Runnable () { public void run () { synchronized (mWritingToDiskLock) { writeToFile(mcr); } synchronized (SharedPreferencesImpl.this ) { mDiskWritesInFlight--; } if (postWriteRunnable != null ) { postWriteRunnable.run(); } } }; final boolean isFromSyncCommit = (postWriteRunnable == null ); if (isFromSyncCommit) { boolean wasEmpty = false ; synchronized (SharedPreferencesImpl.this ) { wasEmpty = mDiskWritesInFlight == 1 ; } if (wasEmpty) { writeToDiskRunnable.run(); return ; } } QueuedWork.singleThreadExecutor().execute(writeToDiskRunnable); }
apply是异步的过程,实际使用HandlerThread去执行的。
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 public void apply () { final long startTime = System.currentTimeMillis(); final MemoryCommitResult mcr = commitToMemory(); final Runnable awaitCommit = new Runnable () { @Override public void run () { try { mcr.writtenToDiskLatch.await(); } catch (InterruptedException ignored) { } } }; QueuedWork.addFinisher(awaitCommit); Runnable postWriteRunnable = new Runnable () { @Override public void run () { awaitCommit.run(); QueuedWork.removeFinisher(awaitCommit); } }; SharedPreferencesImpl.this .enqueueDiskWrite(mcr, postWriteRunnable); notifyListeners(mcr); }
1 QueuedWork.singleThreadExecutor().execute(writeToDiskRunnable);
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 public static void queue (Runnable work, boolean shouldDelay) { Handler handler = getHandler(); synchronized (sLock) { sWork.add(work); if (shouldDelay && sCanDelay) { handler.sendEmptyMessageDelayed(QueuedWorkHandler.MSG_RUN, DELAY); } else { handler.sendEmptyMessage(QueuedWorkHandler.MSG_RUN); } } } private static Handler getHandler () { synchronized (sLock) { if (sHandler == null ) { HandlerThread handlerThread = new HandlerThread ("queued-work-looper" , Process.THREAD_PRIORITY_FOREGROUND); handlerThread.start(); sHandler = new QueuedWorkHandler (handlerThread.getLooper()); } return sHandler; } }
apply() 没有返回值而 commit() 返回 boolean 表明修改是否提交成功
commit() 是把内容同步提交到硬盘的,而 apply() 先立即把修改提交到内存,然后开启一个异步的线程提交到硬盘,并且如果提交失败,你不会收到任何通知。
get 操作都是线程安全的, 并且 get 仅仅是从内存中 (mMap) 获取数据
SharedPreferences不支持多进程,支持多线程。
SharedPreference的文件备份机制 SharedPreference的写入操作正式执行之前,首先会对文件进行备份,写入成功删除备份文件。若异常终止,则将备份文件重命名为源文件。
1 2 3 4 5 6 private void writeToFile (...) { if (!backupFileExists) { !mFile.renameTo(mBackupFile); } }
1 2 3 4 5 6 7 8 9 private void loadFromDisk () { synchronized (mLock) { if (mBackupFile.exists()) { mFile.delete(); mBackupFile.renameTo(mFile); } } }