SharedPreferences數(shù)據(jù)存儲

SharedPreferences簡介:適用于保存少量數(shù)據(jù)帝美,且這些數(shù)據(jù)的格式都是基本類型的值潦蝇。保存數(shù)據(jù)基于XML文件存儲的key-value鍵值對數(shù)據(jù)番捂。SharedPreferences對象本身只能獲取數(shù)據(jù)用踩,而不支持存儲和修改渠退,存儲和修改是通過SharedPreferences.edit()獲取的內(nèi)部類接口Editor對象實現(xiàn)。SharedPreferences本身是一個接口脐彩,應(yīng)用程序無法創(chuàng)建SharedPreferences實例碎乃,只能通過Context提供的getSharedPreferences(String name, int mode)方法來獲取SharedPreferences實例。
Editor基礎(chǔ)方法

  • putString:保存一個鍵值對
  • remove:刪除一個鍵值對
  • clear:清空SP數(shù)據(jù)
  • commit:同步提交修改數(shù)據(jù)
  • apply:異步提交修改數(shù)據(jù)

一惠奸、SharedPreferences基本使用

public class MainActivity extends AppCompatActivity {


    @SuppressLint("CommitPrefEdits")
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //MODE_PRIVATE數(shù)據(jù)只能被本應(yīng)用程序讀梅誓、寫
        //MODE_WORLD_READABLE數(shù)據(jù)能被其他應(yīng)用程序讀,但不能寫
        //MODE_WORLD_WRITEABLE數(shù)據(jù)能被其他應(yīng)用程序讀,寫
        SharedPreferences sharedPreferences = getSharedPreferences("name", MODE_PRIVATE);
        SharedPreferences.Editor editor = sharedPreferences.edit();
        //存儲數(shù)據(jù)
        editor.putString("key", "value");
        editor.apply();
        //獲取數(shù)據(jù)
        sharedPreferences.getString("key", "");

        //獲取其他應(yīng)用的SharedPreferences數(shù)據(jù)
        Context otherAppContent = null;
        try {
            //設(shè)置我們要調(diào)用數(shù)據(jù)的應(yīng)用包名
            otherAppContent = createPackageContext("com.example.datastorageframe", CONTEXT_IGNORE_SECURITY);
        } catch (PackageManager.NameNotFoundException e) {
            e.printStackTrace();
        }
        assert otherAppContent != null;
        @SuppressLint("WorldReadableFiles")
        SharedPreferences otherSharedPreferences = otherAppContent.getSharedPreferences("test", MODE_WORLD_READABLE);
        otherSharedPreferences.getString("name", "");
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
    }
}

創(chuàng)建SharedPreferences工具類

public class SharedPreferencesUtils {
    public static final String name = "test";
    private static SharedPreferencesUtils instance;


    public static SharedPreferencesUtils getInstance() {
        if (instance == null) {
            synchronized (SharedPreferencesUtils.class) {
                if (instance == null) {
                    instance = new SharedPreferencesUtils();
                }
            }
        }
        return instance;
    }

    /**
     * 保存數(shù)據(jù)梗掰,修改數(shù)據(jù)
     *
     * @param key
     * @param value
     * @param <V>
     */
    public <V> void setValue(@NonNull String key, V value) {
        SharedPreferences sp = MyApp.getInstance().getSharedPreferences(name, Context.MODE_PRIVATE);
        SharedPreferences.Editor editor = sp.edit();
        if (value instanceof String) {
            editor.putString(key, (String) value);
        } else if (value instanceof Integer) {
            editor.putInt(key, (Integer) value);
        } else if (value instanceof Long) {
            editor.putLong(key, (Long) value);
        } else if (value instanceof Boolean) {
            editor.putBoolean(key, (Boolean) value);
        } else if (value instanceof Float) {
            editor.putFloat(key, (Float) value);
        }
        editor.commit();
    }

