SharedPreferences에 액세스하는 것이 UI 스레드에서 수행되어야합니까?


113

Gingerbread의 출시와 함께 저는 새로운 API 중 일부를 실험 해 왔으며 그중 하나는 StrictMode 입니다.

경고 중 하나가에 대한 것임을 알았습니다 getSharedPreferences().

이것은 경고입니다.

StrictMode policy violation; ~duration=1949 ms: android.os.StrictMode$StrictModeDiskReadViolation: policy=23 violation=2

getSharedPreferences()UI 스레드에서 수행 되는 호출에 대해 제공됩니다 .

SharedPreferences실제로 UI 스레드에서 액세스하고 변경 해야합니까 ?


저는 항상 UI 스레드에서 기본 설정 작업을 수행했습니다. IO 작업이기 때문에 의미가있는 것
같지만

답변:


184

나는 당신이 이미 그것을 가지고 놀기 때문에 기쁩니다!

참고할 사항 : (게으른 글 머리 기호 형식)

  • 이것이 최악의 문제라면 앱이 좋은 위치에있을 것입니다. :) 쓰기는 일반적으로 읽기보다 느리므로 commit () 대신 SharedPreferenced $ Editor.apply ()를 사용하고 있는지 확인하십시오. apply ()는 GB 및 비동기의 새로운 기능입니다 (그러나 항상 안전하고 수명주기 전환에주의). 리플렉션을 사용하여 GB +에서는 apply ()를, Froyo 이하에서는 commit ()을 조건부로 호출 할 수 있습니다. 이 작업을 수행하는 방법에 대한 샘플 코드가있는 블로그 게시물을 작성할 것입니다.

로드에 관해서는 ...

  • 일단로드되면 SharedPreferences는 싱글 톤이며 프로세스 전체에 캐시됩니다. 그래서 당신은 당신이 그것을 필요로하기 전에 당신이 그것을 기억할 수 있도록 가능한 한 빨리로드하기를 원합니다. (단순한 XML 파일 인 SharedPreferences를 사용하는 경우 크기가 작다고 가정하면 ...) 앞으로 일부 사용자가 버튼을 클릭 할 때 잘못하고 싶지는 않습니다.

  • 하지만 context.getSharedPreferences (...)를 호출 할 때마다 백업 XML 파일이 변경되었는지 확인하기 위해 통계가 표시되므로 어쨌든 UI 이벤트 중에 이러한 통계를 피하고 싶을 것입니다. 통계는 일반적으로 빠르며 (캐시되는 경우가 많음) yaffs는 동시성에 많은 영향을주지 않습니다 (그리고 yaffs에서 실행되는 많은 Android 기기 ... Droid, Nexus One 등). 디스크를 피한다면 , 다른 진행 중이거나 보류중인 디스크 작업에 뒤처지지 않도록합니다.

  • 따라서 onCreate () 중에 SharedPreferences를로드하고 동일한 인스턴스를 다시 사용하여 통계를 피하고 싶을 것입니다.

  • 그러나 onCreate () 중에 환경 설정이 필요하지 않은 경우로드 시간이 앱의 시작을 불필요하게 지연 시키므로 일반적으로 새 스레드를 시작하는 FutureTask <SharedPreferences> 하위 클래스와 같은 것을 .set로 설정하는 것이 좋습니다. () FutureTask 서브 클래스의 값. 그런 다음 필요할 때마다 FutureTask <SharedPreferences>의 멤버를 조회하고 .get ()하십시오. 나는 이것을 Honeycomb의 무대 뒤에서 투명하게 무료로 만들 계획입니다. 이 영역에서 모범 사례를 보여주는 몇 가지 샘플 코드를 릴리스하려고합니다.

다음 주에 StrictMode 관련 주제에 대한 향후 게시물을 보려면 Android 개발자 블로그를 확인하십시오.


와우, 출처에서 직접 그렇게 명확한 대답을 기대하지 않았습니다! 고맙습니다!
cottonBallPaws 2010

