最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Android輕量級存儲SharedPreferences?MMKV?Jetpack?DataStore方案

 更新時(shí)間:2023年08月02日 15:14:59   作者:如愿以償丶  
這篇文章主要為大家介紹了Android輕量級存儲SharedPreferences?MMKV?Jetpack?DataStore方案示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

SharePreferences使用

SharedPreferences:一個輕量級的存儲類,特別適合用于保存應(yīng)用配置參數(shù)。(是用xml文件存放數(shù)據(jù),文件存放在/data/data/<package name>/shared_prefs目錄下)

SharedPreferences用法:

1.保存數(shù)據(jù)

保存數(shù)據(jù)一般分為以下步驟:

使用Activity類的getSharedPreferences方法獲得SharedPreferences對象;

使用SharedPreferences接口的edit獲得SharedPreferences.Editor對象;

通過SharedPreferences.Editor接口的putXXX方法保存key-value對;

通過過SharedPreferences.Editor接口的commit方法保存key-value對。

2.讀取數(shù)據(jù)

使用Activity類的getSharedPreferences方法獲得SharedPreferences對象;

通過SharedPreferences對象的getXXX方法獲取數(shù)據(jù);

3.示例

   //-------------------- SharePreferences -------------------------
    //獲取SharePreferences
    private val sp =
        context.applicationContext.getSharedPreferences(BOOK_PREFERENCES_NAME, MODE_PRIVATE)
    /**
     * SharePreferences 存數(shù)據(jù)
     */
    fun saveBookSP(book: BookBean) {
        //commit默認(rèn)為false,采用異步提交。
        sp.edit(commit = true) {
            putString(KEY_BOOK_NAME, book.name)
            putFloat(KEY_BOOK_PRICE, book.price)
            putString(KEY_BOOK_TYPE, book.type.name)
        }
    }
    /**
     * SharePreferences 獲取數(shù)據(jù)
     */
    val mBookInfo: BookBean
        get() {
            sp.apply {
                var bookName = getString(KEY_BOOK_NAME, "") ?: ""
                var bookPrice = getFloat(KEY_BOOK_PRICE, 0F)
                var bookStr = getString(KEY_BOOK_TYPE, Type.MATH.name)
                var bookType: Type = Type.valueOf(bookStr ?: Type.MATH.name)
                return BookBean(bookName, bookPrice, bookType)
            }
        }

SharedPreferences缺點(diǎn)

  • SP第一次加載數(shù)據(jù)時(shí)需要全量加載,當(dāng)數(shù)據(jù)量大時(shí)可能會阻塞UI線程造成卡頓
  • SP讀寫文件不是類型安全的,且沒有發(fā)出錯誤信號的機(jī)制,缺少事務(wù)性API
  • commit() / apply()操作可能會造成ANR問題:
    commit()是同步提交,會在UI主線程中直接執(zhí)行IO操作,當(dāng)寫入操作耗時(shí)比較長時(shí)就會導(dǎo)致UI線程被阻塞,進(jìn)而產(chǎn)生ANR;apply()雖然是異步提交,但異步寫入磁盤時(shí),如果執(zhí)行了Activity / Service中的onStop()方法,那么一樣會同步等待SP寫入完畢,等待時(shí)間過長時(shí)也會引起ANR問題。針對apply()我們展開來看一下:

SharedPreferencesImpl#EditorImpl.java中最終執(zhí)行了apply()函數(shù):

       @Override
        public void apply() {
            final long startTime = System.currentTimeMillis();
            final MemoryCommitResult mcr = commitToMemory();
            final Runnable awaitCommit = new Runnable() {
                    @Override
                    public void run() {
                        try {
                            //采用final CountDownLatch writtenToDiskLatch = new CountDownLatch(1);
                            mcr.writtenToDiskLatch.await();
                        } catch (InterruptedException ignored) {
                        }
                       ...
                    }
                };
            QueuedWork.addFinisher(awaitCommit);
            Runnable postWriteRunnable = new Runnable() {
                    @Override
                    public void run() {
                        awaitCommit.run();
                        QueuedWork.removeFinisher(awaitCommit);
                    }
                };
            //異步執(zhí)行磁盤寫入操作
            SharedPreferencesImpl.this.enqueueDiskWrite(mcr, postWriteRunnable);
            notifyListeners(mcr);
        }

