Android 경량급 저장 소 소스 분석

16027 단어 android

Android 는 SharePreference 데이터 저장 방식 인 경량급 데이터 저장 방식 을 제공 합 니 다.실질 적 으로 파일 저장 이다. XML 표준 에 맞 는 파일 저장 일 뿐 안 드 로 이 드 에서 자주 사용 되 는 간이 형 데이터 저장 솔 루 션 이다.다음은 소스 코드 의 실현 을 간단하게 분석 하고 소스 코드 는 모두 독립 된 것 이 며 실현 도 사람들의 인 코딩 습관 에 비교적 부합 되 며 다른 모듈 의 소스 코드 에 비해 비교적 간단 하 다.
작은 질문 을 남 겨 주세요. 1. 이렇게 기본 유형의 데 이 터 를 저장 하 는 데 문제 가 있 습 니까?
SharedPreferences sp = getSharedPreferences("sp_demo", Context.MODE_PRIVATE);
sp.edit().putString("name", "  ");
sp.edit().putInt("age", 11);
sp.edit().commit();

2. 이렇게 기본 유형의 데 이 터 를 저장 하 는 것 은 어떤 나 쁜 점 이 있 습 니까?
SharedPreferences sp = getSharedPreferences("sp_demo", Context.MODE_PRIVATE);
SharedPreferences.Editor editor = sp.editor();
editor.putString("name", "  ");
editor.commit();
//SharedPreferences.Editor editor = sp.editor();
editor.putInt("age", 11);
editor.commit();

Shared Preference 를 가 져 오 는 입구 getShared Preferences (String name, int mode) 부터 시작 합 니 다.
    @Override
    public SharedPreferences getSharedPreferences(String name, int mode) {
(mPackageInfo.getApplicationInfo().targetSdkVersion <
                Build.VERSION_CODES.KITKAT) {
            if (name == null) {
                name = "null";
            }
        }
        File file;
        synchronized (ContextImpl.class) {
            if (mSharedPrefsPaths == null) {
                mSharedPrefsPaths = new ArrayMap<>();
            }
            //     xml   , xml     ArrayMap 
            file = mSharedPrefsPaths.get(name);
            if (file == null) {
                file = getSharedPreferencesPath(name);
                mSharedPrefsPaths.put(name, file);
            }
        }
        return getSharedPreferences(file, mode);
    }

원래 우리 의 sp 대상 은 Array Map < String, File > mShared PrefsPaths 에 놓 여 있 었 습 니 다. name - File 이 연 결 된 것 같 습 니 다. 이 getShared Preferences Path (String name) 가 무엇 을 했 는 지 보 세 요.

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

원래 이 물건 은 xml 파일 을 만 드 는 것 이 었 습 니 다. getShared Preferences (String name, int mode) 와 결합 하면 첫 번 째 데 이 터 를 xml 에 저장 할 때 xml 파일 이 없 으 면 이 이름 의 xml 파일 을 만 드 는 것 을 이해 할 수 있 습 니 다. Shared Preference 대상 을 가 져 오 는 방법 을 계속 살 펴 보 겠 습 니 다.
   @Override
    public SharedPreferences getSharedPreferences(File file, int mode) {
        checkMode(mode);
        SharedPreferencesImpl sp;
        synchronized (ContextImpl.class) {
            final ArrayMap cache = getSharedPreferencesCacheLocked();
            sp = cache.get(file);
            if (sp == null) {
                sp = new SharedPreferencesImpl(file, mode);
                cache.put(file, sp);
                return sp;
            }
        }
       return sp;
    }

sp 대상 이 한 층 더 들 어 가 는 걸 보 려 면 왜 이렇게 힘 들 어, nm
private ArrayMap getSharedPreferencesCacheLocked() {
        if (sSharedPrefsCache == null) {
            sSharedPrefsCache = new ArrayMap<>();
        }

        final String packageName = getPackageName();
        ArrayMap packagePrefs = sSharedPrefsCache.get(packageName);
        if (packagePrefs == null) {
            packagePrefs = new ArrayMap<>();
            sSharedPrefsCache.put(packageName, packagePrefs);
        }

        return packagePrefs;
    }

