前言:

使用SharePreferences是不支持在多个进程中操作数据的(不同进程之间的存取和读取,不同进程同时存储相同的数据都会出现问题),所以我们需要自己去实现跨进程的数据存储,但是很多人会指出,我们在创建SharePreferences的时候,官方明明提供了多线程操作的MODE_MULTI_PROCESS,难道不支持么?带着这个疑问我把官方对这部分的介绍贴出来:

int MODE_MULTI_PROCESS
This constant was deprecated in API level 23.
MODE_MULTI_PROCESS does not work reliably in some versions of Android, and furthermore does not provide any mechanism for reconciling concurrent modifications across processes. Applications should not attempt to use it. Instead, they should use an explicit cross-process data management approach such as ContentProvider.

上面已经明确的指出这个常数在API23的时候已经被弃用,并且推荐使用ContentProvider去实现此功能。
Tray是SharePrefrence的一个替代实现方案,提供了更加简单的方法去存储和读取,以及进程间数据的存储和读取。Tray明确的指出,跨进程的数据存储特性是基于ContentProvider的多线程数据存储方案。

使用说明:

一、在gradle中配置:

compile 'net.grandcentrix.tray:tray:0.12.0'

二、使用Tray存储和读取数据:

// 存储数据
AppPreferences appPreferences = new AppPreferences(this);
appPreferences.put(TRAY_KEY, "i am trayPreference param");
//读取数据
String param = appPreferences.getString(TRAY_KEY, "default");

从上面的使用中可以看到使用Tray后不需要再像SharePrefrence一样去获取Edior和apply(),一句话直接搞定,写法上相对简单好多。

三、如果你现在项目中接入的是SharePerefrence,但是突然有个要在不同进程中存储数据的功能需求,要使用Tray,那之前存储在sharepreference中的数据如何处理呢?

Tray还提供了一个功能就是合并SharePreference中的数据到Tray中。
使用方式,我们需要自己创建一个类,将其命名为ImportPreference,继承TrayPreferences,TrayPreferences提供了migrate()方法,
下面我们具体来看一下migrate()方法,这个方法主要是将SharePerference中的数据移动到Tray中存储,然后我们看一下方法中的参数值:migrate(Migration… migrations) 参数是Migration的实例值,那我们需要传入的就是Migration或其子类的实例,文档推荐使用SharedPreferencesImport:

SharedPreferencesImport importPref =new SharedPreferencesImport(getContext(), SharepreferenceName, prefKey, prefKey);
importPreference.migrate(importPref);

参数getContext():上下文对象
参数SharepreferenceName:SharePreference的name名称
参数prefKey:要移动的对象在SharePreference中的key名
参数prefKey:要移动的对象在TrayPreference中的key名

获取到SharedPreferencesImport的实例之后,使用我们自己创建的TrayPreferences的子类来调用migrate()方法,传入SharedPreferencesImport的实例值。

从Tray中取出被移动的数据:
这样我们就把SharePerefrence中的数据移动到Tray中,下次获取此数据的时候可以使用:

String param = appPreferences.getString(trayKey, “default”);

四、现在具体看一下Tray是如何对数据进行存储操作的,竟然so nb

项目中调用put方法存储value:AppPreferences.put(“key”,value) ,跟踪put进去,到Preferences类,我们看看处理的代码

@Override
public boolean put(@NonNull final String key, final float value) {if (!isVersionChangeChecked()) {return false;}v("put '" + key + "=" + value + "' into " + this);return putData(key, value);
}
private boolean putData(String key, Object value) {if (TextUtils.isEmpty(key)) {throw new IllegalArgumentException("Preference key value cannot be empty.");}return getStorage().put(key, value);}

那么这个getStorage()到底是什么? 一步步查找,最终发现是ContentProviderStorage这个类,这个类中的put方法又做了什么操作呢?

