Статьи

Хранение объектов в Android

Одной из альтернатив использования SQLite на Android является хранение объектов Java в SharedPreferences . Здесь мы рассмотрим два разных способа сделать это.

Почему бы не перейти на SQLite для всех нужд хранения? Причины могут быть разными: помимо несоответствия импеданса между объектной ориентацией и реляционными базами данных, SQLite может быть излишним (влечет за собой дополнительные издержки) для некоторых простых случаев использования, или его использование и синтаксис могут вообще не нравиться.

В качестве примера мы будем работать со следующим классом User :

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
/** User object to be saved in db */
public class User{
 
    private int id; // used for object storage
    private String userName;
    private boolean registered;
    private double score;
 
    /** Constructor */
   public User(int id, String userName, boolean registered, double score){
       this.id = id;
       this.userName = userName;
       this.registered = registered;
       this.score = score;
   }
   // getters and setters here...
}

Уникальный идентификатор идентификатора , скорее всего, будет передан нашим сервером, хотя мы также можем вычислить его на самом устройстве, как только мы создадим пользователя и сохраним его отдельно в SharedPreferences , в зависимости от дизайна нашего приложения. Здесь мы просто будем использовать его как ключ хранения объекта.

Далее нам нужно написать наш класс настроек:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
/** stores the user object in SharedPreferences */
public class UserPrefs{
 
    /** This application's preferences label */
    private static final String PREFS_NAME = "com.our.package.UserPrefs";
 
    /** This application's preferences */
    private static SharedPreferences settings;
 
   /** This application's settings editor*/
   private static SharedPreferences.Editor editor;
 
   /** Constructor takes an android.content.Context argument*/
   public UserPrefs(Context ctx){
        if(settings == null){
           settings = ctx.getSharedPreferences(PREFS_NAME,
                                               Context.MODE_PRIVATE );
        }
       /*
        * Get a SharedPreferences editor instance.
        * SharedPreferences ensures that updates are atomic
        * and non-concurrent
        */
        editor = settings.edit();    
   }
   //...
}

Метод 1: Плоские объекты с полевыми ключами

Идея здесь в том, что, хотя SharedPreferences хранит только примитивные типы, объектная конструкция в конечном итоге является «мешком» примитивов. Таким образом, мы можем разложить объект на набор примитивов и сохранить каждый из них индивидуально с уникальным ключом. Однако нам нужно отслеживать, какое сохраненное поле принадлежит какому объекту, если мы хотим восстановить наши объекты из хранилища.

Поэтому, прежде чем писать методы CRUD для данного объекта User в нашем классе UserPrefs , нам нужно определить уникальные ключи для каждого члена класса User. Для удобства мы можем написать метод обеспечения уникальности ключа по всем направлениям:

01
02
03
04
05
06
07
08
09
10
11
12
/** The prefix for flattened user keys */
public static final String KEY_PREFIX =
            "com.our.package.KEY";
 
/** Method to return a unique key for any field belonging to a given object
* @param id of the object
* @param fieldKey of a particular field belonging to that object
* @return key String uniquely identifying the object's field
*/
private String getFieldKey(int id, String fieldKey) {
       return  KEY_PREFIX + id + "_" + fieldKey;
}

Обратите внимание, как getFieldKey () дает нам уникальный идентификатор для имени поля и для пользователя.

Теперь мы можем приступить к написанию методов CRUD:

01
02
03
04
05
06
07
08
09
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
/** generic field keys */
 private static final String KEY_USERNAME = "com.our.package.KEY_USERNAME";
 private static final String KEY_REGISTERED = "com.our.package.KEY_REGISTERED";
 private static final String KEY_SCORE = "com.our.package.KEY_SCORE";
 
/** Store or Update */
public void setUser(User user){
    if(user == null)
      return; // don't bother
 
    int id = user.getId();
    editor.putString(
               getFieldKey(id, KEY_USERNAME),
               user.getUsername() );
    editor.putBoolean(
               getFieldKey(id, KEY_REGISTERED),
               user.isRegistered() );
    editor.putFloat(
               getFieldKey(id, KEY_SCORE),
               user.getScore() );
 
    editor.commit();
}
 
/** Retrieve */
public User getUser(int id){
    String name = settings.getString(
                  getFieldKey(id, KEY_USERNAME),
                  "" ); // default value
    boolean registered =  settings.getBoolean(
                 getFieldKey(id, KEY_REGISTERED),
                 false); // default value
    double score =  settings.getFloat(
                 getFieldKey(id, KEY_SCORE),
                 0); // default value
 
    return new User(id, name, registered, score);
}
 
/** Delete */
public void deleteUser(User user){
   if(user == null)
      return; // don't bother
 
   int id = user.getId();
   editor.remove( getFieldKey(id, KEY_USERNAME) );
   editor.remove( getFieldKey(id, KEY_REGISTERED) );
   editor.remove( getFieldKey(id, KEY_SCORE) );
 
   editor.commit();
}

Теперь мы можем создавать, извлекать, обновлять и удалять любые объекты пользователя из любой точки нашего приложения:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
// get a SharedPreferences instance
UserPrefs prefs = new UserPrefs( this.getApplicationContext() );
 
// get id from server or local storage
// then find User with that id
User user = prefs.getUser(id);
 
// operations on User, e.g.
user.setRegistered(true);
user.setScore(new_score);
 
// save
prefs.setUser(user);
// ...or delete
prefs.deleteUser(user),

Для встроенных объектов мы применяем ту же логику рекурсивно и вызываем их методы получения / установки из наших пользовательских методов CRUD. Этот метод выравнивания объектов представляет собой простое альтернативное хранилище без использования SQL на телефоне. Это, конечно, имеет свои ограничения по мере увеличения количества вложенных объектов.

Способ 2: Google Gson

Gson — это библиотека Java, которая предоставляет простые методы toJson () и fromJson () для преобразования объектов Java в формат JSON и наоборот.
Затем мы просто сохраняем формат JSON как целую строку в SharedPreferences . Этот метод предполагает добавление новой библиотеки в наши проекты, но он значительно удобнее:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
// convert User object user to JSON format
Gson gson = new Gson();
String user_json = gson.toJson(user);
 
// store in SharedPreferences
String id =  "" +  user.getId(); // get storage key
editor.putString(id, user_json);
editor.commit();
 
// time flies...
 
// do the reverse operation
 user_json = settings.getString(id, "");
 user  = gson.fromJson(user_json, User.class);

Предыдущие методы CRUD теперь можно переписать в нашем классе UserPrefs без необходимости использовать другие ключи, кроме идентификаторов пользователей. Это должно быть просто и оставлено читателю в качестве упражнения.

Вывод

Мы рассмотрели несколько способов использования SharedPreferences для хранения объектов. Другими вариантами работы с объектами являются несколько ORM для Android ( ORMLite , greenDAO , Sugar ORM , ActiveAndroid и т. Д.) Или мобильные базы данных NOSQL, такие как Couchbase Lite (бета-версия в настоящее время). Couchbase Lite — это, по сути, база данных JSON, в то время как различные ORM могут существенно упростить наш код, когда модель данных становится сложной, и мы находимся в процессе написания множества стандартных операций SQLite. Как и в случае со всеми абстракциями, нацеленными на уменьшение базовой сложности, недостатком ORM является закон утечек :

Все нетривиальные абстракции, в какой-то степени, негерметичны.

Альтернативы ORM и NOSQL станут темами будущих статей.

Ссылка: Хранение объектов в Android от нашего партнера по JCG Тони Сицилиана в блоге Tony’s Blog .