Як надсилати об'єкти через пакет


119

Мені потрібно передати посилання на клас, який проводить більшу частину моєї обробки через пакет.

Проблема полягає в тому, що він не має нічого спільного з намірами чи контекстами і має велику кількість непримітивних об'єктів. Як я пакую клас у складений / серіалізуючий та передаю його до startActivityForResult?


2
"Мені потрібно передати посилання на клас, який здійснює більшу частину моєї обробки через пакет" - чому?
CommonsWare

1
У мене є об'єкт (DataManager), він обробляє сервер і виконує кілька резервних файлів для деяких графічних інтерфейсів. Коли коли-небудь встановлено нове з'єднання, я хочу, щоб користувач міг запустити нову діяльність, яка перелічує в ListView всі активні з’єднання і примушує користувача вибрати таке. Потім отримані дані прив’язуються до нового графічного інтерфейсу. Це дійсно лише вибір вибору шкіри заднього кінця.
вхід

3
Якщо ви маєте справу з одним і тим же екземпляром об'єкта протягом декількох видів діяльності, можливо, вам слід розглянути шаблон однотонності . Там хороший підручник тут .
sotrh

Відповіді:


55

З'ясування того, який шлях потрібно пройти, потрібно відповісти не лише на ключове питання CommonsWare "чому", але і на питання "до чого?" ти передаєш його

Реальність полягає в тому, що єдине, що може пройти через пакети, - це звичайні дані - все інше базується на інтерпретації того, що ці дані означають або вказують. Ви не можете буквально передати об'єкт, але те, що ви можете зробити, це одна з трьох речей:

1) Ви можете розбити об'єкт до даних, що складаються, і якщо те, що знаходиться на іншому кінці, має знання про той самий тип об'єкта, він може зібрати клон із серіалізованих даних. Ось так більшість поширених типів проходять через пучки.

2) Можна пропустити непрозору ручку. Якщо ви передаєте його в тому ж контексті (хоча можна запитати, чому турбувати), це буде ручка, яку ви можете викликати або перенаправляти. Але якщо передати його через Binder в інший контекст, це буквальне значення буде довільним числом (насправді ці довільні числа рахуються послідовно від запуску). Ви нічого не можете зробити, окрім як слідкуйте, поки не повернете його до початкового контексту, що змусить Біндера перетворити його назад в оригінальну ручку, зробивши його знову корисною.

3) Ви можете передати магічну ручку, наприклад, дескриптор файлу або посилання на певні об'єкти ОС / платформи, і якщо встановити правильні прапори, Binder створить клон, що вказує на той самий ресурс для одержувача, який фактично може бути використаний на інший кінець. Але це працює лише для дуже мало типів об’єктів.

Швидше за все, ви або передаєте свій клас просто для того, щоб інший кінець міг відслідковувати його і повертати йому пізніше, або ви передаєте його в контекст, де клон може бути створений із серіалізованих складових даних ... або ще ви намагаєтесь зробити щось, що просто не працює, і вам потрібно переосмислити весь підхід.


