SharedPreferencesへのアクセスはUIスレッドから行う必要がありますか?


112

Gingerbreadのリリースに伴い、新しいAPIのいくつかを試しています。そのうちの1つはStrictModeです。

警告の1つがに対するものであることに気付きましたgetSharedPreferences()

これは警告です:

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

そしてそれgetSharedPreferences()は、UIスレッドで行われる呼び出しに対して与えられます。

SharedPreferencesアクセスと変更は本当にUIスレッドから行う必要がありますか?


私は常にUIスレッドで設定操作を行いました。私はそれがIO操作だから、それは理にかなっていると思いますが
Falmarri

回答:


184

すでに遊んでいてうれしいです!

注意すべきいくつかのこと:( 怠惰な弾丸の形で)

  • これがあなたの問題の最悪の場合、あなたのアプリはおそらく良い場所にあります。:)書き込みは一般に読み取りよりも遅いため、commit()ではなくSharedPreferenced $ Editor.apply()を使用していることを確認してください。apply()はGBおよび非同期の新機能です(ただし、常に安全で、ライフサイクルの移行には注意してください)。リフレクションを使用して、条件付きでGB +でapply()を呼び出し、Froyo以下でcommit()を呼び出すことができます。これを行う方法のサンプルコードでブログ投稿を行います。

読み込みに関しては...

  • 読み込まれると、SharedPreferencesはシングルトンであり、プロセス全体でキャッシュされます。できるだけ早くロードしたいので、必要になる前にメモリに保存しておきます。(SharedPreferencesを使用している場合と同じように、単純なXMLファイルであると想定して、小さいと仮定します。)将来、一部のユーザーがボタンをクリックしたときに、失敗することはありません。

  • ただし、context.getSharedPreferences(...)を呼び出すたびに、バッキングXMLファイルが変更されているかどうかを確認するために統計情報が表示されるため、UIイベント中にこれらの統計情報を回避する必要があります。統計は通常高速です(そしてしばしばキャッシュされます)が、yaffは同時実行性にあまり影響を与えません(そして多くのAndroidデバイスはyaffで実行されます... Droid、Nexus Oneなど)。 、他の処理中または保留中のディスク操作に遅れをとることを回避できます。

  • そのため、おそらく、onCreate()中にSharedPreferencesをロードし、同じインスタンスを再利用して、統計を回避する必要があります。

  • しかし、onCreate()の間にいずれの設定も必要ない場合は、その読み込み時間がアプリの起動を不必要に停止させるため、.setへの新しいスレッドを開始するFutureTask <SharedPreferences>サブクラスのようなものを用意することをお勧めします。 ()FutureTaskサブクラスの値。次に、FutureTask <SharedPreferences>のメンバーを必要なときに参照し、それを.get()します。私は、これをHoneycombの舞台裏で透過的に自由にする予定です。この領域のベストプラクティスを示すサンプルコードをいくつかリリースしてみます。

Androidデベロッパーブログで、今後1週間以内にStrictModeに関連するトピックに関する今後の投稿を確認してください。


うわー、そのような明確な答えがソースから直接得られることを期待していませんでした!どうもありがとう!
cottonBallPaws 2010

9
この素晴らしい投稿の新しい読者のために、@ Brad Fitzpatrickによる上記のブログ投稿へのリンクを見つけてください。Android 開発者によるBradによる厳格モードに関するブログ投稿です。この投稿には、androidバージョンに基づいて、apply(from gingerbread以降)またはcommit(froyo)を使用してsharedpreferencesを保存するためのサンプルコードへのリンクもあります:[条件付きでapplyまたはcommitを使用](code.google.com/p/zippy-android / source / browse / trunk / examples /…
tony m

4
これはまだICS \ JBに関連していますか?
ekatz 2013

5

共有設定はフラッシュストレージから読み取られるため、アクセスにはかなり時間がかかる場合があります。たくさん読みますか?SQLiteデータベースなど、別の形式を使用することもできます。

しかし、StrictModeを使用して見つけたすべてを修正しないでください。またはドキュメントを引用するには:

ただし、StrictModeが検出したすべてを修正する必要はありません。特に、通常のアクティビティライフサイクルでは、多くの場合、ディスクアクセスの多くのケースが必要です。StrictModeを使用して、誤って行ったことを見つけます。ただし、UIスレッドのネットワークリクエストは、ほとんど常に問題です。


6
しかし、SQLiteは、フラッシュストレージから読み取る必要があるファイルでもありませんが、設定ファイルに比べて大きくて複雑なファイルです。私は、設定に関連付けられているデータ量については、設定ファイルはSQLiteデータベースよりもはるかに高速であると想定しています。
トム

そのとおりです。ブラッドがすでに述べたように、これはほとんどの場合問題ありません-また、SharedPreferencesを1回(多分、FutureTaskを使用するスレッドでも)ロードし、単一のインスタンスへの可能なアクセスのために保持することをお勧めします。
mreichelt 2012年

5

ブラッドの答えについての1つの微妙な点: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()はフォアグラウンドスレッドでは安全であるように見えます。

(ちなみに、これをここに記載して申し訳ありません。これをブラッドの回答へのコメントとして書いたでしょうが、私は参加しただけで、そうするための十分な評判がありません。)


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()などを呼び出すときにnullポインターがスローされるようにします(Androidによってアプリケーションオブジェクトがメモリからスワイプされた場合)


0

SharedPreferencesクラスは、ディスク上のXMLファイル内で一部の読み取りと書き込みを行うため、他のIO操作と同様に、ブロックされている可能性があります。SharedPreferencesに現在格納されているデータの量は、API呼び出しによって消費される時間とリソースに影響します。最小量のデータの場合、データの取得/出力は数ミリ秒(場合によっては1ミリ秒未満)の問題です。ただし、エキスパートの観点からは、API呼び出しをバックグラウンドで実行することでパフォーマンスを向上させることが重要になる場合があります。非同期のSharedPreferencesについては、Datumライブラリをチェックアウトすることをお勧めします。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.