日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

SharePreference源码学习和多进程的场景

發布時間:2025/4/16 编程问答 20 豆豆
生活随笔 收集整理的這篇文章主要介紹了 SharePreference源码学习和多进程的场景 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

復習了下SharePreference的使用,以及了解下SharePreference的源碼實現,解決多進程情況下的SharePreference問題,做下筆記。

參考文章:

源碼分析:

www.jianshu.com/p/8eb2147c3…

www.jianshu.com/p/3b2ac6201…

SharePreference的多進程解決方案:

juejin.im/entry/59083…

SharePreference

Android平臺中一個輕量級的存儲庫,用來保存應用程序的各種配置信息。本質是一個以“key-value”鍵值對的方式保存數據的xml文件。

文件保存地址:在/data/data/package name/shared_prefs目錄下就可以查看到這個文件了

簡單使用例子

//獲取得到SharePreference,第一個參數是文件名稱,第二個參數是操作模式//一般是MODE_PRIVATE模式,指定該SharedPreferences數據只能被本應用程序讀、寫SharedPreferences sharedPreferences = getSharedPreferences("test", MODE_PRIVATE);//創建Editor對象SharedPreferences.Editor editor=sharedPreferences.edit();//保存數據editor.putString("name","donggua");//editor.commit();editor.apply();//讀取數據String result=sharedPreferences.getString("name","默認值"); 復制代碼

commit和apply的區別

當使用commit去提交數據的時候,發現IDE提示讓我們使用apply方法。

  • commit:同步提交,commit將同步的把數據寫入磁盤和內存緩存,并且有返回值。
  • apply:異步提交,會把數據同步寫入內存緩存,然后異步保存到磁盤,可能會失敗,失敗不會收到錯誤回調。

兩者的區別:

  • commit的效率會比apply慢一點。在一個進程中,如果在不關心提交結果是否成功的情況下,優先考慮apply方法。
  • 都是原子性操作,但是原子的操作不同。commit的從數據提交到保存到內存后再保存到磁盤中,中間不可打斷。而apply方法是將數據保存到內存后就可以返回了,異步執行保存到磁盤的操作,

源碼分析

獲取SharePreference對象

利用Context獲取到SharePreference實例,ContextImpl是Context的實現類,實現了getSharedPreferences方法。

  • 因為SharedPreferences是支持自定義文件名的,所以這里利用了ArrayMap<File, SharedPreferencesImpl>來緩存不同文件對應的SharedPreferencesImpl對象。一個File文件對應一個SharePreference對象。
  • getSharedPreferencesCacheLocked(),獲取緩存的ArrayMap<File, SharedPreferencesImpl>對象,沒有則創建一個。
