Краткое определение посылки 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 ); } }
То, что мы сделали, было:
- Сделайте, чтобы наш класс User реализовал интерфейс Parcelable . Parcelable не является интерфейсом маркера, отсюда и следующее:
- Реализовать свой describeContents метод, который в данном случае ничего не делает.
- Реализуйте его абстрактный метод writeToParce l, который принимает текущее состояние объекта и записывает его в Parcel
- Добавьте статическое поле с именем CREATOR в наш класс, который является объектом, реализующим интерфейс Parcelable.Creator
- Добавить конструктор, который принимает участок в качестве параметра . CREATOR вызывает этот конструктор для перестройки нашего объекта.
Сначала это выглядит как много лишнего кода, но имейте в виду, что, как и в большинстве случаев, наше приложение может эволюционировать и включать больше данных от пользователя … Иногда нам нужно передавать сложные объекты из одного компонента в другой, и прохождение объекта дает более чистый дизайн.
Та же логика применяется для связи между Activity (пользовательский интерфейс переднего плана) и фоновой службой . Мы просто вызовем метод startService вместо startActivity и передадим ему наш объект Parcelable User . Обратите внимание , что служба является не работает в отдельном процессе по умолчанию.
На данный момент, есть несколько вопросов, которые могут быть подняты:
- Разве использование дружественного к IPC настраиваемого объекта для внутрипроцессного взаимодействия просто лишнее ?
- Зачем нам использовать Parcelable , когда у нас уже есть встроенная Java-сериализация?
Ответ на первое беспокойство … возможно. Но общение через пользовательский объект, а не через список пар ключ-значение — это больше ОО, и оно не оказывает заметного негативного влияния на производительность.
Что касается второго вопроса, почему бы просто не сделать так, чтобы пользователь реализовал Serializable , теоретически более простой интерфейс маркера? Одним словом, производительность . Использование Parcels более эффективно, чем сериализация, за счет некоторой дополнительной сложности.
Эта дополнительная эффективность, в свою очередь, имеет свои пределы: передача изображения ( Bitmap ) с использованием Parcelable, как правило, не очень хорошая идея (хотя Bitmap действительно реализует Parcelable) . Гораздо более эффективный способ использования памяти — передать только его URI или Resource ID , чтобы другие компоненты Android в вашем приложении могли иметь к нему доступ.
Другое ограничение Parcelable заключается в том, что его нельзя использовать для сериализации общего назначения в хранилище , поскольку базовая реализация может различаться в зависимости от версии ОС Android. Так что да, посылки быстрее по конструкции, но как высокопроизводительный транспорт, а не как замена универсального механизма сериализации.
Сказав все это, поскольку наш объект User является Parcelable , он теперь может быть отправлен из этого приложения в другое, работающее в другом процессе, в частности через интерфейс, реализующий удаленную службу. В следующем посте мы рассмотрим IPC и язык определения интерфейса Android ( AIDL ).
из блога Тони