    /**
     * 讀取數(shù)據(jù)
     *
     * @param key
     * @param defaultValue
     * @param <V>
     * @return
     */
    public <V> V getValue(@NonNull String key, V defaultValue) {
        SharedPreferences sp = MyApp.getInstance().getSharedPreferences(name, Context.MODE_PRIVATE);
        Object value = defaultValue;
        if (defaultValue instanceof String) {
            value = sp.getString(key, (String) defaultValue);
        } else if (defaultValue instanceof Integer) {
            value = sp.getInt(key, (Integer) defaultValue);
        } else if (defaultValue instanceof Long) {
            value = sp.getLong(key, (Long) defaultValue);
        } else if (defaultValue instanceof Boolean) {
            value = sp.getBoolean(key, (Boolean) defaultValue);
        } else if (defaultValue instanceof Float) {
            value = sp.getFloat(key, (Float) defaultValue);
        }
        return (V) value;
    }

    /**
     * 清空數(shù)據(jù)
     */
    public void clearData() {
        SharedPreferences.Editor editor = MyApp.getInstance().
                getSharedPreferences(name, Context.MODE_PRIVATE).edit();
        editor.clear();
        editor.commit();
    }
}

優(yōu)點:SharedPreferences對象與SQLite數(shù)據(jù)庫相比顯得格外輕量級嵌言,免去了創(chuàng)建數(shù)據(jù)庫,創(chuàng)建表及穗,寫SQL語句等諸多操作摧茴,相對而言更加方便,簡潔埂陆。
缺點:只能存儲boolean苛白,int,float焚虱,long和String五種簡單的數(shù)據(jù)類型购裙。無法進(jìn)行條件查詢等。

二著摔、源碼解析