@Override public SharedPreferences getSharedPreferences(File file, int mode) {SharedPreferencesImpl sp;synchronized (ContextImpl.class) {//獲取緩存的mapfinal ArrayMap<File, SharedPreferencesImpl> cache = getSharedPreferencesCacheLocked();//拿到對應的文件的SharePreferencesp = 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");}}//沒有緩存,創建SharedPreferencesImpl對象,并保存到緩存中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.sp.startReloadIfChangedUnexpectedly();}return sp; }@GuardedBy("ContextImpl.class") private ArrayMap<File, SharedPreferencesImpl> getSharedPreferencesCacheLocked() {if (sSharedPrefsCache == null) {sSharedPrefsCache = new ArrayMap<>();}final String packageName = getPackageName();ArrayMap<File, SharedPreferencesImpl> packagePrefs = sSharedPrefsCache.get(packageName);if (packagePrefs == null) {packagePrefs = new ArrayMap<>();sSharedPrefsCache.put(packageName, packagePrefs);}return packagePrefs; } 復制代碼

SharedPreferencesImpl是SharedPreferences接口的實現類,實現了commit和apply方法。先看看SharedPreferencesImpl的構造方法。會異步調用一個startLoadFromDisk的方法,作用是從磁盤中把SharePreference文件里面保存的xml信息讀取到內存中,并保存到Map里面。

SharedPreferencesImpl(File file, int mode) {mFile = file;mBackupFile = makeBackupFile(file);mMode = mode;mLoaded = false;mMap = null;mThrowable = null;startLoadFromDisk(); }private void startLoadFromDisk() {synchronized (mLock) {mLoaded = false;}new Thread("SharedPreferencesImpl-load") {public void run() {loadFromDisk();}}.start(); }private void loadFromDisk(){ //省略部分代碼 try {stat = Os.stat(mFile.getPath());if (mFile.canRead()) {BufferedInputStream str = null;try {str = new BufferedInputStream(new FileInputStream(mFile), 16 * 1024);//進行xml解析map = (Map<String, Object>) XmlUtils.readMapXml(str);} catch (Exception e) {Log.w(TAG, "Cannot read " + mFile.getAbsolutePath(), e);} finally {IoUtils.closeQuietly(str);}} } catch (ErrnoException e) {// An errno exception means the stat failed. Treat as empty/non-existing by// ignoring. } catch (Throwable t) {thrown = t; } }//省略部分代碼。//將解析結果保存的map進行賦值 if (map != null) {mMap = map;mStatTimestamp = stat.st_mtim;mStatSize = stat.st_size; } 復制代碼

讀取數據

例如SharedPreferencesImpl的實現getString()方法,是直接從內存中的mMap直接就把數據讀取出來,并沒有涉及到磁盤操作。(恍然大悟,以前以為讀取數據也要去讀取file文件)

@Override @Nullable public String getString(String key, @Nullable String defValue) {synchronized (mLock) {awaitLoadedLocked();String v = (String)mMap.get(key);return v != null ? v : defValue;} } 復制代碼

保存數據

EditorImpl類實現了Editor接口。apply和commit都會調用commitToMemory方法,將數據保存到內存中,后面調用enqueueDiskWrite將數據保存到磁盤中。

//臨時緩存多個key的數據,后面提交數據的時候,就遍歷這個map就行 private final Map<String, Object> mModified = new HashMap<>(); //CountDownLatch,等待直到保存到磁盤的操作完成。 final CountDownLatch writtenToDiskLatch = new CountDownLatch(1);//在當前線程直接寫文件,調用await,同步等待,最后返回操作的結果result @Override 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; }//異步等待保存操作,無法獲取操作的結果 @Override public void apply() {final long startTime = System.currentTimeMillis();//保存到內存中final MemoryCommitResult mcr = commitToMemory();final Runnable awaitCommit = new Runnable() {@Overridepublic 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() {@Overridepublic 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); } 復制代碼

多進程中的SharePreference

上面講的默認是單進程中的SharePreference,讀取操作是直接從內存中的Map讀取的,不涉及IO操作。如果是在多進程中的話,不同進程之間的內存并不是共享的,這個時候讀寫同一個SharePreference就會出現問題了。比如多個進程對同一個sharedpreference進行修改,總會有一個進程獲取到的結果不是實時修改后的結果。

解決方法:推薦使用ContentProvider來處理多進程間的文件共享。

ContentProvider的特點:

  • ContentProvider內部的同步機制會防止多個進程同時訪問,避免數據沖突。
  • ContentProvider的數據源,并不是只能選擇數據庫,其實核心操作就在update()和query()這兩個操作,里面操作存取的數據源其實可以根據我們需要,替換成文件,也可以換成SharedPreferences。

所以我們可以使用ContentProvider做了一下中間媒介,讓它幫我們實現多進程同步機制,里面操作的數據改成SharedPreferences來實現。這樣的話就可以實現了跨進程訪問SharePreference。

下面簡單地寫一個demo,讀取的時候只需要傳進相應的uri就行了。比如下面的代碼,path字段的第二個是fileName,第三個是key值。

public class MultiProcessSharePreference extends ContentProvider{ @Nullable @Override public Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable String selection, @Nullable String[] selectionArgs, @Nullable String sortOrder) {//獲取xml的文件名,默認取path字段的第一個Log.d(TAG, "query: uri:" + uri);String tableName = uri.getPathSegments().get(0);String name = uri.getPathSegments().get(1);String key = uri.getPathSegments().get(2);Log.d(TAG, "query: tableName:" + tableName);Log.d(TAG, "query: fileName:" + name);Log.d(TAG, "query: key:" + key);//創建sharedPreferences對象SharedPreferences sharedPreferences = getContext().getSharedPreferences(name, Context.MODE_PRIVATE);//創建一個cursor對象MatrixCursor cursor = null;switch (uriMatcher.match(uri)) {case CODE_PREFERENCE_STRING:String value = sharedPreferences.getString(key, "默認值");cursor = new MatrixCursor(PREFERENCE_COLUMNS, 1);MatrixCursor.RowBuilder rowBuilder = cursor.newRow();rowBuilder.add(value);break;default:Log.d(TAG, "query: Uri No Match");}return cursor; } } 復制代碼
  • MatrixCursor: 如果需要一個cursor而沒有一個現成的cursor的話,那么可以使用MatrixCursor實現一個虛擬的表。MatrixCursor.RowBuilder是用來添加Row數據的,通過rowBuilder的add方法,就可以把數值添加到行里面了。使用場景:比如ContentProvider的query方法是返回一個cursor類型的數據,而數據源用的是SharePreference,這個時候就可以利用MatrixCursor。MartixCursor本質上是用一個一位數據來模擬一個二維數據,根據行值和列值就可以找到對應的數據了。

MatrixCursor的源碼解析:blog.csdn.net/zhang_jun_l…

//定義每一列的字段名字 public static final String COLUMN_VALUE = "value"; //創建一個字符數組,字符數組的值對應著表的字段 private static String[] PREFERENCE_COLUMNS = {COLUMN_VALUE}; //構造一個MatrixCursor對象 MatrixCursor cursor = new MatrixCursor(PREFERENCE_COLUMNS, 1); //通過matrixCursor的addRow方法添加一行值 MatrixCursor.RowBuilder rowBuilder = cursor.newRow(); rowBuilder.add(value); 復制代碼
  • 優化一下的思路:
    • 在ContentProvider里面加一個HashMap<String,SharePreference>進行一下緩存,key值是文件名,value是對應的SharePreference對象,這樣的話,就不用每次都去加載SharePreference對象了。
    • 在ContentProvider里面實現回調listener,在key值有變化的時候,進行通知訂閱者。

總結

以上是生活随笔為你收集整理的SharePreference源码学习和多进程的场景的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。

主站蜘蛛池模板: 久久艹综合 | 韩国一区二区在线观看 | 免费人成年激情视频在线观看 | 男人的天堂一区二区 | 麻豆精品| 夜夜狠 | 伊人影院综合在线 | 欧洲熟妇的性久久久久久 | 国产免费二区 | 天天摸天天摸 | 亚洲欧美日韩精品久久亚洲区 | 特级淫片裸体免费看 | 天天操天天摸天天干 | 国产毛片a | 亚洲精品久久久久久久久久吃药 | 一级黄色免费毛片 | 婷婷av一区二区三区 | 成人黄色小视频在线观看 | 香蕉尹人| 自拍偷拍视频网站 | 日韩人妻精品一区二区三区视频 | 一级黄网站 | 成了校长的性脔h文 | 怡春院视频 | 国产精品无码一区二区桃花视频 | 一边摸一边抽搐一进一出视频 | 蜜桃视频导航 | 91最新国产 | 国内成人av | 永久中文字幕 | 亚洲成人 av | 国产你懂 | 国产精品一区二区在线免费观看 | 91插插插影库永久免费 | 日本69式三人交 | 国产a级黄色 | 日韩影院在线 | 亚洲精品99久久久久中文字幕 | 99久久99久久精品国产片果冰 | 亚欧美在线观看 | 白嫩日本少妇做爰 | 欧美成人精品欧美一级私黄 | 久久久久亚洲av无码a片 | 日本高清网色 | 国产亚洲美女精品久久久2020 | 日本高清免费aaaaa大片视频 | 啪啪视屏 | 成人hd | 日韩欧美视频免费在线观看 | 人妻一区二区三区免费 | 日本精品影院 | av首页在线| 狂野欧美性猛交免费视频 | 国产一区二区三区在线播放无 | 致命魔术电影高清在线观看 | 国产农村妇女毛片精品久久 | 成人交性视频免费看 | 91av视频网站 | 亚洲视频国产 | 影音先锋中文在线 | 日韩人妻精品一区二区 | 在线播放一区 | 国产精品视频成人 | 精品欧美一区二区三区 | 亚洲国产精品自拍视频 | 国产精品免费无遮挡无码永久视频 | 男人插入女人下面的视频 | 丝袜制服影音先锋 | 中国一级大黄大黄大色毛片 | 国产最新地址 | 激情av网| 成人午夜视频精品一区 | 欧美一区二区三区不卡视频 | 一本一道久久a久久综合蜜桃 | 毛茸茸多毛bbb毛多视频 | 日本va视频 | 亚洲午夜小视频 | 色噜噜狠狠一区二区三区牛牛影视 | 欧美精品在线一区二区 | 亚洲欧美国产日韩精品 | 久久免费久久 | 亚洲成人第一网站 | 国产91影院| 欧美理论片在线观看 | julia一区二区三区中文字幕 | 成人午夜视频免费在线观看 | 欧美人与按摩师xxxx | 国产亚洲制服欧洲高清一区 | 欧美久久成人 | 91黄色大片 | jizz黄色片 | 3p视频在线 | 日本丰满少妇 | 亚洲午夜无码av毛片久久 | 欧美一区二区三区免费观看 | 亚洲天堂成人在线观看 | 美女色网站 | 韩国美女主播跳舞 | 一级黄色免费网站 |