9
이 멋진 게시물의 새로운 독자를 위해 @Brad Fitzpatrick이 위에 언급 한 블로그 게시물 링크 아래에서 찾아보십시오 : Brad의 엄격 모드에 대한 Android 개발자 블로그 게시물 . 이 게시물에는 공유 환경 설정을 저장하기 위해 android 버전을 기반으로 apply (gingerbread부터) 또는 commit (froyo)을 사용하는 샘플 코드 링크도 있습니다. [조건부 적용 또는 커밋 사용] ( code.google.com/p/zippy-android / source / browse / trunk / examples /… )
tony m

4
여전히 ICS \ JB와 관련이 있습니까?
ekatz

5

공유 기본 설정에 액세스하는 것은 플래시 저장소에서 읽기 때문에 시간이 오래 걸릴 수 있습니다. 많이 읽으십니까? 아마도 SQLite 데이터베이스와 같은 다른 형식을 사용할 수 있습니다.

그러나 StrictMode를 사용하여 찾은 모든 것을 수정하지는 마십시오. 또는 문서를 인용하려면 :

하지만 StrictMode가 찾은 모든 것을 고쳐야한다고 생각하지 마세요. 특히 정상적인 활동 수명주기 동안 많은 경우의 디스크 액세스가 필요합니다. 실수로 한 일을 찾으려면 StrictMode를 사용하십시오. 하지만 UI 스레드의 네트워크 요청은 거의 항상 문제입니다.


6
그러나 SQLite는 플래시 스토리지에서 읽어야하는 파일도 아니지만 기본 설정 파일에 비해 더 크고 복잡한 파일입니다. 환경 설정과 관련된 데이터 양에 대해 환경 설정 파일이 SQLite 데이터베이스보다 훨씬 빠를 것이라고 가정했습니다.

맞습니다. Brad가 이미 언급했듯이 이것은 거의 항상 문제가되지 않습니다. 또한 SharedPreferences를 한 번로드하고 (FutureTask를 사용하는 스레드에서도 가능) 단일 인스턴스에 대한 가능한 액세스를 위해 유지하는 것이 좋습니다.
mreichelt

5

Brad의 대답에 대한 한 가지 미묘한 점 : onCreate ()에서 SharedPreferences를로드하더라도 공유 파일 환경 설정을 읽을 때까지 (백그라운드 스레드에서) getString () 등이 차단되기 때문에 백그라운드 스레드에서 값을 읽어야합니다.

public String getString(String key, String defValue) {
    synchronized (this) {
        awaitLoadedLocked();
        String v = (String)mMap.get(key);
        return v != null ? v : defValue;
    }
}

edit ()도 같은 방식으로 차단되지만 apply ()는 포 그라운드 스레드에서 안전 해 보입니다.

(여기에 넣어서 미안합니다. 나는 이것을 Brad의 대답에 대한 코멘트로 넣었을 것입니다. 그러나 나는 방금 가입했고 그렇게 할 충분한 평판이 없습니다.)


1

나는 이것이 오래된 질문이라는 것을 알고 있지만 내 접근 방식을 공유하고 싶습니다. 독서 시간이 길었고 공유 된 선호도와 글로벌 애플리케이션 클래스의 조합을 사용했습니다.

ApplicationClass :

public class ApplicationClass extends Application {

    private LocalPreference.Filter filter;

    public LocalPreference.Filter getFilter() {
       return filter;
    }

    public void setFilter(LocalPreference.Filter filter) {
       this.filter = filter;
    }
}

LocalPreference :

public class LocalPreference {

    public static void saveLocalPreferences(Activity activity, int maxDistance, int minAge,
                                            int maxAge, boolean showMale, boolean showFemale) {

        Filter filter = new Filter();
        filter.setMaxDistance(maxDistance);
        filter.setMinAge(minAge);
        filter.setMaxAge(maxAge);
        filter.setShowMale(showMale);
        filter.setShowFemale(showFemale);

        BabysitApplication babysitApplication = (BabysitApplication) activity.getApplication();
        babysitApplication.setFilter(filter);

        SecurePreferences securePreferences = new SecurePreferences(activity.getApplicationContext());
        securePreferences.edit().putInt(Preference.FILER_MAX_DISTANCE.toString(), maxDistance).apply();
        securePreferences.edit().putInt(Preference.FILER_MIN_AGE.toString(), minAge).apply();
        securePreferences.edit().putInt(Preference.FILER_MAX_AGE.toString(), maxAge).apply();
        securePreferences.edit().putBoolean(Preference.FILER_SHOW_MALE.toString(), showMale).apply();
        securePreferences.edit().putBoolean(Preference.FILER_SHOW_FEMALE.toString(), showFemale).apply();
    }