(1)getSharedPreferences源碼

    @Override
    public SharedPreferences getSharedPreferences(String name, int mode) {
        //判空處理
        if (mPackageInfo.getApplicationInfo().targetSdkVersion <
                Build.VERSION_CODES.KITKAT) {
            if (name == null) {
                name = "null";
            }
        }

        File file;
        synchronized (ContextImpl.class) {
            if (mSharedPrefsPaths == null) {
                mSharedPrefsPaths = new ArrayMap<>();
            }
            //根據(jù)名字取文件
            file = mSharedPrefsPaths.get(name);
            if (file == null) {
                //根據(jù)名字創(chuàng)建文件
                file = getSharedPreferencesPath(name);
                //保存到ArrayMap中
                mSharedPrefsPaths.put(name, file);
            }
        }
        //根據(jù)文件獲取sp對象
        return getSharedPreferences(file, mode);
    }

    @Override
    public SharedPreferences getSharedPreferences(File file, int mode) {
        //sp實現(xiàn)類
        SharedPreferencesImpl sp;
        synchronized (ContextImpl.class) {
            //根據(jù)包名獲取ArrayMap<File, SharedPreferencesImpl>
            final ArrayMap<File, SharedPreferencesImpl> cache = getSharedPreferencesCacheLocked();
            sp = cache.get(file);
            if (sp == null) {
                checkMode(mode);
                if (getApplicationInfo().targetSdkVersion >= android.os.Build.VERSION_CODES.O) {
                    if (isCredentialProtectedStorage()
                            && !getSystemService(UserManager.class)
                                    .isUserUnlockingOrUnlocked(UserHandle.myUserId())) {
                        throw new IllegalStateException("SharedPreferences in credential encrypted "
                                + "storage are not available until after user is unlocked");
                    }
                }
                //實例化sp缓窜,并保存到緩存中
                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) {
            //如果是多進(jìn)程模式需要重新讀取文件
            sp.startReloadIfChangedUnexpectedly();
        }
        return sp;
    }

(2)SharedPreferencesImpl源碼

    SharedPreferencesImpl(File file, int mode) {
        mFile = file;
        //備份file
        mBackupFile = makeBackupFile(file);
        mMode = mode;
        mLoaded = false;
        mMap = null;
        mThrowable = null;
        startLoadFromDisk();
    }

    private void startLoadFromDisk() {
        synchronized (mLock) {
            mLoaded = false;
        }
        //開啟一個線程從磁盤讀取數(shù)據(jù)
        new Thread("SharedPreferencesImpl-load") {
            public void run() {
                loadFromDisk();
            }
        }.start();
    }

    private void loadFromDisk() {
        synchronized (mLock) {
            //是否讀取過
            if (mLoaded) {
                return;
            }
            //如果備份文件存在定续,刪除mFile谍咆,將備份文件重命名給mFile
            if (mBackupFile.exists()) {
                mFile.delete();
                mBackupFile.renameTo(mFile);
            }
        }
        Map<String, Object> map = null;
        StructStat stat = null;
        Throwable thrown = null;
        try {
            stat = Os.stat(mFile.getPath());
            if (mFile.canRead()) {
                BufferedInputStream str = null;
                try {
                    str = new BufferedInputStream(
                            new FileInputStream(mFile), 16 * 1024);
                    //將xml文件轉(zhuǎn)成map
                    map = (Map<String, Object>) XmlUtils.readMapXml(str);
                } catch (Exception e) {
                } finally {
                    IoUtils.closeQuietly(str);
                }
            }
        } catch (ErrnoException e) {
        } catch (Throwable t) {
            thrown = t;
        }

        synchronized (mLock) {
            //表示已經(jīng)讀取過,下次調(diào)用getSharedPreferences不會再從磁盤讀取
            mLoaded = true;
            mThrowable = thrown;
            try {
                if (thrown == null) {
                    if (map != null) {
                        //賦值給成員變量mMap
                        mMap = map;
                        mStatTimestamp = stat.st_mtim;
                        mStatSize = stat.st_size;
                    } else {
                        mMap = new HashMap<>();
                    }
                }
            } catch (Throwable t) {
                mThrowable = t;
            } finally {
                //釋放鎖
                mLock.notifyAll();
            }
        }
    }

    @Override
    @Nullable
    public String getString(String key, @Nullable String defValue) {
        synchronized (mLock) {
            //阻塞等待sp將xml讀取到內(nèi)存后再get
            awaitLoadedLocked();
            String v = (String)mMap.get(key);
            //如果value為空返回默認(rèn)值
            return v != null ? v : defValue;
        }
    }

    @GuardedBy("mLock")
    private void awaitLoadedLocked() {
        if (!mLoaded) {
            BlockGuard.getThreadPolicy().onReadFromDisk();
        }
        //sp讀取完成后會把mLoaded設(shè)置為true
        while (!mLoaded) {
            try {
                mLock.wait();
            } catch (InterruptedException unused) {
            }
        }
        if (mThrowable != null) {
            throw new IllegalStateException(mThrowable);
        }
    }

(3)Editor源碼

    @Override
    public Editor edit() {
        synchronized (mLock) {
            awaitLoadedLocked();
        }
        //每次都會返回一個新的Editor
        return new EditorImpl();
    }

    public final class EditorImpl implements Editor {
        //code...
        //將鍵值對寫入mModified
        @Override
        public Editor putString(String key, @Nullable String value) {
            synchronized (mEditorLock) {
                mModified.put(key, value);
                return this;
            }
        }

        @Override
        public Editor putInt(String key, int value) {
            synchronized (mEditorLock) {
                mModified.put(key, value);
                return this;
            }
        }
        @Override
        public Editor putLong(String key, long value) {
            synchronized (mEditorLock) {
                mModified.put(key, value);
                return this;
            }
        }
        @Override
        public Editor putFloat(String key, float value) {
            synchronized (mEditorLock) {
                mModified.put(key, value);
                return this;
            }
        }
        @Override
        public Editor putBoolean(String key, boolean value) {
            synchronized (mEditorLock) {
                mModified.put(key, value);
                return this;
            }
        }

        @Override
        public Editor remove(String key) {
            synchronized (mEditorLock) {
                mModified.put(key, this);
                return this;
            }
        }

        @Override
        public Editor clear() {
            synchronized (mEditorLock) {
                //根據(jù)這個判斷是否clear
                mClear = true;
                return this;
            }
        }

        @Override
        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);
        }

        @Override
        public boolean commit() {
            long startTime = 0;

            if (DEBUG) {
                startTime = System.currentTimeMillis();
            }
            //提交到內(nèi)存中私股,并返回mcr對象
            MemoryCommitResult mcr = commitToMemory();
            //放入寫入隊列
            SharedPreferencesImpl.this.enqueueDiskWrite(
                mcr, null /* sync write on this thread okay */);
            try {
                //線程等待摹察,直到寫入文件操作后
                mcr.writtenToDiskLatch.await();
            } catch (InterruptedException e) {
                return false;
            } finally {
            }
            //通知監(jiān)聽
            notifyListeners(mcr);
            //返回提交結(jié)果
            return mcr.writeToDiskResult;
        }

        private MemoryCommitResult commitToMemory() {
            long memoryStateGeneration;
            List<String> keysModified = null;
            Set<OnSharedPreferenceChangeListener> listeners = null;
            Map<String, Object> mapToWriteToDisk;

            synchronized (SharedPreferencesImpl.this.mLock) {
                if (mDiskWritesInFlight > 0) {
                    //這個時候說明正在寫入文件,無法修改倡鲸,寫入文件之后會重置為0
                    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 (mEditorLock) {
                    boolean changesMade = false;
                    //調(diào)用了clear方法的操作
                    if (mClear) {
                        if (!mapToWriteToDisk.isEmpty()) {
                            changesMade = true;
                            mapToWriteToDisk.clear();
                        }
                        mClear = false;
                    }

                    for (Map.Entry<String, Object> e : mModified.entrySet()) {
                        String k = e.getKey();
                        Object v = e.getValue();
                        //this就是為了判斷remove的
                        if (v == this || v == null) {
                            if (!mapToWriteToDisk.containsKey(k)) {
                                continue;
                            }
                            mapToWriteToDisk.remove(k);
                        } else {
                            if (mapToWriteToDisk.containsKey(k)) {
                                Object existingValue = mapToWriteToDisk.get(k);
                                //未發(fā)生修改跳過
                                if (existingValue != null && existingValue.equals(v)) {
                                    continue;
                                }
                            }
                            mapToWriteToDisk.put(k, v);
                        }

                        changesMade = true;
                        if (hasListeners) {
                            keysModified.add(k);
                        }
                    }
                    //將修改的map置空
                    mModified.clear();

                    if (changesMade) {
                        //當(dāng)前提交到內(nèi)存的次數(shù)
                        mCurrentMemoryStateGeneration++;
                    }

                    memoryStateGeneration = mCurrentMemoryStateGeneration;
                }
            }
            //將提交內(nèi)存次數(shù)供嚎,發(fā)生修改的集合,監(jiān)聽峭状,整個map封裝成MemoryCommitResult
            return new MemoryCommitResult(memoryStateGeneration, keysModified, listeners,
                    mapToWriteToDisk);
        }
        //code...
    }

    private void enqueueDiskWrite(final MemoryCommitResult mcr,
                                  final Runnable postWriteRunnable) {
        final boolean isFromSyncCommit = (postWriteRunnable == null);

        final Runnable writeToDiskRunnable = new Runnable() {
                @Override
                public void run() {
                    synchronized (mWritingToDiskLock) {
                        //寫入文件
                        writeToFile(mcr, isFromSyncCommit);
                    }
                    synchronized (mLock) {
                        //寫入完成后減1
                        mDiskWritesInFlight--;
                    }
                    if (postWriteRunnable != null) {
                        postWriteRunnable.run();
                    }
                }
            };
        if (isFromSyncCommit) {
            boolean wasEmpty = false;
            synchronized (mLock) {
                wasEmpty = mDiskWritesInFlight == 1;
            }
            if (wasEmpty) {
                //執(zhí)行寫入文件
                writeToDiskRunnable.run();
                return;
            }
        }
        //apply操作才會執(zhí)行
        QueuedWork.queue(writeToDiskRunnable, !isFromSyncCommit);
    }

