Việc truy cập SharedPreferences có được thực hiện ngoài Chuỗi giao diện người dùng không?


112

Với việc phát hành Gingerbread, tôi đã thử nghiệm với một số API mới, một trong số chúng là Chế độ nghiêm ngặt .

Tôi nhận thấy rằng một trong những cảnh báo dành cho getSharedPreferences().

Đây là cảnh báo:

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

và nó được đưa ra cho một getSharedPreferences()cuộc gọi được thực hiện trên chuỗi giao diện người dùng.

Có nên SharedPreferencesthực sự thực hiện truy cập và thay đổi ngoài chuỗi giao diện người dùng không?


Tôi luôn thực hiện các thao tác tùy chọn của mình trên chuỗi giao diện người dùng. Mặc dù tôi đoán nó có ý nghĩa vì đó là một hoạt động IO
Falmarri

Câu trả lời:


184

Tôi rất vui vì bạn đã chơi với nó!

Một số điều cần lưu ý: (ở dạng dấu đầu dòng lười biếng)

  • nếu đây là vấn đề tồi tệ nhất của bạn, ứng dụng của bạn có thể đang ở một vị trí tốt. :) Tuy nhiên, quá trình ghi thường chậm hơn lần đọc, vì vậy hãy đảm bảo rằng bạn đang sử dụng SharedPreferenced $ Editor.apply () thay vì commit (). apply () mới trong GB và không đồng bộ (nhưng luôn an toàn, cẩn thận với các chuyển đổi vòng đời). Bạn có thể sử dụng phản chiếu để gọi có điều kiện apply () trên GB + và commit () trên Froyo trở xuống. Tôi sẽ thực hiện một bài đăng trên blog với mã mẫu về cách thực hiện việc này.

Về việc tải, tuy nhiên ...

  • sau khi được tải, SharedPreferences là các tệp đơn và được lưu vào bộ nhớ cache trong toàn bộ quá trình. vì vậy bạn muốn tải nó sớm nhất có thể để bạn có nó trong bộ nhớ trước khi cần. (giả sử nó nhỏ, đúng như vậy nếu bạn đang sử dụng SharedPreferences, một tệp XML đơn giản ...) Bạn không muốn xảy ra lỗi trong tương lai khi một số người dùng nhấp vào nút.

  • nhưng bất cứ khi nào bạn gọi context.getSharedPreferences (...), tệp XML hỗ trợ sẽ được thống kê để xem liệu nó có bị thay đổi hay không, vì vậy bạn vẫn muốn tránh những số liệu thống kê đó trong các sự kiện giao diện người dùng. Một thống kê thường phải nhanh (và thường được lưu trong bộ nhớ đệm), nhưng yaffs không có nhiều thứ trong cách đồng thời (và rất nhiều thiết bị Android chạy trên yaffs ... Droid, Nexus One, v.v.) vì vậy nếu bạn tránh đĩa , bạn tránh bị kẹt sau các hoạt động đĩa đang bay hoặc đang chờ xử lý khác.

  • vì vậy bạn có thể sẽ muốn tải SharedPreferences trong onCreate () của bạn và sử dụng lại cùng một phiên bản, tránh trạng thái.

  • nhưng nếu bạn vẫn không cần tùy chọn của mình trong onCreate (), thì thời gian tải đó đang làm ngưng trệ quá trình khởi động ứng dụng của bạn một cách không cần thiết, vì vậy, tốt hơn là nên có một lớp con nào đó như FutureTask <SharedPreferences> để khởi động một chuỗi mới tới .set () giá trị của lớp con FutureTask. Sau đó, chỉ cần tra cứu thành viên của FutureTask <SharedPreferences> của bạn bất cứ khi nào bạn cần và .get () nó. Tôi dự định làm điều này miễn phí ở hậu trường trong Honeycomb, một cách minh bạch. Tôi sẽ cố gắng phát hành một số mã mẫu hiển thị các phương pháp hay nhất trong lĩnh vực này.

Kiểm tra blog Nhà phát triển Android để biết các bài đăng sắp tới về các chủ đề liên quan đến Chế độ nghiêm ngặt trong (các) tuần tới.


Chà, không mong đợi nhận được câu trả lời rõ ràng như vậy ngay từ nguồn! Cảm ơn nhiều!
cottonBallPaws

9
Vì lợi ích của những độc giả mới của bài đăng tuyệt vời này, hãy tìm bên dưới liên kết đến bài đăng trên blog được đề cập ở trên bởi @Brad Fitzpatrick: bài đăng trên blog của nhà phát triển android về chế độ nghiêm ngặt của Brad . Bài đăng cũng có liên kết đến mã mẫu để sử dụng áp dụng (từ bánh gừng trở đi) hoặc cam kết (froyo) dựa trên phiên bản Android để lưu trữ các tài liệu tham khảo được chia sẻ: [có điều kiện sử dụng áp dụng hoặc cam kết] ( code.google.com/p/zippy-android / nguồn / duyệt / thân cây / ví dụ /… )
tony m

4
Điều này có còn phù hợp trên ICS \ JB không?
ekatz

