突然想到个问题,SharedPreferences线程安全么?有没有使用缓存相关的技术?
首先想到的是Activity里面的:
public abstract SharedPreferences getSharedPreferences(String name, int mode);
在android.content.Context中,我们首先找到简单的解释:
The single SharedPreferences instance that can be used to retrieve and modify the preference values.
关键字:单例,继续
1 // android.app.ContextImpl.java 2 private static final HashMap<String, SharedPreferencesImpl> sSharedPrefs = new HashMap<String, SharedPreferencesImpl>(); 3 @Override 4 public SharedPreferences getSharedPreferences(String name, int mode) { 5 SharedPreferencesImpl sp; 6 File prefsFile; 7 boolean needInitialLoad = false; 8 synchronized (sSharedPrefs) { 9 sp = sSharedPrefs.get(name); 10 if (sp != null && !sp.hasFileChangedUnexpectedly()) { 11 return sp; 12 } 13 prefsFile = getSharedPrefsFile(name); 14 if (sp == null) { 15 sp = new SharedPreferencesImpl(prefsFile, mode, null); 16 sSharedPrefs.put(name, sp); 17 needInitialLoad = true; 18 } 19 } 20 21 synchronized (sp) { 22 if (needInitialLoad && sp.isLoaded()) { 23 // lost the race to load; another thread handled it 24 return sp; 25 } 26 File backup = makeBackupFile(prefsFile); 27 if (backup.exists()) { 28 prefsFile.delete(); 29 backup.renameTo(prefsFile); 30 } 31 32 // Debugging 33 if (prefsFile.exists() && !prefsFile.canRead()) { 34 Log.w(TAG, "Attempt to read preferences file " + prefsFile + " without permission"); 35 } 36 37 Map map = null; 38 FileStatus stat = new FileStatus(); 39 if (FileUtils.getFileStatus(prefsFile.getPath(), stat) && prefsFile.canRead()) { 40 try { 41 FileInputStream str = new FileInputStream(prefsFile); 42 map = XmlUtils.readMapXml(str); 43 str.close(); 44 } catch (org.xmlpull.v1.XmlPullParserException e) { 45 Log.w(TAG, "getSharedPreferences", e); 46 } catch (FileNotFoundException e) { 47 Log.w(TAG, "getSharedPreferences", e); 48 } catch (IOException e) { 49 Log.w(TAG, "getSharedPreferences", e); 50 } 51 } 52 sp.replace(map, stat); 53 } 54 return sp; 55 }
认识1:getSharedPreferences通过Map保证SharedPreferences单例。
继续
//SharedPreferencesImpl.java 52 final class More ...SharedPreferencesImpl implements SharedPreferences { 53 private static final String TAG = "SharedPreferencesImpl"; 54 private static final boolean DEBUG = false; 55 56 // Lock ordering rules: 57 // - acquire SharedPreferencesImpl.this before EditorImpl.this 58 // - acquire mWritingToDiskLock before EditorImpl.this 59 60 private final File mFile; 61 private final File mBackupFile; 62 private final int mMode; 63 64 private Map<String, Object> mMap; // guarded by ‘this‘
mMap是不是editor的缓存?
//SharedPreferencesImpl.java 273 public Editor edit() { 274 // TODO: remove the need to call awaitLoadedLocked() when 275 // requesting an editor. will require some work on the 276 // Editor, but then we should be able to do: 277 // 278 // context.getSharedPreferences(..).edit().putString(..).apply() 279 // 280 // ... all without blocking. 281 synchronized (this) { 282 awaitLoadedLocked(); 283 } 284 285 return new EditorImpl(); 286 } 303 public final class EditorImpl implements Editor { 304 private final Map<String, Object> mModified = Maps.newHashMap(); 305 private boolean mClear = false; 306 307 public Editor putString(String key, String value) { 308 synchronized (this) { 309 mModified.put(key, value); 310 return this; 311 } 312 }
认识2:每次edit会new EditorImpl ,并且EditorImpl 自带mModified缓存。
388 // Returns true if any changes were made 389 private MemoryCommitResult commitToMemory() { 390 MemoryCommitResult mcr = new MemoryCommitResult(); 391 synchronized (SharedPreferencesImpl.this) { 392 // We optimistically don‘t make a deep copy until 393 // a memory commit comes in when we‘re already 394 // writing to disk. 395 if (mDiskWritesInFlight > 0) { 396 // We can‘t modify our mMap as a currently 397 // in-flight write owns it. Clone it before 398 // modifying it. 399 // noinspection unchecked 400 mMap = new HashMap<String, Object>(mMap); 401 } 402 mcr.mapToWriteToDisk = mMap; 403 mDiskWritesInFlight++; 404 405 boolean hasListeners = mListeners.size() > 0; 406 if (hasListeners) { 407 mcr.keysModified = new ArrayList<String>(); 408 mcr.listeners = 409 new HashSet<OnSharedPreferenceChangeListener>(mListeners.keySet()); 410 } 411 412 synchronized (this) { 413 if (mClear) { 414 if (!mMap.isEmpty()) { 415 mcr.changesMade = true; 416 mMap.clear(); 417 } 418 mClear = false; 419 } 420 421 for (Map.Entry<String, Object> e : mModified.entrySet()) { 422 String k = e.getKey(); 423 Object v = e.getValue(); 424 if (v == this) { // magic value for a removal mutation 425 if (!mMap.containsKey(k)) { 426 continue; 427 } 428 mMap.remove(k); 429 } else { 430 boolean isSame = false; 431 if (mMap.containsKey(k)) { 432 Object existingValue = mMap.get(k); 433 if (existingValue != null && existingValue.equals(v)) { 434 continue; 435 } 436 } 437 mMap.put(k, v); 438 } 439 440 mcr.changesMade = true; 441 if (hasListeners) { 442 mcr.keysModified.add(k); 443 } 444 } 445 446 mModified.clear(); 447 } 448 } 449 return mcr; 450 } 452 public boolean commit() { 453 MemoryCommitResult mcr = commitToMemory(); 454 SharedPreferencesImpl.this.enqueueDiskWrite( 455 mcr, null /* sync write on this thread okay */); 456 try { 457 mcr.writtenToDiskLatch.await(); 458 } catch (InterruptedException e) { 459 return false; 460 } 461 notifyListeners(mcr); 462 return mcr.writeToDiskResult; 463 }
commit时,editor先把mModified同步到mMap,然后再写入File。认识3:editor.commit是线程安全的。
564 private void writeToFile(MemoryCommitResult mcr) { 565 // Rename the current file so it may be used as a backup during the next read 566 if (mFile.exists()) { 567 if (!mcr.changesMade) { 568 // If the file already exists, but no changes were 569 // made to the underlying map, it‘s wasteful to 570 // re-write the file. Return as if we wrote it 571 // out. 572 mcr.setDiskWriteResult(true); 573 return; 574 } 575 if (!mBackupFile.exists()) { 576 if (!mFile.renameTo(mBackupFile)) { 577 Log.e(TAG, "Couldn‘t rename file " + mFile 578 + " to backup file " + mBackupFile); 579 mcr.setDiskWriteResult(false); 580 return; 581 } 582 } else { 583 mFile.delete(); 584 } 585 } 586 587 // Attempt to write the file, delete the backup and return true as atomically as 588 // possible. If any exception occurs, delete the new file; next time we will restore 589 // from the backup. 590 try { 591 FileOutputStream str = createFileOutputStream(mFile); 592 if (str == null) { 593 mcr.setDiskWriteResult(false); 594 return; 595 } 596 XmlUtils.writeMapXml(mcr.mapToWriteToDisk, str); 597 FileUtils.sync(str); 598 str.close(); 599 ContextImpl.setFilePermissionsFromMode(mFile.getPath(), mMode, 0); 600 try { 601 final StructStat stat = Libcore.os.stat(mFile.getPath()); 602 synchronized (this) { 603 mStatTimestamp = stat.st_mtime; 604 mStatSize = stat.st_size; 605 } 606 } catch (ErrnoException e) { 607 // Do nothing 608 } 609 // Writing was successful, delete the backup file if there is one. 610 mBackupFile.delete(); 611 mcr.setDiskWriteResult(true); 612 return; 613 } catch (XmlPullParserException e) { 614 Log.w(TAG, "writeToFile: Got exception:", e); 615 } catch (IOException e) { 616 Log.w(TAG, "writeToFile: Got exception:", e); 617 } 618 // Clean up an unsuccessfully written file 619 if (mFile.exists()) { 620 if (!mFile.delete()) { 621 Log.e(TAG, "Couldn‘t clean up partially-written file " + mFile); 622 } 623 } 624 mcr.setDiskWriteResult(false); 625 }
认识4:存在mBackupFile,直接操作mFile,如果出现问题,恢复备份。
总结:SharedPreferences使用时,单例,线程安全,存在缓存(二次加载快速)。
时间: 2024-09-29 02:34:39