SharedPreferences的commit和apply

SharedPreferences在安卓中是最常用的保存数据 的方式。

下面就了解一下SharedPreferences的commit和apply这两个提交数据的方法。

其实SharedPreferences和我们常用的Context都是接口,所以具体的实现方法其实是在SharedPreferencesImpl和ContextImpl中的

下面看一下源码:

public boolean commit() {
    MemoryCommitResult mcr = commitToMemory();//获取需要保存的数据集合
    SharedPreferencesImpl.this.enqueueDiskWrite(
        mcr, null /* sync write on this thread okay */);//提交数据,并进行保存操作
    try {
        mcr.writtenToDiskLatch.await();//将当前线程挂起,等待写入线程操作执行完成
    } catch (InterruptedException e) {
        return false;
    }
    notifyListeners(mcr);//通知数据
    return mcr.writeToDiskResult;
}
可以看到最主要的写入数据到本地的方法就是enqueueDisWrite,然后闭锁等待所有文件写入成功,最后在返回写入结果。再看apply方法

public void apply() {
    final MemoryCommitResult mcr = commitToMemory();//提取需要保存的数据
    final Runnable awaitCommit = new Runnable() {
            public void run() {
                try {
                    mcr.writtenToDiskLatch.await();//挂起该子线程,等待写入数据线程执行
                } catch (InterruptedException ignored) {
                }
            }
        };

    QueuedWork.add(awaitCommit);//将等待线程加到工作队列中

    Runnable postWriteRunnable = new Runnable() {
            public void run() {
                awaitCommit.run();
                QueuedWork.remove(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);
}
可以看到执行enqueueDiskWrite方法中传入了一个子线程runnable,接下来就是最重要的enqueueDiskWrite方法:

private void enqueueDiskWrite(final MemoryCommitResult mcr,
                              final Runnable postWriteRunnable) {
    final Runnable writeToDiskRunnable = new Runnable() {
            public void run() {
                synchronized (mWritingToDiskLock) {
                    writeToFile(mcr);//
                }
                synchronized (SharedPreferencesImpl.this) {
                    mDiskWritesInFlight--;
                }
                if (postWriteRunnable != null) {
                    postWriteRunnable.run();
                }
            }
        };//开启子线程写入数据到本地

    final boolean isFromSyncCommit = (postWriteRunnable == null);

    // Typical #commit() path with fewer allocations, doing a write on
    // the current thread.
    if (isFromSyncCommit) {
        boolean wasEmpty = false;
        synchronized (SharedPreferencesImpl.this) {
            wasEmpty = mDiskWritesInFlight == 1;
        }
        if (wasEmpty) {
            writeToDiskRunnable.run();//commit时直接在当前线程执行写数据任务
            return;
        }
    }

    QueuedWork.singleThreadExecutor().execute(writeToDiskRunnable);//apply时,开启单个线程池执行任务
}

变量isFromSyncCommit的值取决于是否传入一个子线程,如果传入了,就直接在当前线程中(有可能是主线程)执行写数据,然后返回,如果没有传入,就会通过QueuedWork.singleThreadExecutor创建一个单线程的线程池来执行写数据的线程.所以commit方法有可能会在主线程中执行造成线程阻塞,但是会同步读写数据;而apply则是直接将写数据丢到一个子线程中去执行.

因为apply中首先会将等待写入数据的线程加入到QuereWork中,

public static void waitToFinish() {
    Runnable toFinish;
    while ((toFinish = sPendingWorkFinishers.poll()) != null) {
        toFinish.run();
    }
}
当中的waitToFinish会轮寻线程,当然这个方法在activity的onPause中会调用,所以当并发apply的时候在这里轮循就可能导致anr异常。

其次,apply方法如果在应用进程退出的时候,因为开启了子线程执行,所以可能不会正确的保存数据。例如当apply之后调用

android.os.Process.killProcess(android.os.Process.myPid());

然后在进来的时候就会发现数据并没有被正确保存,但是commit确实可以的



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值