[Android 07] SharedPreferences sống thử với Singleton

1. Singleton là gì?
  • Singleton pattern là một lớp nhằm đảm bảo rằng chỉ có 1 instance của nó được tạo ra và cung cấp 1 cách giao tiếp chung bên trong nó cho những tác nhân bên ngoài. Nói chung nó sẽ đảm bảo tính nhất quán (consistency).
  • Singleton quản lý việc truy cập khá tốt vì chỉ có 1 instance duy nhất.
  • Có thể cải tiến các tác vụ và các thể hiện do pattern, sẽ được kế thừa và tùy biến thông qua thể hiện của lớp con.
  • Quản lý số lượng các instance của 1 lớp,
  • Flexible hơn so với việc dùng 1 lớp có thuộc tính static (Lí do bởi vì static có thể sử dụng 1 instance duy nhất, còn singleton pattern cho phép quản lý instance tốt hơn và dễ dàng tùy biến).
2. Khi nào dùng Singleton?
  • Khi cần instance duy nhất của 1 lớp.
  • Khi instance đó có khả năng mở rộng thông qua kế thừa, do đó các dev có thể sử dụng kế thừa mà không cần thay đổi mã code.
3. Cách thức Singleton được tạo.
  • Định nghĩa thuộc tính private và static trong lớp Singleton
  • Định nghĩa constructor thành protected hoặc private để người dùng không thể tạo trực tiếp từ class.
  • Định nghĩa accessor trong getInstance() thành public và static, đồng thời kiểm tra xem nó đã được khởi tạo hay chưa, tránh trường hợp getInstance() trả về null, nguy hiểm trong các trường hợp đa luồng và bất đồng bộ.
  • Cẩn thận kiểm tra nếu sử dụng multithreading vì ví dụ trường hợp có hai threads có thể gọi phương thức sinh object tại cùng một thời điểm và 2 instance được tạo ra. Lúc này có thể sử dụng classLoader hoặc double-check-locking. Nó cũng tương tự Immutable trong java core vậy. 😀
4. Sử dụng SharedPreferences và Singleton.
Hãy xem ví dụ sau đây, tất cả đã được chú thích.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
package project.android.xml;

import android.content.Context;
import android.content.SharedPreferences;

import com.google.gson.Gson;

import java.util.Objects;

/**
 * Created by GUT81HC on 4/11/2018.
 */

public class SharedPreferencesManager {
    public static final String PREF_NAME = "project.android.xml";
    /**
     * Sử dụng Singleton Class để truy cập SharedPreferencesManager.
     * Được khởi tạo 1 lần duy nhất lúc khởi chạy component của ứng dụng
     * Sử dụng phương thức static khởi tạo (Sử dụng ApplicationContext)
     */
    private static final String TAG = SharedPreferencesManager.class.getName();
    private static SharedPreferencesManager sharedPreferencesManager;
    private SharedPreferences sharedPreferences;

    /**
     * Khởi tạo Constructor cho SharedPreferencesManager
     *
     * @param context: lấy phương thức getSharedPreferences
     */
    private SharedPreferencesManager(Context context) {
        sharedPreferences = context.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE);
    }

    /**
     * Lấy instance của SharedPreferencesManager nếu đã được khởi tạo, hoặc ném ra 1 exception IllegalStateException.
     *
     * @return
     */
    public static SharedPreferencesManager getInstance() {
        if (sharedPreferencesManager == null) {
            throw new IllegalStateException("Have not initialized yet");
        }
        return sharedPreferencesManager;
    }

    /**
     * Khởi tạo SharedPreferencesManager.
     * Nếu context bằng null, trả về NullPointerException.
     * Nếu instance của SharedPreferencesManager bằng null, đồng bộ hóa bằng cách lock nó lại, dùng synchonized
     * để khởi tạo lại instance của SharedPreferencesManager bằng từ khóa new.
     *
     * @param context
     */
    public static void initialize(Context context) {
        if (context == null) {
            throw new NullPointerException("ApplicationContext is null");
        }
        if (sharedPreferencesManager == null) {
            synchronized ((SharedPreferencesManager.class)) {
                if (sharedPreferencesManager == null) {
                    sharedPreferencesManager = new SharedPreferencesManager(context);
                }
            }
        }
    }

    private static String createJsonFromObject(Object object) {
        return new Gson().toJson(object);
    }

    /**
     * Trả về giá trị SharedPreferences từ android.content
     *
     * @return
     */
    private SharedPreferences getPrefs() {
        return sharedPreferences;
    }

    /**
     * Xóa hết tất cả prefs trong SharedPreferences.
     * Sử dụng apply thay vì commit nếu muốn bất đồng bộ và không cần trả về kết quả thực thi ổn hay không.
     */
    public void clearPrefs() {
        SharedPreferences.Editor editor = getPrefs().edit();
        editor.clear();
        editor.apply();
    }

    /**
     * Xóa một key trong SharedPreferences.
     *
     * @param key: Chấp nhận primitive data.
     */
    public void removeKey(String key) {
        getPrefs().edit().remove(key).apply();
    }

    /**
     * Tìm key tồn tại hay không trong SP.
     *
     * @param key
     * @return if contains key in S.P
     */
    public boolean containsKey(String key) {
        return getPrefs().contains(key);
    }

    /**
     * Thêm 1 key String và value vào SP, tương tự như các primitive data khác.
     *
     * @param key
     * @param value
     */
    public void setString(String key, String value) {
        SharedPreferences.Editor editor = getPrefs().edit();
        editor.putString(key, value);
        editor.apply();
    }

    /**
     * Thêm key và value với kiểu Object class vào trong SP.
     *
     * @param key
     * @param object: Object đã được cast thành String và sẽ sử dụng Gson để map dữ liệu thành object ngược lại khi reverse.
     * @param <M>
     */
    public <M extends Objects> void setObject(String key, M object) {
        String value = createJsonFromObject(object);
        SharedPreferences.Editor editor = getPrefs().edit();
        editor.putString(key, value);
        editor.apply();
    }

    /**
     * Tương tự put Object vào SP, việc put Collection vào SP cũng sử dụng Gson để cast Object thành String.
     *
     * @param key:           String
     * @param dataCollection : Collection Object.
     * @param <C>            : Generic Colletion Object.
     */
    public <C> void setCollection(String key, C dataCollection) {
        SharedPreferences.Editor editor = getPrefs().edit();
        String value = createJsonFromObject(dataCollection);
        editor.putString(key, value);
        editor.apply();
    }

    /**
     * Lắng nghe thay đổi của SP thông qua 1 listener.
     *
     * @param listener
     */
    public void registerPrefsListener(SharedPreferences.OnSharedPreferenceChangeListener listener) {
        getPrefs().registerOnSharedPreferenceChangeListener(listener);
    }
}
HappyCoding!

Nhận xét

Bài đăng phổ biến từ blog này

[Android-02] LayoutParams trong Android