5

Việc truy cập các tùy chọn được chia sẻ có thể mất khá nhiều thời gian vì chúng được đọc từ bộ nhớ flash. Bạn có đọc nhiều không? Có thể khi đó bạn có thể sử dụng một định dạng khác, ví dụ: cơ sở dữ liệu SQLite.

Nhưng không sửa chữa mọi thứ bạn tìm thấy bằng Chế độ nghiêm ngặt. Hoặc để trích dẫn tài liệu:

Nhưng đừng cảm thấy bắt buộc phải sửa mọi thứ mà Chế độ nghiêm ngặt tìm thấy. Đặc biệt, nhiều trường hợp truy cập đĩa thường cần thiết trong vòng đời hoạt động bình thường. Sử dụng Chế độ nghiêm ngặt để tìm những việc bạn đã vô tình làm. Tuy nhiên, các yêu cầu mạng trên chuỗi giao diện người dùng hầu như luôn là một vấn đề.


6
Nhưng không phải SQLite cũng là một tệp phải được đọc từ bộ nhớ flash - mà là một tệp lớn hơn và phức tạp hơn so với tệp tùy chọn. Tôi đã giả định rằng, đối với lượng dữ liệu được liên kết với tùy chọn, tệp tùy chọn sẽ nhanh hơn nhiều so với cơ sở dữ liệu SQLite.
Tom

Đúng rồi. Như Brad đã đề cập, điều này hầu như luôn luôn không có vấn đề gì - và anh ấy cũng đề cập rằng tốt hơn hết là tải SharedPreferences một lần (thậm chí có thể trong một Thread bằng FutureTask) và giữ nó để có thể truy cập vào phiên bản duy nhất.
mreichelt 22/10/12

5

Một điều tinh tế về câu trả lời của Brad: ngay cả khi bạn tải SharedPreferences trong onCreate (), bạn có thể vẫn nên đọc các giá trị trên chuỗi nền vì getString () v.v. chặn cho đến khi đọc xong tùy chọn tệp được chia sẻ (trên chuỗi nền):

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

edit () cũng chặn theo cách tương tự, mặc dù apply () dường như an toàn trên luồng nền trước.

(BTW xin lỗi vì đã đặt vấn đề này ở đây. Tôi sẽ đặt nó như một bình luận cho câu trả lời của Brad, nhưng tôi chỉ mới tham gia và không có đủ danh tiếng để làm như vậy.)


1

Tôi biết đây là một câu hỏi cũ nhưng tôi muốn chia sẻ cách tiếp cận của mình. Tôi đã có thời gian đọc lâu dài và sử dụng kết hợp các sở thích được chia sẻ và lớp ứng dụng toàn cầu:

Lớp ứng dụng:

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 (hoạt động được gọi đầu tiên trong ứng dụng của bạn):

LocalPreference.getLocalPreferences(this);

Các bước giải thích:

  1. Hoạt động chính gọi getLocalPreferences (this) -> điều này sẽ đọc các tùy chọn của bạn, đặt đối tượng lọc trong lớp ứng dụng của bạn và trả về nó.
  2. Khi bạn gọi lại hàm getLocalPreferences () ở một nơi khác trong ứng dụng, trước tiên nó sẽ kiểm tra xem nó không có sẵn trong lớp ứng dụng, nhanh hơn rất nhiều.

LƯU Ý: LUÔN LUÔN kiểm tra xem biến rộng của ứng dụng có khác với NULL hay không, lý do -> http://www.developerphil.com/dont-store-data-in-the-application-object/

Đối tượng ứng dụng sẽ không ở trong bộ nhớ mãi mãi, nó sẽ bị giết. Trái với suy nghĩ của nhiều người, ứng dụng sẽ không được khởi động lại từ đầu. Android sẽ tạo một đối tượng Ứng dụng mới và bắt đầu hoạt động mà người dùng đã ở trước đó để tạo ảo giác rằng ứng dụng đó chưa bao giờ bị khai tử ngay từ đầu.

Nếu tôi không kiểm tra null, tôi sẽ cho phép ném nullpointer khi gọi ví dụ getMaxDistance () trên đối tượng bộ lọc (nếu đối tượng ứng dụng đã được xóa khỏi bộ nhớ bởi Android)


0

Lớp SharedPreferences thực hiện một số thao tác đọc và ghi bên trong các tệp XML trên đĩa, vì vậy, giống như bất kỳ hoạt động IO nào khác, nó có thể bị chặn. Lượng dữ liệu hiện được lưu trữ trong SharedPreferences ảnh hưởng đến thời gian và tài nguyên được sử dụng bởi các lệnh gọi API. Đối với lượng dữ liệu tối thiểu, chỉ cần vài mili giây (đôi khi thậm chí ít hơn một mili giây) để lấy / đưa dữ liệu. Nhưng từ quan điểm của một chuyên gia, điều quan trọng là phải cải thiện hiệu suất bằng cách thực hiện các lệnh gọi API trong nền. Đối với SharedPreferences không đồng bộ, tôi khuyên bạn nên kiểm tra thư viện Datum .

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.