創(chuàng)建一個awaitCommit的Runnable任務(wù)并將其加入到QueuedWork中,該任務(wù)內(nèi)部直接調(diào)用了CountDownLatch.await()方法,即直接在UI線程執(zhí)行等待操作,那么我們看QueuedWork中何時(shí)執(zhí)行這個任務(wù)。

QueuedWork.java:

public class QueuedWork {
  private static final LinkedList<Runnable> sFinishers = new LinkedList<>();
  public static void waitToFinish() {
     ...
     Handler handler = getHandler();
     try {
        //8.0之后優(yōu)化,會主動嘗試執(zhí)行寫磁盤任務(wù)
         processPendingWork();
     } finally {
         StrictMode.setThreadPolicy(oldPolicy);
     }
     try {
         while (true) {
             Runnable finisher;
             synchronized (sLock) {
                 //從隊(duì)列中取出任務(wù)
                 finisher = sFinishers.poll();
             }
             //如果任務(wù)為空,則跳出循環(huán),UI線程可以繼續(xù)往下執(zhí)行
             if (finisher == null) {
                 break;
             }
             //任務(wù)不為空,執(zhí)行CountDownLatch.await(),即UI線程會阻塞等待
             finisher.run();
         }
     } finally {
         sCanDelay = true;
     }
  }
 }

waitToFinish()方法會嘗試從Runnable任務(wù)隊(duì)列中取任務(wù),如果有的話直接取出并執(zhí)行,我們看看哪里調(diào)用了waitToFinish():

ActivityThread.java

 @Override
 public void handleStopActivity(IBinder token, int configChanges,
            PendingTransactionActions pendingActions, boolean finalStateRequest, String reason) {
     // Make sure any pending writes are now committed.
     if (!r.isPreHoneycomb()) {
          QueuedWork.waitToFinish();
     }
    }
private void handleStopService(IBinder token) {
    QueuedWork.waitToFinish();
}

可以看到在ActivityThread中handleStopActivity、handleStopService方法中都會調(diào)用waitToFinish()方法,即在Activity的onStop()中、Service的onStop()中都會先同步等待寫入任務(wù)完成才會繼續(xù)執(zhí)行。

所以apply()雖然是異步寫入磁盤,但是如果此時(shí)執(zhí)行到Activity/Service的onStop(),依然可能會阻塞UI線程導(dǎo)致ANR。

DataStore

Jetpack DataStore 是一種改進(jìn)的數(shù)據(jù)存儲解決方案,允許您使用協(xié)議緩沖區(qū)存儲鍵值對或類型化對象。

DataStore 使用 Kotlin 協(xié)程和 Flow 以異步、一致的事務(wù)方式存儲數(shù)據(jù)。并且可以對SP數(shù)據(jù)進(jìn)行遷移,旨在取代SP。如果正在使用SharedPreferences 存儲數(shù)據(jù),請考慮遷移到 DataStore。

Jetpack DataStore 有兩種實(shí)現(xiàn)方式:

  • Preferences DataStore:以鍵值對的形式存儲在本地類似 SharedPreferences 。
  • Proto DataStore:存儲類的對象(typed objects ),通過 protocol buffers 將對象序列化存儲在本地。

Preferences DataStore使用

1.添加依賴項(xiàng):

implementation 'androidx.datastore:datastore-preferences:1.0.0'

2.構(gòu)建Preferences DataStore:

/**
 * TODO:創(chuàng)建 Preferences DataStore
 *   參數(shù)1:name:創(chuàng)建Preferences DataStore文件名稱。
 *               會在/data/data/項(xiàng)目報(bào)名/files/下創(chuàng)建名為pf_dataastore的文件
 *   參數(shù)2:corruptionHandler:如果DataStore在試圖讀取數(shù)據(jù)時(shí),數(shù)據(jù)無法反序列化,會拋出androidx.datastore.core.CorruptionException,
 *                            此時(shí)會執(zhí)行corruptionHandler。
 *   參數(shù)3:produceMigrations:SP產(chǎn)生遷移到Preferences DataStore。ApplicationContext作為參數(shù)傳遞給這些回調(diào),遷移在對數(shù)據(jù)進(jìn)行任何訪問之前運(yùn)行。
 *   參數(shù)4:scope:協(xié)成的作用域,默認(rèn)IO操作在Dispatchers.IO線程執(zhí)行。
 */
val Context.dataStorePf: DataStore&lt;Preferences&gt; by preferencesDataStore(
    //文件名稱
    name = "preferences_dataStore")

當(dāng)我們構(gòu)建后,會在/data/data/<package name>/files/下創(chuàng)建名為preferences_dataStore的文件如下:

構(gòu)建Preferences DataStore

//常量
const val BOOK_PREFERENCES_NAME = "book_preferences"
const val KEY_BOOK_NAME = "key_book_name"
const val KEY_BOOK_PRICE = "key_book_price"
const val KEY_BOOK_TYPE = "key_book_type"
/**
 * TODO:創(chuàng)建 Preferences DataStore
 *   參數(shù)1:name:創(chuàng)建Preferences DataStore文件名稱。
 *               會在/data/data/項(xiàng)目報(bào)名/files/下創(chuàng)建名為pf_dataastore的文件
 *   參數(shù)2:corruptionHandler:如果DataStore在試圖讀取數(shù)據(jù)時(shí),數(shù)據(jù)無法反序列化,會拋出androidx.datastore.core.CorruptionException,
 *                            此時(shí)會執(zhí)行corruptionHandler。
 *   參數(shù)3:produceMigrations:SP產(chǎn)生遷移到Preferences DataStore。ApplicationContext作為參數(shù)傳遞給這些回調(diào),遷移在對數(shù)據(jù)進(jìn)行任何訪問之前運(yùn)行。
 *   參數(shù)4:scope:協(xié)成的作用域,默認(rèn)IO操作在Dispatchers.IO線程執(zhí)行。
 */
val Context.dataStorePf: DataStore<Preferences> by preferencesDataStore(
    name = "preferences_dataStore")

存儲的實(shí)體類

data class BookBean( var name: String = "",
                 var price: Float = 0f,
                 var type: Type = Type.ENGLISH) {
}

enum class Type{
    MATH,       //數(shù)學(xué)
    CHINESE,    //語文
    ENGLISH     //英語
}

數(shù)據(jù)存儲/獲取

Activity中

  //-------------------- Preferences DataStore -------------------------
    /**
     * TODO:Preferences DataStore  保存數(shù)據(jù)
     */
    fun savePD(view: View) {
        val book = BookBean("張三", 25f, Type.CHINESE)
        viewModel.saveBookPD(book)
    }

    /**
     * TODO:Preferences DataStore 獲取數(shù)據(jù)
     */
    fun getPD(view: View) {
        lifecycleScope.launch {
            viewModel.bookPfFlow.collect {
                tv_pd_data.text = it.toString()
            }
        }
    }

ViewModel中

//-------------------- Preferences DataStore -------------------------
    /**
     * TODO:Preferences DataStore 保存數(shù)據(jù) 必須在協(xié)程中進(jìn)行
     */
    fun saveBookPD(bookBean: BookBean) {
        viewModelScope.launch {
            dataStoreRepo.saveBookPD(bookBean)
        }
    }

    /**
     * TODO:Preferences DataStore 獲取數(shù)據(jù)
     */
    val bookPfFlow = dataStoreRepo.bookPDFlow

Repository類中

