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 는 매우 크 고 메모리 에 문제 가 존재 하기 때문에 제목 으로 돌아 가 경량급 저장 은 크 고 복잡 한 데이터 저장 에 적합 하지 않 습 니 다.
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
Kotlin의 기초 - 2부지난 글에서는 Kotlin이 무엇인지, Kotlin의 특징, Kotlin에서 변수 및 데이터 유형을 선언하는 방법과 같은 Kotlin의 기본 개념에 대해 배웠습니다. 유형 변환은 데이터 변수의 한 유형을 다른 데이터...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.