Android—数据持久化、SP源码

3种数据持久化:

  • File:openFileInput(String fileName)、openFileOutput(String fileName, int mode)

不对存储的内容进行任何的格式化处理,比较合适存储一些简单的文本数据或二进制数据

  • SharedPreferences

使用键值对的方式来存储数据,保存数据更加方便。数据以明文的方式保存在文件中,需要加密,一般保存应用设置。

  • SQLite 继承SqLiteOpenHelper类创建数据库,

可以保存大量复杂的关系型数据 

账号密码自动登陆:

在项目中,我们一般使用SharePrefences实现自动登录的功能,在登录成功后,将数据(用户名,密码)保存在SharePrefences中,然后再次进入app时,判断SharePrefences中有无数据,有的话就跳到主页面,没有的话就跳到登录页。

SharedPreferences 的源码实现:

SharedPreferences 是线程安全的。

final class SharedPreferencesImpl implements SharedPreferences {
  // 1、使用注释标记锁的顺序
  // Lock ordering rules:
  //  - acquire SharedPreferencesImpl.mLock before EditorImpl.mLock
  //  - acquire mWritingToDiskLock before EditorImpl.mLock
 
  // 2、通过注解标记持有的是哪把锁
  @GuardedBy("mLock")
  private Map<String, Object> mMap;
 
  @GuardedBy("mWritingToDiskLock")
  private long mDiskStateGeneration;
 
  public final class EditorImpl implements Editor {
    @GuardedBy("mEditorLock")
    private final Map<String, Object> mModified = new HashMap<>();
  }
}

SharedPreferences 是由 Context 返回的,获取 SharedPreferences 的方法定义在抽象类 Context 中。

    public abstract SharedPreferences getSharedPreferences(String name, int mode);
    public abstract SharedPreferences getSharedPreferences(File file, int mode);

第一个方法是我们常用的,只要传入文件名,SP会自动寻找已有文件或创建,第二个是我们传入的文件。

所以 SharedPreferences 的操作,本质上就是对文件的操作,使用SharedPreferences时,只有第一次读取数据是有概率卡主线程几十到几百毫秒,而之后的读取时间几乎可以忽略不计。最后会落实到一个 xml 文件上。

    @Override
    public File getSharedPreferencesPath(String name) {
        return makeFilename(getPreferencesDir(), name + ".xml");
    }

标准路径在 /data/data/应用包名/shared_prefs 文件夹中,且都是 xml 文件。

commit 和 apply 的对比

EditorImpl 内部有一个内存缓存,用来保存用户修改后的操作:

    private final Map<String, Object> mModified = Maps.newHashMap();

在执行 commit 或者 apply 前,比如editor.putString("Key","Value")会把修改存储在 mModified 中。

    public Editor putString(String key, @Nullable String value) {
        synchronized (this) {
            mModified.put(key, value);
                return this;
            }
        }
    }

commit(同步):构造一个MemoryCommitResult来进行结果投递,在post任务以后会直接在当前线程进行wait。

apply(异步):构造一个MemoryCommitResult来调度IO(QueuedWork提供了singleThreadExecutor),apply()不会等待,而由QueuedWork的waitToFinish()方法的调用者(ActivityThread)来保证在某些时间点等待task的完成。

QueuedWork.singleThreadExecutor().execute(writeToDiskRunnable);

QueuedWork:一个内部工具类,用于跟踪那些未完成的或尚未结束的全局任务。由 waitToFinish 方法保证执行,在 Activity onStop 以及 Service 处理 onStop,onStartCommand 时等待写入操作,所有排队的异步任务都在一个独立、专用的线程上处理。平时使用的时候,尽量使用 apply 避免卡住主线程。

apply方法造成的ANR:

在apply()方法中,首先会创建一个等待锁,最终更新文件的任务会交给QueuedWork.singleThreadExecutor()单个线程或者HandlerThread去执行,当文件更新完毕后会释放锁。 但当Activity.onStop()以及Service处理onStop等相关方法时,则会执行 QueuedWork.waitToFinish()等待所有的等待锁释放,因此如果SharedPreferences一直没有完成更新任务,有可能会导致卡在主线程,最终超时导致ANR。

加载 xml 数据文件

 SharedPreferences 的加载流程,就是把文件的内容载入内存的过程。

    private void loadFromDisk() {
        ...
        str = new BufferedInputStream(
                new FileInputStream(mFile), 16*1024);
        map = XmlUtils.readMapXml(str);
        ...
    }

本质上,就是读取一个 xml 文件,被内容解析为 Map 对象。这个 map 包含了我们之前保存的所有键值对的数据。 

SharedPreferences 的读取非常快,载入完成后,后面的读操作都是针对 mMap 的,响应速度是内存级别的非常快。

SharedPreferences 不存放大量数据原因:

  • apply操作耗时过久会导致ANR
  • 如果数据量很大的话,返回的map对象会占很大一块内存
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值