【Android】【框架】【磁盘缓存】【SharedPreferences】

https://www.jianshu.com/p/9ae0f6842689?tdsourcetag=s_pctim_aiomsg

整体架构

初始化的时候会加载整个XML到内存中
作为内存缓存
这也是不能跨进程的原因,任何东西只要做缓存那就是2套数据了,即使SP做了多进程措施也无济于事

  1. 取:从内存取
  2. commit:直接同步执行(同时最多执行1个任务,多余1个任务,还是要入队)
  3. apply:入队

引起ANR的原因

取是不会有ANR的,只需要在最初加载文件的时候读取一次即可,后续都是从内存去取
commit直接在当前线程执行,虽说SP是轻量级的IO,但是至少也是IO操作,做个最简单的IO操作,也要几百ms
apply入队本是对此很不错的优化,但是最坑的一点是,Activity、Service、Broadcast都会在某些条件下等待SP队列全部执行完成
Google美名其曰确保IO可以顺利执行

image.png

可以参考Activity ANR的堆栈

【Android】【框架】【磁盘缓存】【SharedPreferences】_第1张图片
image.png

waitToFinish方法会加锁,等待queue清空

 public static void waitToFinish() {
        long startTime = System.currentTimeMillis();
        boolean hadMessages = false;

        Handler handler = getHandler();

        synchronized (sLock) {
            if (handler.hasMessages(QueuedWorkHandler.MSG_RUN)) {
                // Delayed work will be processed at processPendingWork() below
                handler.removeMessages(QueuedWorkHandler.MSG_RUN);

                if (DEBUG) {
                    hadMessages = true;
                    Log.d(LOG_TAG, "waiting");
                }
            }

            // We should not delay any work as this might delay the finishers
            sCanDelay = false;
        }

        StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskWrites();
        try {
            processPendingWork();
        } finally {
            StrictMode.setThreadPolicy(oldPolicy);
        }

        try {
            while (true) {
                Runnable finisher;

                synchronized (sLock) {
                    finisher = sFinishers.poll();
                }

                if (finisher == null) {
                    break;
                }

                finisher.run();
            }
        } finally {
            sCanDelay = true;
        }

        synchronized (sLock) {
            long waitTime = System.currentTimeMillis() - startTime;

            if (waitTime > 0 || hadMessages) {
                mWaitTimes.add(Long.valueOf(waitTime).intValue());
                mNumWaits++;

                if (DEBUG || mNumWaits % 1024 == 0 || waitTime > MAX_WAIT_TIME_MILLIS) {
                    mWaitTimes.log(LOG_TAG, "waited: ");
                }
            }
        }
    }

所以解决方式是反射清理等待锁

磁盘缓存框架选型

目前顶级项目很少会采用SP来进行磁盘缓存了,其ANR是一个绕不开的坎
先不说apply引起的ANR
很多时候,很多业务场景,对业务即时性的要求很高,所以必须采用commit的形式去做缓存
这个时候就对缓存框架的IO速度有极高的要求了
显然SP没有那么高效
微信开源的MMKV是值得使用的,虽然还有一些小坑会导致异常的ANR,但是整体上来说,是最顶级的缓存框架

原理

上面的整体架构有介绍原理了
也是比较易于理解,通俗易懂的
所以源码细节这里不再过多纠结了
想学习一些并发、IO的细节可以参考 https://mp.weixin.qq.com/s/qXQQb0IxrYeAFmrvXLydmA?client=tim&ADUIN=136284008&ADSESSION=1555676164&ADTAG=CLIENT.QQ.5603_.0&ADPUBNO=26882

后记

有什么写得错误、让人费解或遗漏的地方,希望可以不吝赐教,我会马上更改

学习自

https://mp.weixin.qq.com/s/qXQQb0IxrYeAFmrvXLydmA?client=tim&ADUIN=136284008&ADSESSION=1555676164&ADTAG=CLIENT.QQ.5603_.0&ADPUBNO=26882
https://www.jianshu.com/p/9ae0f6842689

你可能感兴趣的:(【Android】【框架】【磁盘缓存】【SharedPreferences】)