web-dev-qa-db-ja.com

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

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

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

これは警告です:

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

uIスレッドで行われるgetSharedPreferences()呼び出しに対して指定されています。

SharedPreferencesへのアクセスと変更は、実際にUIスレッドから行う必要がありますか?

107
cottonBallPaws

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

注意すべき点がいくつかあります:(lazy bullet form)

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

ただし、読み込みに関しては...

  • ロードされると、SharedPreferencesはシングルトンになり、プロセス全体でキャッシュされます。そのため、できるだけ早くロードして、必要になる前にメモリに格納する必要があります。 (単純なXMLファイルであるSharedPreferencesを使用している場合のように、小さいと仮定します...)将来、ユーザーがボタンをクリックしたときにフォールトしたくないでしょう。

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

  • そのため、おそらくonCreate()の実行中にSharedPreferencesをロードし、同じインスタンスを再利用して、統計を回避したいと思うでしょう。

  • ただし、onCreate()中にプリファレンスが必要ない場合、そのロード時間はアプリの起動を不必要に停止させるので、一般に.setに新しいスレッドを開始するFutureTask <SharedPreferences>サブクラスのようなものを用意する方が良いです。 ()FutureTaskサブクラスの値。次に、必要なときにFutureTask <SharedPreferences>のメンバーを検索し、.get()します。 Honeycombの舞台裏でこれを無料で公開する予定です。この分野のベストプラクティスを示すサンプルコードをリリースしてみます。

今後数週間のStrictMode関連のテーマに関する今後の投稿については、Android Developersブログをご覧ください。

178

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

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

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

5
mreichelt

Bradの答えについての微妙な点:onCreate()でSharedPreferencesをロードしても、getString()などは、finishs(バックグラウンドスレッドで)共有ファイルプリファレンスを読み取るまでブロックするため、おそらくバックグラウンドスレッドで値を読み取る必要があります。

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

edit()も同様にブロックしますが、apply()はフォアグラウンドスレッドでは安全に見えます。

(ちなみに、これをここに書き留めてすみません。私はこれをブラッドの答えへのコメントとして書きましたが、私は参加したばかりで、そうするのに十分な評判がありません。)

4
Tom O'Neill

これは古い質問ですが、私のアプローチを共有したいと思います。読書時間が長く、共有設定とグローバルアプリケーションクラスの組み合わせを使用しました。

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は、新しいアプリケーションオブジェクトを作成し、ユーザーが以前いた場所でアクティビティを開始して、そもそもアプリケーションが強制終了されなかったという錯覚を与えます。

Nullをチェックしなかった場合、フィルターオブジェクトでgetMaxDistance()を呼び出すときにnullpointerをスローできます(アプリケーションオブジェクトがAndroidによってメモリからスワイプされた場合)

1
Jdruwe