修改值只會將其存到mModified的map中去克滴,所以在編輯器中所做的所有更改都會批處理,直到我們調(diào)用commit或apply才會設(shè)置到mMap和xml文件中优床。

  • SP文件不宜過大劝赔,如果SP文件需要存儲的內(nèi)容過多,可以根據(jù)不同的功能劃分成多個文件胆敞;
  • 如果可以的話盡可能早的調(diào)用getSharedPreferences,這樣在調(diào)用put和get操作時着帽,文件已經(jīng)被讀取到內(nèi)存中了;
  • 不要多次調(diào)用edit(), 應(yīng)該調(diào)用一次edit(),因為每次調(diào)用edit()都會新建一個Editor;
  • 不要多次調(diào)用commit()或apply()移层,如果多次存入值仍翰,應(yīng)該在最后一次調(diào)用。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末观话,一起剝皮案震驚了整個濱河市予借,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖灵迫,帶你破解...
    沈念sama閱讀 218,386評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件喧笔,死亡現(xiàn)場離奇詭異,居然都是意外死亡龟再,警方通過查閱死者的電腦和手機书闸,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,142評論 3 394
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來利凑,“玉大人浆劲,你說我怎么就攤上這事“С海” “怎么了牌借?”我有些...
    開封第一講書人閱讀 164,704評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長割按。 經(jīng)常有香客問我膨报,道長,這世上最難降的妖魔是什么适荣? 我笑而不...
    開封第一講書人閱讀 58,702評論 1 294
  • 正文 為了忘掉前任现柠,我火速辦了婚禮,結(jié)果婚禮上弛矛,老公的妹妹穿的比我還像新娘够吩。我一直安慰自己,他們只是感情好丈氓,可當(dāng)我...
    茶點故事閱讀 67,716評論 6 392
  • 文/花漫 我一把揭開白布周循。 她就那樣靜靜地躺著,像睡著了一般万俗。 火紅的嫁衣襯著肌膚如雪湾笛。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,573評論 1 305
  • 那天闰歪,我揣著相機與錄音嚎研,去河邊找鬼。 笑死课竣,一個胖子當(dāng)著我的面吹牛嘉赎,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播于樟,決...
    沈念sama閱讀 40,314評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼公条,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了迂曲?” 一聲冷哼從身側(cè)響起靶橱,我...
    開封第一講書人閱讀 39,230評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后关霸,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體传黄,經(jīng)...
    沈念sama閱讀 45,680評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,873評論 3 336
  • 正文 我和宋清朗相戀三年队寇,在試婚紗的時候發(fā)現(xiàn)自己被綠了膘掰。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,991評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡佳遣,死狀恐怖识埋,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情零渐,我是刑警寧澤窒舟,帶...
    沈念sama閱讀 35,706評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站诵盼,受9級特大地震影響惠豺,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜风宁,卻給世界環(huán)境...
    茶點故事閱讀 41,329評論 3 330
  • 文/蒙蒙 一洁墙、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧杀糯,春花似錦扫俺、人聲如沸苍苞。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,910評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽羹呵。三九已至骂际,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間冈欢,已是汗流浹背歉铝。 一陣腳步聲響...
    開封第一講書人閱讀 33,038評論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留凑耻,地道東北人太示。 一個月前我還...
    沈念sama閱讀 48,158評論 3 370
  • 正文 我出身青樓,卻偏偏與公主長得像香浩,于是被迫代替她去往敵國和親类缤。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,941評論 2 355

推薦閱讀更多精彩內(nèi)容