원래 이 녀석 은 File 대상 을 Shared Preferences Impl 과 연결 시 켰 습 니 다. Array Map 에 존재 합 니 다. 그러면 우 리 는 작은 매듭 을 짓 고 fileName 의 xml 파일 을 명명 하면 File 이 생 성 되 기 때문에 fileName - file 대상 이 한 조각 에 연결 되 었 습 니 다. 이 어 File - Shared Preferences Impl 이 한 조각 에 연결 되 었 습 니 다.그러면 최종 모든 저장 논 리 는 Shared Preferences Impl 이라는 실현 클래스 에서 이 루어 질 것 이 라 고 추측 합 니 다. 계속 보 세 요.
  public Editor edit() {
        synchronized (mLock) {
            awaitLoadedLocked();
        }
        return new EditorImpl();
    }

이것 은 Editor 입 니 다. Shared Preferences Impl 의 내부 클래스 입 니 다. 인터페이스 입 니 다. 0.001 s 노출 방법 을 보 세 요.
//Editor           
  public interface Editor {
        Editor putString(String key, String value);
        Editor putStringSet(String key, Set values);
        Editor putInt(String key, int value);
        Editor putLong(String key, long value);
        Editor putFloat(String key, float value);
        Editor putBoolean(String key, boolean value);
        Editor remove(String key);
        Editor clear();
        boolean commit();
        void apply();
    }
   public final class EditorImpl implements Editor {
        //mModified     String--Object Map,        put   key--value,     ,   ,   (       )
        //      getXXX     mModified   ,       ,             sp.editor(),      new EditorImpl(),       mModified   map,            ,       
        private final Map mModified = Maps.newHashMap();

        private boolean mClear = false;
        
        //      boolean  ,       
        public Editor putBoolean(String key, boolean value) {
            synchronized (mLock) {
            //        ,      ,    ,    
                mModified.put(key, value);
                return this;
            }
        }
        
        public Editor remove(String key) {
            synchronized (mLock) {
                mModified.put(key, this);
                return this;
            }
        }
        
        public Editor clear() {
            synchronized (mLock) {
                mClear = true;
                return this;
            }
        }
        
        //    commit  
        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;
            } finally {
            }
            notifyListeners(mcr);
            return mcr.writeToDiskResult;
        }
        
        //        ,     
        private MemoryCommitResult commitToMemory() {
            //                     
            Map mapToWriteToDisk;
          //     mMap,  ,   m  ,    ,  ,         getXXX(String key)          map, app                
            mapToWriteToDisk = mMap;//        map,    map        xml          
                synchronized (mLock) {
                    boolean changesMade = false;
                    //mClear   ,    ,   clear()    mClear     ?.....  clear       
                    if (mClear) {
                        if (!mMap.isEmpty()) {
                            changesMade = true;
                            mMap.clear();
                        }
                        mClear = false;
                    }
                    for (Map.Entry e : mModified.entrySet()) {
                        String k = e.getKey();
                        Object v = e.getValue();
                        //map.remove       editor.remove()           map remove,     value == this  remove  ?
                        if (v == this || v == null) {
                            if (!mMap.containsKey(k)) {
                                continue;
                            }
                            mMap.remove(k);
                        } else {
                            if (mMap.containsKey(k)) {
                                Object existingValue = mMap.get(k);
                                if (existingValue != null && existingValue.equals(v)) {
                                    continue;
                                }
                            }
                            //                 put  ,         mMap  
                            mMap.put(k, v);
                        }
                        if (hasListeners) {
                            keysModified.add(k);
                        }
                    }
                    //  commit      map       
                    mModified.clear();
                    memoryStateGeneration = mCurrentMemoryStateGeneration;
                }
            }
            //         ,          ,                     
            return new MemoryCommitResult(memoryStateGeneration, keysModified, listeners,
                    mapToWriteToDisk);
        }
        
        //      ,    
    private static class MemoryCommitResult {
        final long memoryStateGeneration;
        @Nullable final List keysModified;
        @Nullable final Set listeners;
        final Map mapToWriteToDisk;
        final CountDownLatch writtenToDiskLatch = new CountDownLatch(1);

        @GuardedBy("mWritingToDiskLock")
        volatile boolean writeToDiskResult = false;
        boolean wasWritten = false;

        private MemoryCommitResult(long memoryStateGeneration, @Nullable List keysModified,
                @Nullable Set listeners,
                Map mapToWriteToDisk) {
            this.memoryStateGeneration = memoryStateGeneration;
            this.keysModified = keysModified;
            this.listeners = listeners;
            this.mapToWriteToDisk = mapToWriteToDisk;
        }

        void setDiskWriteResult(boolean wasWritten, boolean result) {
            this.wasWritten = wasWritten;
            writeToDiskResult = result;
            writtenToDiskLatch.countDown();
        }
    }
    
    //           ,        ,    ,          
    
    //      map  ,        disk 
     private void enqueueDiskWrite(final MemoryCommitResult mcr,final Runnable postWriteRunnable) {
     //      runnabl == null  comit,    apply  
        final boolean isFromSyncCommit=(postWriteRunnable == null);

        final Runnable writeToDiskRunnable = new Runnable() {
                public void run() {
                    synchronized (mWritingToDiskLock) {
                    //      disk     
                    writeToFile(mcr,isFromSyncCommit);
                    }
                    synchronized (mLock) {
                        mDiskWritesInFlight--;
                    }
                    if (postWriteRunnable != null) {
                        postWriteRunnable.run();
                    }
                }
            };
       
       //     ,  commit  
        if (isFromSyncCommit) {
            boolean wasEmpty = false;
            synchronized (mLock) {
                wasEmpty = mDiskWritesInFlight == 1;
            }
            if (wasEmpty) {
            //commit   ,          ,       write
                writeToDiskRunnable.run();
                return;
            }
        }
        //apply   ||    commit  ,          ,      commit         apply,   ?       commit,   UI       (     )
        QueuedWork.queue(writeToDiskRunnable, !isFromSyncCommit);
    }
    
    public void apply() {
    // commit  ,             mMap     ,          map
            final MemoryCommitResult mcr = commitToMemory();
            final Runnable awaitCommit = new Runnable() {
                    public void run() {
                        try {
 //          writtenToDiskLatch     ,       ,    mac         disk  ,          mc      ,                ?                         mcr.writtenToDiskLatch.await();
                        } catch (InterruptedException ignored) {
                        }
            QueuedWork.addFinisher(awaitCommit);
            Runnable postWriteRunnable = new Runnable() {
                    public void run() {
                        awaitCommit.run();
                        QueuedWork.removeFinisher(awaitCommit);
                    }
                };

     //    commit     ,runnable !=null,      
     SharedPreferencesImpl.this.enqueueDiskWrite(mcr, postWriteRunnable);
            notifyListeners(mcr);
        }
        
           apply commit         ,    getXX  mMap           ,          ,          mMap   Map  mWriteToDiskMap,      MemoryCommitResult,         xml,  xml   disk  ?        
 
 //     
  private void writeToFile(MemoryCommitResult mcr, boolean isFromSyncCommit) {
        //mFile      ,       File  ,          mFile.delete()  
        boolean fileExists = mFile.exists();
        if (fileExists) {
            boolean needsWrite = false;
            //mDiskStateGeneration      ,         MemoryCommitResult      
            if (mDiskStateGeneration < mcr.memoryStateGeneration) {
                if (isFromSyncCommit) {
                    needsWrite = true;
                } else {
                    synchronized (mLock) {
                      if (mCurrentMemoryStateGeneration == mcr.memoryStateGeneration) {
                            needsWrite = true;
                        }
                    }
                }
            }
            if (!needsWrite) {
                mcr.setDiskWriteResult(false, true);
                return;
            }
            boolean backupFileExists = mBackupFile.exists();
            if (!backupFileExists) {
                    mcr.setDiskWriteResult(false, false);
                    return;
                }
            } else {
                mFile.delete();
            }
        }
        try {
            FileOutputStream str = createFileOutputStream(mFile);
            if (str == null) {
                mcr.setDiskWriteResult(false, false);
                return;
            }
            // mMap   Map      mFile  ,mFile  ,waht?,    ContextImpl getSharedPreference(String name,Mode mode)    name,    mFile,  new SharedPreferenceImpl(File file,Mode mode),  mFile   ,     map   mFile ,   
            XmlUtils.writeMapXml(mcr.mapToWriteToDisk, str);
                // xml       disk 
            FileUtils.sync(str);
              str.close();
            ContextImpl.setFilePermissionsFromMode(mFile.getPath(), mMode, 0);
                try {
                final StructStat stat = Os.stat(mFile.getPath());
                synchronized (mLock) {
                    mStatTimestamp = stat.st_mtime;
                    mStatSize = stat.st_size;
                }
            } catch (ErrnoException e) {
                // Do nothing
            }
            mBackupFile.delete();
            mDiskStateGeneration = mcr.memoryStateGeneration;
            mcr.setDiskWriteResult(true, true);            mSyncTimes.add(Long.valueOf(fsyncDuration).intValue());
            mNumSync++;
            return;
        } catch (XmlPullParserException e) { 
        } catch (IOException e) {
        }

        if (mFile.exists()) {
            if (!mFile.delete()) {
            }
        }
        mcr.setDiskWriteResult(false, false);
    }




작은 매듭
1. Shared Preference 에서 데 이 터 를 저장 하 는 형식 은 xml 파일 이 고 생 성 할 때 서로 다른 name 은 서로 다른 xml 파일 에 대응 하 며 본질은 파일 읽 기와 쓰기 입 니 다.
2. Shared Preferences 의 Editor 에서 commt () 방법 으로 데 이 터 를 제출 하면 그 과정 은 먼저 데 이 터 를 메모리 에 업데이트 한 다음 에 현재 스 레 드 에 파일 을 작성 하 는 것 입 니 다. apply () 방법 으로 데 이 터 를 제출 하면 먼저 메모리 에 기록 합 니 다. 이 어 새로운 스 레 드 에서 비동기 로 파일 을 씁 니 다. 주의: commt 를 조작 할 때 잠 금 동작 이 있 기 때문에 효율 이 낮 습 니 다.만약 우리 가 한 번 에 여러 개의 수정 과 쓰기 작업 이 있 을 때, 대량 put 가 끝나 면 다시 한 번 확인 을 제출 하면 효율 을 높 일 수 있 습 니 다.
3. Shared Preferences 는 예화 할 때 먼저 disk 에서 파일 을 비동기 로 읽 은 다음 메모리 에 캐 시 합 니 다. 다음 읽 기 동작 은 파일 작업 이 아 닌 메모리 캐 시 작업 입 니 다.
4. 키 값 이 메모리 에 한 부 를 저장 하기 때문에 mMap 에 넣 고 메모리 로 getXXX (String key) 를 바 꿀 때 속도 가 향상 되 기 때문에 대량의 데 이 터 를 저장 하면 이 map 는 매우 크 고 메모리 에 문제 가 존재 하기 때문에 제목 으로 돌아 가 경량급 저장 은 크 고 복잡 한 데이터 저장 에 적합 하지 않 습 니 다.

좋은 웹페이지 즐겨찾기