@Override
public boolean put(@NonNull final String key, @Nullable final String migrationKey,@Nullable final Object data) {if (getType() == Type.UNDEFINED) {throw new TrayRuntimeException("writing data into a storage with type UNDEFINED is forbidden. Only Read and delete is allowed.");}
final String value = data == null ? null : String.valueOf(data);
final Uri uri = mTrayUri.builder().setType(getType()).setModule(getModuleName()).setKey(key).build();return mProviderHelper.persist(uri, value, migrationKey);}

可以看到,重点代码是获取了一个uri,然后通过provuderHelper调用persist方法将uri和存储值value和替代key(migrationKey应该就是在将数据移动存储的情况下才会有值)。

public boolean persist(@NonNull final Uri uri, @Nullable String value,@Nullable final String previousKey) {ContentValues values = new ContentValues();values.put(TrayContract.Preferences.Columns.VALUE, value);values.put(TrayContract.Preferences.Columns.MIGRATED_KEY, previousKey);try {return mContext.getContentResolver().insert(uri, values) != null;} catch (Throwable e) {e.printStackTrace();return false;}}

重点在这里,Tray存储数据的时候果然是通过ContentProvider操作,机智啊。
再来看看数据读取时最后是如何操作的(TrayProviderHelper中):

@NonNull
public List<TrayItem> queryProvider(@NonNull final Uri uri) throws TrayException {final Cursor cursor;try {cursor = mContext.getContentResolver().query(uri, null, null, null, null);} catch (Throwable e) {throw new TrayException("Hard error accessing the ContentProvider", e);}// Return Preference if foundif (cursor == null) {// When running in here, please check if your ContentProvider has the correct authoritythrow new TrayException("could not access stored data with uri " + uri);}final ArrayList<TrayItem> list = new ArrayList<>();for (boolean hasItem = cursor.moveToFirst(); hasItem; hasItem = cursor.moveToNext()) {final TrayItem trayItem = cursorToTrayItem(cursor);list.add(trayItem);}cursor.close();return list;}

在ContentProvider中取出cursor之后放到ArrayList集合中,然后如何处理这个集合呢?在ContentProviderStorage类中:

@Override
@Nullable
public TrayItem get(@NonNull final String key) {final Uri uri = mTrayUri.builder().setType(getType()).setModule(getModuleName()).setKey(key).build();final List<TrayItem> prefs = mProviderHelper.queryProviderSafe(uri);final int size = prefs.size();if (size > 1) {TrayLog.w("found more than one item for key '" + key+ "' in module " + getModuleName() + ". "+ "This can be caused by using the same name for a device and user specific preference.");for (int i = 0; i < prefs.size(); i++) {final TrayItem pref = prefs.get(i);TrayLog.d("item #" + i + " " + pref);}}return size > 0 ? prefs.get(0) : null;}

最后返回的是集合的第1个Item,这里的每个item是Tray自定义的TrayItem类,那有是如何处理TrayItem类的呢?我们拿getString为例:

@Override
public String getString(@NonNull final String key) throws ItemNotFoundException {final TrayItem pref = getPref(key);if (pref == null) {throw new ItemNotFoundException("Value for Key <%s> not found", key);}return pref.value();
}

bingo ,取到了值。
到这里Tray使用ContenProvider的存储机制核心代码差不多就这些,能力有限,欢迎补充。

参考链接:https://github.com/grandcentrix/tray

Tray 轻量级数据存储 sharepreference的替代实现方案相关推荐

  1. 鸿蒙harmonyOS 使用轻量级数据存储Preferences出现的一个问题

    鸿蒙harmonyOS 使用轻量级数据存储Preferences出现的一个问题 文章目录 鸿蒙harmonyOS 使用轻量级数据存储Preferences出现的一个问题 前言 一.使用同一个上下文即可 ...

  2. android轻量级数据存储框架Hawk

    今天自己写demo学习的时候,发现了一个超级好用的数据存储框架Hawk,意外发现这个框架还可以存储List数据.下面总结用法如下: 1.在build.gradle下添加依赖 compile 'com. ...

  3. Android开发(二十四)——数据存储SharePreference、SQLite、File、ContentProvider

    Android提供以下四种存储方式: SharePreference SQLiteFile ContentProvider Android系统中数据基本都是私有的,一般存放在"data/da ...

  4. 使用share prefernces实现轻量级数据存储

    share prefernces就是个键值对的仓库. 由于share prefernces共享于整个应用,所以使用默认的就好. PreferenceManager.getDefaultSharedPr ...

  5. Android五大数据存储

    前言 数据存储可谓是Android中灰常灰常重要的一部分了.任何一个应用离不开数据的存储,有时需内存存储,有时需本地存储,还有时需要两个进程间传输数据,等等.那接下来介绍的五大存储中将包括了所有的应用 ...

  6. 实战干货|自研数据存储迁移MySQL实战

    背景   最近公司内部在做某自研数据存储的下线工作,这里我们暂且化名其为DistributeSQL,由于DistributeSQL不再进行服务支持,需要迁移项目中使用到该存储到其他数据存储中.   本 ...

  7. NSUserDefaults的用法(轻量级本地数据存储)

    NSUserDefaults适合存储轻量级的本地数据,比如要保存一个登陆界面的数据,用户名.密码之类的,个人觉得使用NSUserDefaults是首选.下次再登陆的时候就可以直接从NSUserDefa ...

  8. android数据存储心得,android学习心得 轻量级存储SharePreferences

    SharedPreferences是Android中存储简单数据的一个工具类.可以想象它是一个小小的Cookie,它通过用键值对的方式把简单数据类型(boolean.int.float.long和St ...

  9. IOS中NSUserDefaults的用法(轻量级本地数据存储)

    NSUserDefaults适合存储轻量级的本地数据,比如要保存一个登陆界面的数据,用户名.密码之类的,个人觉得使用NSUserDefaults是首选.下次再登陆的时候就可以直接从NSUserDefa ...

最新文章

  1. C#创建和调用DLL
  2. @OneToMany
  3. python中处理日期和时间的标准模块是-Python time模块参考手册
  4. 【安全牛学习笔记】SQLMAP- 自动注入
  5. Android ListView重写Adapter
  6. mac json格式化工具_简洁好用的工具都是相似的
  7. 嵌入式Linux系统之I.MX6触摸屏驱动程序TSC2007.C的分析、移植与校准
  8. 软件测试人员的三重境界
  9. 2021牛客暑期多校训练营9,签到题HE
  10. tf.nn.embedding_lookup,tf.variable系列变量
  11. mysql可视化工具选型
  12. 数字转为大写金额(C#)
  13. Pixel 3 的最佳照片功能
  14. 第九弹:计算机编程入门,免费学习资源,2020.07.05更新
  15. 关于光速不变与相对论的理解
  16. 计算机电路基础知识点,《计算机电路基础(1)》课教学经验点滴论文.doc
  17. wps表格户主序号_WPS表格怎么设置自动排列序号?
  18. 2015-10-16 Invoke 函数 InvokeRepeating函数 CancelInvoke取消Invoke函数
  19. 华为WATCH GT3正式开售,健康监测和运动管理我全都要
  20. 2023最新前端面试题3(持续更新)

热门文章

  1. window与linux环境下,java调用c语言的方法
  2. 使用python读取excel进行处理,并将结果存储在新的excel
  3. 上亚马逊自行车头盔、滑雪和滑板头盔标准要求
  4. 中国号码注册谷歌账号_亲测有效
  5. (二)分布式存储原理与设计
  6. 编程规范学习资料清单
  7. verilog产生随机数序列_总结verilog产生随机数的$random和seed
  8. java 数组--基础理论
  9. 自动化测试执行过程和报告内容分析
  10. 在硅谷这个神之眷顾的幸运小楼面前,谁都不算锦鲤...