//-------------------- Preferences DataStore -------------------------
    /**
     * Preferences DataStore 存數(shù)據(jù)
     */
    suspend fun saveBookPD(book: BookBean) {
        context.dataStorePf.edit { preferences ->
            preferences[PreferenceKeys.P_KEY_BOOK_NAME] = book.name
            preferences[PreferenceKeys.P_KEY_BOOK_PRICE] = book.price
            preferences[PreferenceKeys.P_KEY_BOOK_TYPE] = book.type.name
        }
    }
    /**
     * Preferences DataStore 獲取數(shù)據(jù)
     */
    val bookPDFlow: Flow<BookBean> = context.dataStorePf.data
        .map { preferences ->
            // No type safety.
            val name = preferences[PreferenceKeys.P_KEY_BOOK_NAME] ?: ""
            val bookPrice = preferences[PreferenceKeys.P_KEY_BOOK_PRICE] ?: 0f
            val bookType =
                Type.valueOf(preferences[PreferenceKeys.P_KEY_BOOK_TYPE] ?: Type.MATH.name)
            return@map BookBean(name, bookPrice, bookType)
        }

SP遷移至Preferences DataStore如果想將項(xiàng)目的SP進(jìn)行遷移,只需要在Preferences DataStore在構(gòu)建時(shí)配置參數(shù)3,如下:

//SharedPreference文件名
const val BOOK_PREFERENCES_NAME = "book_preferences"
val Context.dataStorePf: DataStore<Preferences> by preferencesDataStore(
    name = "preferences_dataStore",
    //將SP遷移到Preference DataStore中
    produceMigrations = { context ->
        listOf(SharedPreferencesMigration(context, BOOK_PREFERENCES_NAME))
    })

這樣構(gòu)建完成時(shí),SP中的內(nèi)容也會遷移到Preferences DataStore中了,注意遷移是一次性的,即執(zhí)行遷移后,SP文件會被刪除.

MMKV

MMKV 是基于 mmap 內(nèi)存映射的 key-value 組件,底層序列化/反序列化使用 protobuf 實(shí)現(xiàn),性能高,穩(wěn)定性強(qiáng)。

原理

  • 內(nèi)存準(zhǔn)備通過 mmap 內(nèi)存映射文件,提供一段可供隨時(shí)寫入的內(nèi)存塊,App 只管往里面寫數(shù)據(jù),由操作系統(tǒng)負(fù)責(zé)將內(nèi)存回寫到文件,不必?fù)?dān)心 crash 導(dǎo)致數(shù)據(jù)丟失。
  • 數(shù)據(jù)組織數(shù)據(jù)序列化方面我們選用 protobuf 協(xié)議,pb 在性能和空間占用上都有不錯的表現(xiàn)。
  • 寫入優(yōu)化考慮到主要使用場景是頻繁地進(jìn)行寫入更新,我們需要有增量更新的能力。我們考慮將增量 kv 對象序列化后,append 到內(nèi)存末尾。
  • 空間增長使用 append 實(shí)現(xiàn)增量更新帶來了一個新的問題,就是不斷 append 的話,文件大小會增長得不可控。我們需要在性能和空間上做個折中。

使用

1.添加依賴:

implementation 'com.tencent:mmkv:1.2.13'

2.Application的onCreate方法中初始化

class App:Application() {
    override fun onCreate() {
        super.onCreate()
        val rootDir = MMKV.initialize(this)
        Log.e("TAG","mmkv root: $rootDir")
    }
}

3.數(shù)據(jù)存儲/獲?。?/strong>

MMKV kv = MMKV.defaultMMKV();
kv.encode("bool", true);
boolean bValue = kv.decodeBool("bool");
kv.encode("int", Integer.MIN_VALUE);
int iValue = kv.decodeInt("int");
kv.encode("string", "Hello from mmkv");
String str = kv.decodeString("string");

