Статьи

Использование Android-посылки

Краткое определение посылки Android будет определением контейнера сообщений для облегченного высокопроизводительного межпроцессного взаимодействия (IPC). В Android «процесс» является стандартным Linux, и один процесс не может нормально обращаться к памяти другого процесса, поэтому в Parcel s система Android разбивает объекты на примитивы, которые можно маршалировать / демаршалировать через границы процесса.

Но Parcel можно также использовать в одном и том же процессе , чтобы передавать данные по различным компонентам одного и того же приложения. Например, типичное Android-приложение имеет несколько экранов, называемых «Действиями», и должно передавать данные или действия от одного действия к другому. Чтобы написать объект, через который можно пройти, мы можем реализовать интерфейс Parcelable . Сам Android предоставляет встроенный  объект Parcelable, называемый Intent,  который используется для передачи информации из одного компонента в другой.

Использование Intent довольно просто. Допустим, мы собираем пользовательские данные с нашего начального экрана под названием CollectDataActivity .

// inside CollectDataActivity, construct intent to pass along the next Activity, i.e. screen
Intent in = new Intent(this, ProcessDataActivity.class);
in.putExtra("userid", id); // (key,value) pairs
in.putExtra("age", age);
in.putExtra("phone", phone);
in.putExtra("is_registered", true);

// call next Activity --> next screen comes up
startActivity(in);

Нам нужно собрать эту информацию с нашего экрана сбора данных для ее обработки. Итак, все, что мы делаем, это следующее:

 

// inside ProcessDataActivity, get the info needed from previous Activity
Intent in = this.getIntent();
in.getLongExtra("userid", 0L);
in.getIntExtra("age", 0);
in.getStringExtra("phone");
in.getBooleanExtra("is_registered", false); // false = default value overridden by user input

Опять же, довольно просто. Мы извлекаем данные, используя те же ключи, которые использовались для их отправки, и используя соответствующие методы нашего Интента для каждого типа данных. Но даже при общении с Intent s мы все равно можем использовать Parcel s для передачи данных в пределах намерения. Например, мы можем сделать вышеупомянутое более элегантным способом, используя  пользовательский  класс Parcelable User :

В первом занятии:

// in CollectDataActivity, populate the Parcelable User object using its setter methods
User usr = new User();
usr.setId(id); // collected from user input// etc..

// pass it to another component
Intent in = new Intent(this, ProcessDataActivity.class);
in.putExtra("user", usr);
startActivity(in);

Во втором занятии:

// in ProcessDataActivity retrieve User 
Intent intent = getIntent();
User usr = (User) intent.getParcelableExtra("user");

Вот  как выглядит класс Parcelable User:

import android.os.Parcel;
import android.os.Parcelable;

public class User implements Parcelable {
    private long id;
    private int age;
    private String phone;
    private boolean registered;

    // No-arg Ctor
    public User(){}

    // all getters and setters go here //...

    /** Used to give additional hints on how to process the received parcel.*/
    @Override
    public int describeContents() {
// ignore for now
return 0;
    }

    @Override
    public void writeToParcel(Parcel pc, int flags) {
pc.writeLong(id);
pc.writeInt(age);
pc.writeString(phone);
pc.writeInt( registered ? 1 :0 );
   }

   /** Static field used to regenerate object, individually or as arrays */
  public static final Parcelable.Creator<User> CREATOR = new Parcelable.Creator<User>() {
         public User createFromParcel(Parcel pc) {
             return new User(pc);
         }
         public User[] newArray(int size) {
             return new User[size];
         }
   };

   /**Ctor from Parcel, reads back fields IN THE ORDER they were written */
   public User(Parcel pc){
       id         = pc.readLong();
       age        =  pc.readInt();
       phone      = pc.readString();
       registered = ( pc.readInt() == 1 );
  }
}

То, что мы сделали, было:

  1. Сделайте, чтобы наш класс User реализовал интерфейс Parcelable . Parcelable не является интерфейсом маркера, отсюда и следующее:
  2. Реализовать свой describeContents метод, который в данном случае ничего не делает.
  3. Реализуйте его абстрактный метод  writeToParce l, который принимает текущее состояние объекта и записывает его в Parcel
  4. Добавьте статическое поле с именем CREATOR в наш класс, который является объектом, реализующим интерфейс Parcelable.Creator
  5. Добавить конструктор, который принимает участок в качестве параметра . CREATOR вызывает этот конструктор для перестройки нашего объекта.

Сначала это выглядит как много лишнего кода, но имейте в виду, что, как и в большинстве случаев, наше приложение может эволюционировать и включать больше данных от пользователя … Иногда нам нужно передавать сложные объекты из одного компонента в другой, и прохождение объекта дает более чистый дизайн.

Та же логика применяется для связи между Activity (пользовательский интерфейс переднего плана) и фоновой службой . Мы просто вызовем метод startService вместо startActivity и передадим ему наш объект Parcelable User . Обратите внимание , что служба является не работает в отдельном процессе по умолчанию.

На данный момент, есть несколько вопросов, которые могут быть подняты:

  1.  Разве использование дружественного к IPC настраиваемого объекта для внутрипроцессного взаимодействия просто  лишнее ?
  2. Зачем нам использовать  Parcelable , когда у нас уже есть встроенная Java-сериализация?

Ответ на первое беспокойство … возможно. Но общение через пользовательский объект, а не через список пар ключ-значение — это больше ОО, и оно не оказывает заметного негативного влияния на производительность.

Что касается второго вопроса, почему бы просто не сделать так, чтобы пользователь реализовал Serializable , теоретически более простой интерфейс маркера? Одним словом, производительность . Использование Parcels  более эффективно, чем сериализация, за счет некоторой дополнительной сложности.

Эта дополнительная эффективность, в свою очередь, имеет свои пределы: передача изображения (  Bitmap ) с использованием Parcelable, как правило,  не очень хорошая идея (хотя Bitmap  действительно реализует  Parcelable) . Гораздо более эффективный способ использования памяти — передать только его URI или Resource ID , чтобы другие компоненты Android в вашем приложении могли иметь к нему доступ.

Другое ограничение Parcelable   заключается в том, что его  нельзя использовать для сериализации общего назначения в хранилище , поскольку базовая реализация может различаться в зависимости от версии ОС Android. Так что да, посылки быстрее по конструкции, но как высокопроизводительный транспорт, а не как замена универсального механизма сериализации.

Сказав все это, поскольку наш объект User является   Parcelable , он теперь может быть отправлен из этого приложения в другое, работающее в другом процессе, в частности через интерфейс, реализующий удаленную службу. В следующем посте мы рассмотрим IPC и язык определения интерфейса Android  ( AIDL ).

из блога Тони