1
Дякуємо за відповідь на екскурсію Ваше право, все, що мені потрібно зробити, - це просто передати посилання на список об’єктів до моєї нової діяльності. Нова активність буде брати деякі дані зі списку та відображати вибраний ListView. onSelect, активність поверне результат (деякі дані, що відносяться до об'єкта клацання) до активності хоста. Якщо я правильно розумію, я вважаю, що ваш варіант 2 найкраще вирішує це; як мені дістати цю непрозору ручку?
вхід

Ваша інша діяльність не може витягти будь-які дані з непрозорого об’єкта для відображення. Напевно, ви хочете зробити це створити та передати через деякі сурогатні об’єкти підтримуваного типу, які містять копії інформації, яка була б відображена.
Кріс Страттон

158

Ви також можете використовувати Gson для перетворення об'єкта в JSONObject і передачі його в пакет. Для мене це був найелегантніший спосіб, коли я це зробив. Я не перевіряв, як це впливає на продуктивність.

У початковій діяльності

Intent activity = new Intent(MyActivity.this,NextActivity.class);
activity.putExtra("myObject", new Gson().toJson(myobject));
startActivity(activity);

У наступній активності

String jsonMyObject;
Bundle extras = getIntent().getExtras();
if (extras != null) {
   jsonMyObject = extras.getString("myObject");
}
MyObject myObject = new Gson().fromJson(jsonMyObject, MyObject.class);

3
Оскільки це справа проходження матеріалів між видами діяльності, це не відбувається досить часто, щоб мати великий вплив на загальну ефективність програми. При цьому я сумніваюся, що це буде працювати над серіалізацією DataManager оригінальної публікації, оскільки це звучить так, ніби він має з'єднання з сокетом та інші подібні класи.
britzl

4
Також Google пропонує це рішення замість Serialize: перегляньте останній розділ "Рекомендовані альтернативи" цієї сторінки документа
TechNyquist

3
так само, як слово попередження, я деякий час дотримувався цієї методики, але є обмеження на пам’ять у тому, що ви можете передавати як String, тому будьте впевнені, що ваші дані не надто великі.
джидува

Див. Blog.madadipouya.com/2015/09/21/…, щоб дізнатися, як додати підтримку Gson до свого проекту.
geekQ

Розумне рішення, шапки для вас!
Rohit Mandiwal

20

Parcelable - це хороший спосіб передачі об'єкту з наміром.

Як я можу зробити власні об'єкти парцельованими? є досить гарною відповіддю щодо використання Parcelable

Офіційні документи google також містять приклад


1
або вони також можуть бути серіалізаційними.
Джефрі Блатман

1
Але значно знижує продуктивність в 10 разів !! Перевірте цей орієнтир: developerphil.com/parcelable-vs-serializable
saiyancoder

2
+1 @ коментар Mati, однак, щоб поставити його в контекст 10x при застосуванні до одного об'єкта, еквівалентно 1 мс. Тож, можливо, не так погано, як це звучить.
пінойїд

1
Погодьтеся. Проблема полягає в тому, що ви маєте справу з колекціями, що є дуже поширеним випадком використання, якщо ви отримуєте ресурси від API відпочинку. Але для одного об'єкта не повинно бути щось горезвісне. У будь-якому випадку, якщо весь код котлової панелі щось вам заважає , ви можете спробувати цю вкладку, яка створює все для вас: github.com/johncarl81/parceler . Справді приємний підхід!
saiyancoder


14

Ви можете використовувати глобальний додаток стан .

Оновлення:

Налаштуйте та додайте це до свого AndroidManifest.xml:

<application android:label="@string/app_name" android:debuggable="true" android:name=".CustomApplication"

А потім мати такий клас у своєму проекті:

package com.example;

import android.app.Application;

public class CustomApplication extends Application {
    public int someVariable = -1;
}

А оскільки " Доступ до нього можна отримати за допомогою будь-якої діяльності чи служби через getApplication () ", ви використовуєте його так:

CustomApplication application = (CustomApplication)getApplication();
application.someVariable = 123; 

Сподіваюся, що це допомагає.


1
Дякую за відповідь, але як?
вхід

Я вважаю, що ви просто підклас Application та зможете зберігати все, що завгодно. Зміни у форматі XML згадуються у вищенаведеному посиланні.
Марк Сторер

9
Як загальний принцип дизайну, це гарна ідея уникати глобалів, якщо вони вам справді не потрібні. У цьому випадку є хороші альтернативи.
dhaag23

Гаразд, я думаю, що я розумію, що ви говорите. Просто розгорніть додаток і киньте змінну, в якій тримається об'єкт, який потрібно передати; Я переглянув довідкову сторінку і не побачив необхідних змін у форматі XML.
вхід

Я хотів написати це і як відповідь. Це, безумовно, один із способів зробити це. Але майте на увазі, що ці об’єкти залишаються в пам’яті, якщо ви їх не будете рефренувати (або контекст програми знищений) і не можуть займати місця, коли вони вам не потрібні.
Ігор Чордаш

12

Ви також можете зробити свої об'єкти серіалізаційними та використовувати методи getSerializable і putSerializable групи Bundle .


1
Я спробував це і швидко зрозумів, що це буде недоцільно. Я не думаю, що більшість об'єктів, що зберігаються в пройденому класі (потоках), є серіалізаційними. :) спасибі, хоча.
вхід

10

Можливе рішення:

Bundle bundle = new Bundle();
bundle.putSerializable("key", new CustomObject());

Клас CustomObject:

class CustomObject implements Serializable{
 private SubCustomObject1 sc1;
 private SubCustomObject2 sc2;
}

Підпорядковані об'єкти:

class SubCustomObject1 implements Serializable{ }

class SubCustomObject2  implements Serializable{ }

7

Ще один спосіб відправки об'єктів через пакет - це використання bundle.putByteArray
зразкового коду

public class DataBean implements Serializable {
private Date currentTime;

public setDate() {
    currentTime = Calendar.getInstance().getTime();
 }

public Date getCurrentTime() {
    return currentTime;
 }
}

помістити Об'єкт DataBean в пакет:

class FirstClass{
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//Your code...

//When you want to start new Activity...
Intent dataIntent =new Intent(FirstClass.this, SecondClass.class);
            Bundle dataBundle=new Bundle();
            DataBean dataObj=new DataBean();
            dataObj.setDate();
            try {
                dataBundle.putByteArray("Obj_byte_array", object2Bytes(dataObj));

            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();

            }

            dataIntent.putExtras(dataBundle);

            startActivity(dataIntent);
}

Перетворення об'єктів у байтові масиви

/**
 * Converting objects to byte arrays
 */
static public byte[] object2Bytes( Object o ) throws IOException {
      ByteArrayOutputStream baos = new ByteArrayOutputStream();
      ObjectOutputStream oos = new ObjectOutputStream( baos );
      oos.writeObject( o );
      return baos.toByteArray();
    }

Повернути об’єкт із пакета:

class SecondClass{
DataBean dataBean;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//Your code...

//Get Info from Bundle...
    Bundle infoBundle=getIntent().getExtras();
    try {
        dataBean = (DataBean)bytes2Object(infoBundle.getByteArray("Obj_byte_array"));
    } catch (IOException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    } catch (ClassNotFoundException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
}

Спосіб отримання об'єктів з байтових масивів:

/**
 * Converting byte arrays to objects
 */
static public Object bytes2Object( byte raw[] )
        throws IOException, ClassNotFoundException {
      ByteArrayInputStream bais = new ByteArrayInputStream( raw );
      ObjectInputStream ois = new ObjectInputStream( bais );
      Object o = ois.readObject();
      return o;
    }

Сподіваюсь, це допоможе іншим приятелям.


це виглядає гладко і легко дивиться на код. Але я думаю, що є щось більше в тому, чому SDK не пропонує щось подібне для передачі об'єктів. Чи можете ви сказати мені щось більше про це рішення?
Маріо Ленчі

3
Зовсім не потрібно для цього коду! Використовуйте bundle.putSerializable (objectImplementingSerializable) - це робиться під тим, що ви знову реалізуєте тут знову ...
Risadinha

3

1. Дуже прямий і простий у використанні приклад, що робить об'єкт, що передається, реалізувати Serializable.

class Object implements Serializable{
    String firstName;
   String lastName;
}

2.пустіть об'єкт у пакеті

Bundle bundle = new Bundle();
Object Object = new Object();
bundle.putSerializable("object", object);

3.отримати переданий об’єкт із пакета як Serializable, а потім передати на Object.

Object object = (Object) getArguments().getSerializable("object");

0

Це дуже запізніла відповідь на моє власне запитання, але вона все одно привертає увагу, тому я відчуваю, що треба її вирішити. Більшість із цих відповідей є правильними і справляються із завданням ідеально. Однак це залежить від потреб програми. Ця відповідь буде використана для опису двох рішень цієї проблеми.

Застосування

По-перше, це Заявка , оскільки тут найбільше говорили про відповіді. Додаток є хорошим об'єктом для розміщення об'єктів, які потребують посилання на контекст. "ServerSocket", безсумнівно, знадобиться контекст (для вводу / виводу файлу або прості оновлення ListAdapter). Я особисто віддаю перевагу цьому маршруту. Мені подобаються програми, вони корисні для пошуку контексту (оскільки вони можуть бути статичними і, швидше за все, не спричиняти витік пам'яті) і мають простий життєвий цикл.

Сервіс

` Обслуговування займає друге місце . `Сервіс` насправді кращий вибір для моєї проблеми, оскільки саме такі послуги призначені:
Сервіс - це компонент програми, який може виконувати тривалі операції в
фон і не забезпечує інтерфейс користувача.
Послуги акуратні тим, що вони мають більш визначений життєвий цикл, який легше контролювати. Далі, якщо потрібно, служби можуть працювати за межами програми (тобто під час завантаження). Це може знадобитися для деяких додатків або просто акуратною функцією.

Це не було повним описом, але я залишив посилання на документи для тих, хто хоче розглянути більше. Загалом Service, тим краще для потрібного мені екземпляра - запуску ServerSocket на моєму SPP-пристрої.


0

Я натрапив на це питання, коли шукав спосіб передачі об’єкта Date. У моєму випадку, як було запропоновано серед відповідей, я використовував Bundle.putSerializable (), але це не працюватиме для складної речі, як описано у початковому дописі DataManager.

Моя пропозиція, яка дасть дуже схожий результат на додавання згаданого DataManager у додаток або перетворення його на Singleton, полягає у використанні введення залежностей та прив'язуванні DataManager до області Singleton та введення даних DataManager там, де це потрібно. Ви не тільки отримаєте перевагу підвищеної перевірки, але і отримаєте чистіший код без усього кодового коду "проходження залежностей між класами та видами діяльності". (Robo) З Guice дуже легко працювати, і нова рамка Dagger також виглядає перспективною.


1
Ну, з чимось на зразок Date, ви могли просто передати довге значення. Але, решта звучить добре. Дякую.
ahodder

0

ще один простий спосіб передачі об’єкта за допомогою пакета:

  • в об’єкті класу створіть статичний список або іншу структуру даних за допомогою ключа
  • коли ви створюєте об'єкт, додайте його до списку / структури даних за допомогою ключа (наприклад, довга мітка часу, коли об’єкт створений)
  • створити метод статичний getObject (довгий ключ) для отримання об'єкта зі списку
  • в комплекті передають ключ, тому ви можете отримати об'єкт пізніше з іншої точки коду
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.