以上就是Android輕量級存儲SharedPreferences MMKV Jetpack DataStore方案的詳細(xì)內(nèi)容,更多關(guān)于Android輕量級存儲的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Kotlin圖文并茂講解續(xù)體與續(xù)體攔截器和調(diào)度器

    Kotlin圖文并茂講解續(xù)體與續(xù)體攔截器和調(diào)度器

    這篇文章主要介紹了Kotlin開發(fā)中續(xù)體與續(xù)體攔截器和調(diào)度器的相關(guān)使用,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2022-08-08
  • MobLink?Android?快速集成指南

    MobLink?Android?快速集成指南

    這篇文章主要為大家介紹了MobLink?Android?快速集成指南,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-10-10
  • 淺談Android LruCache的緩存策略

    淺談Android LruCache的緩存策略

    這篇文章主要介紹了淺談Android LruCache的緩存策略,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-04-04
  • 基于Flutter制作一個長按展示操作項(xiàng)面板的桌面圖標(biāo)

    基于Flutter制作一個長按展示操作項(xiàng)面板的桌面圖標(biāo)

    Flutter是一種強(qiáng)大的跨平臺移動應(yīng)用程序框架,它能夠幫助開發(fā)者輕松地創(chuàng)建漂亮、快速、高效的應(yīng)用程序,本文的主題是如何在Flutter中制作一個長按展示操作項(xiàng)面板的桌面圖標(biāo),在某些場景下,這個功能會讓應(yīng)用程序更加便利和易用
    2023-06-06
  • Android網(wǎng)絡(luò)判斷知識小結(jié)

    Android網(wǎng)絡(luò)判斷知識小結(jié)

    本文通過兩段實(shí)例代碼分別給大家介紹Android中判斷當(dāng)前網(wǎng)絡(luò)是否可用和Android 關(guān)于判斷應(yīng)用是否有網(wǎng)絡(luò)的相關(guān)知識,對android網(wǎng)絡(luò)判斷相關(guān)知識感興趣的朋友一起學(xué)習(xí)吧
    2015-12-12
  • android listview優(yōu)化幾種寫法詳細(xì)介紹

    android listview優(yōu)化幾種寫法詳細(xì)介紹

    這篇文章只是總結(jié)下getView里面優(yōu)化視圖的幾種寫法,需要的朋友可以參考下
    2012-11-11
  • 基于Flutter實(shí)現(xiàn)多邊形和多角星組件

    基于Flutter實(shí)現(xiàn)多邊形和多角星組件

    開發(fā)中,免不了會用到多邊形、多角星等圖案,比較常用的多邊形比如雷達(dá)圖、多角星比如評價(jià)星級的五角星等,本文章就使用Flutter繪制封裝一個這樣的組件,需要的可以參考一下
    2022-05-05
  • Android實(shí)現(xiàn)拍照、選擇相冊圖片并裁剪功能

    Android實(shí)現(xiàn)拍照、選擇相冊圖片并裁剪功能

    這篇文章主要為大家詳細(xì)介紹了Android實(shí)現(xiàn)拍照、選擇相冊圖片并裁剪功能的相關(guān)資料,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2016-12-12
  • Android音頻錄制MediaRecorder之簡易的錄音軟件實(shí)現(xiàn)代碼

    Android音頻錄制MediaRecorder之簡易的錄音軟件實(shí)現(xiàn)代碼

    這篇文章主要介紹了Android音頻錄制MediaRecorder之簡易的錄音軟件實(shí)現(xiàn)代碼,有需要的朋友可以參考一下
    2014-01-01
  • Android  AbsoluteLayout和RelativeLayout布局詳解

    Android AbsoluteLayout和RelativeLayout布局詳解

    本文主要講解Android AbsoluteLayout和RelativeLayout布局,這里整理了相關(guān)資料,并附示例代碼和效果圖,有興趣的小伙伴可以參考下
    2016-08-08

最新評論

乐陵市| 中方县| 临沧市| 洛南县| 缙云县| 崇信县| 右玉县| 响水县| 大余县| 泗阳县| 邵东县| 平定县| 铜山县| 濮阳县| 应城市| 随州市| 荆州市| 荆门市| 东乡县| 张家界市| 兴城市| 扎赉特旗| 徐水县| 温宿县| 巫山县| 三门县| 沙田区| 万山特区| 上蔡县| 奉新县| 彰武县| 平罗县| 惠来县| 灵璧县| 长春市| 邛崃市| 泽州县| 紫云| 东辽县| 重庆市| 盘锦市|