вторник, 24 мая 2011 г.

Android: передача объектов между Activity

В одном из предыдущих постов мы затронули тему перехода между Activity в Android-приложениях. И, в частности, обсуждали передачу данных из одного Activity в другое. Очевидное решение  intent.putExtra(NextActivity.FIELD_NAME, field_value) хорошо служит нам до тех пор, пока мы передаём данные "простых" типов: String, int, long или массивы. Но стоит нам попытаться передать таким образом объект из нашей модели данных, начинаются трудности.
Объекты таким способом передавать можно, но они должны реализовывать интерфейс Serializable или Parcelable. Первый вариант по некоторым найденным мной отзывам, снижает производительность приложения. Второй - давайте рассмотрим внимательнее.
Вот пример класса одного из моих приложений:

  1. public class Email implements Parcelable {
  2.  
  3.   private String address;
  4.   private String type;
  5.  
  6.   private Email(Parcel in) {
  7.     this.address = in.readString();
  8.     this.type = in.readString();
  9.   }
  10.  
  11.   public String getAddress() {
  12.     return address;
  13.   }
  14.  
  15.   public void setAddress(String address) {
  16.     this.address = address;
  17.   }
  18.  
  19.   public String getType() {
  20.     return type;
  21.   }
  22.  
  23.   public void setType(String t) {
  24.     this.type = t;
  25.   }
  26.  
  27.   public Email(String a, String t) {
  28.     this.address = a;
  29.     this.type = t;
  30.   }
  31.  
  32.   public int describeContents() {
  33.     return 0;
  34.   }
  35.   public static final Parcelable.Creator CREATOR = new Parcelable.Creator() {
  36.  
  37.     public Email createFromParcel(Parcel in) {
  38.       return new Email(in);
  39.     }
  40.  
  41.     public Email[] newArray(int size) {
  42.       return new Email[size];
  43.     }
  44.   };
  45.  
  46.   public void writeToParcel(Parcel parcel, int i) {
  47.     parcel.writeString(address);
  48.     parcel.writeString(type);
  49.   }
  50. }
Как видим,он реализует интерфейс Parcelable, а значит может легко передаваться между Activity. Как мы этого добиваемся? Мы дополняем логику класса методами для его Parcel-изации и Де-parcel-изации, проще говоря, сохранения в Parcel и восстановления из него. Parcel тут можно рассматривать как некий буфер, в который можно сложить в определённом порядке данные любых типов и затем (в том же порядке !) их оттуда извлечь.
Складывать содержимое полей класса в Parcel очень просто. Это реализуется в методе writeToParcel. Для восстановления объекта действуем так: Создаём вложенный класс Parcelable.Creator, в котором реализуем два метода. Второй выглядит всегда одинаково, а вот первый для нас весьма важен: он вызовет новый конструктор нашего класса, передавая в него Parcel. В конструкторе мы должны реализовать логику, обратную методу writeToParcel, т.е. вычитать из Parcel-а значения полей класса в том же порядке, в каком их туда записывали. В случае простых полей типа String это несложно. Если же у нас есть поля типа ArrayList, то делаем так:
p.writeInt(phone.size());
for (int i = 0; i < phone.size(); i++) {
  p.writeString(phone.get(i));
}
для сохранения, и
int count = p.readInt();
for (int i = 0; i < count; i++) {
  String ph = p.readString();
  phone.add(ph);
}
для восстановления. Тут p, как вы, наверное, догадались - объект класса Parcel, а phone - поле типа ArrayList.
Если полями нашего класса являются другие наши классы, то мы должны их также "научить Parcel-изоваться".  Тогда поля таких типов мы будем укладывать в Parcel методом
p.writeParcelable(organization, Parcelable.CONTENTS_FILE_DESCRIPTOR);
и извлекать оттуда методом
organization = p.readParcelable(getClass().getClassLoader());

8 комментариев:

  1. А где тип для Parcelable.Creator?
    Стоит делать так:
    Parcelable.Creator CREATOR = new Parcelable.Creator()

    ОтветитьУдалить
  2. Блин, теги съелись. Повторю:
    Parcelable.Creator[Email] CREATOR = new Parcelable.Creator[Email]()...
    квадратные скобки заменить угловыми.

    ОтветитьУдалить
  3. Круто, давно искал что-то подобное, только не понятно, что в себе содержит переменная organization

    ОтветитьУдалить
  4. А не желаете написать статью про то, как сохранить произвольно добавленные визуальные компоненты на экране после поворота экрана(screen rotation)? Думаю данная статья будет актуальна.

    ОтветитьУдалить
  5. прошу прощения, но не понял как потом этот класс в дугой активити получить?

    ОтветитьУдалить
  6. а что если в моем объекте хранится ArrayList. Как реализовать метод writeToParcel?

    ОтветитьУдалить