    public static Filter getLocalPreferences(Activity activity) {

        BabysitApplication babysitApplication = (BabysitApplication) activity.getApplication();
        Filter applicationFilter = babysitApplication.getFilter();

        if (applicationFilter != null) {
            return applicationFilter;
        } else {
            Filter filter = new Filter();
            SecurePreferences securePreferences = new SecurePreferences(activity.getApplicationContext());
            filter.setMaxDistance(securePreferences.getInt(Preference.FILER_MAX_DISTANCE.toString(), 20));
            filter.setMinAge(securePreferences.getInt(Preference.FILER_MIN_AGE.toString(), 15));
            filter.setMaxAge(securePreferences.getInt(Preference.FILER_MAX_AGE.toString(), 50));
            filter.setShowMale(securePreferences.getBoolean(Preference.FILER_SHOW_MALE.toString(), true));
            filter.setShowFemale(securePreferences.getBoolean(Preference.FILER_SHOW_FEMALE.toString(), true));
            babysitApplication.setFilter(filter);
            return filter;
        }
    }

    public static class Filter {
        private int maxDistance;
        private int minAge;
        private int maxAge;
        private boolean showMale;
        private boolean showFemale;

        public int getMaxDistance() {
            return maxDistance;
        }

        public void setMaxDistance(int maxDistance) {
            this.maxDistance = maxDistance;
        }

        public int getMinAge() {
            return minAge;
        }

        public void setMinAge(int minAge) {
            this.minAge = minAge;
        }

        public int getMaxAge() {
            return maxAge;
        }

        public void setMaxAge(int maxAge) {
            this.maxAge = maxAge;
        }

        public boolean isShowMale() {
            return showMale;
        }

        public void setShowMale(boolean showMale) {
            this.showMale = showMale;
        }

        public boolean isShowFemale() {
            return showFemale;
        }

        public void setShowFemale(boolean showFemale) {
            this.showFemale = showFemale;
        }
    }

}

MainActivity (애플리케이션에서 먼저 호출되는 활동) :

LocalPreference.getLocalPreferences(this);

설명 된 단계 :

  1. 메인 액티비티는 getLocalPreferences (this)를 호출합니다.-> 이것은 여러분의 환경 설정을 읽고, 여러분의 애플리케이션 클래스에 필터 객체를 설정하고 그것을 반환합니다.
  2. 응용 프로그램의 다른 곳에서 getLocalPreferences () 함수를 다시 호출하면 먼저 훨씬 빠른 응용 프로그램 클래스에서 사용할 수 없는지 확인합니다.

참고 : 응용 프로그램 전체 변수가 NULL과 다른지 항상 확인하십시오. 이유-> http://www.developerphil.com/dont-store-data-in-the-application-object/

응용 프로그램 개체는 메모리에 영원히 남아 있지 않으며 종료됩니다. 대중의 믿음과는 달리 앱이 처음부터 다시 시작되지 않습니다. Android는 새로운 Application 개체를 만들고 사용자가 이전에 있었던 활동을 시작하여 애플리케이션이 애초에 죽지 않았다는 착각을줍니다.

null을 확인하지 않으면 필터 개체에서 getMaxDistance ()를 호출 할 때 nullpointer가 throw되도록 허용합니다 (응용 프로그램 개체가 Android에 의해 메모리에서 스 와이프 된 경우).


0

SharedPreferences 클래스는 디스크의 XML 파일 내에서 일부 읽기 및 쓰기를 수행하므로 다른 IO 작업과 마찬가지로 차단 될 수 있습니다. 현재 SharedPreferences에 저장된 데이터의 양은 API 호출에 사용되는 시간과 리소스에 영향을줍니다. 최소한의 데이터 양의 경우 데이터를 가져 오거나 넣는 데 몇 밀리 초 (때로는 1 밀리 초 미만)이면됩니다. 그러나 전문가의 관점에서는 백그라운드에서 API 호출을 수행하여 성능을 향상시키는 것이 중요 할 수 있습니다. 비동기 SharedPreferences의 경우 Datum 라이브러리를 확인하는 것이 좋습니다 .

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.