Как передать объект из одного действия в другое на Android - PullRequest
696 голосов
/ 29 апреля 2010

Я пытаюсь отправить объект моего клиента класса из одного Activity и отобразить его в другом Activity.

Код для класса клиента:

public class Customer {

    private String firstName, lastName, Address;
    int Age;

    public Customer(String fname, String lname, int age, String address) {

        firstName = fname;
        lastName = lname;
        Age = age;
        Address = address;
    }

    public String printValues() {

        String data = null;

        data = "First Name :" + firstName + " Last Name :" + lastName
        + " Age : " + Age + " Address : " + Address;

        return data;
    }
}

Я хочу отправить свой объект с одного Activity на другой, а затем отобразить данные на другом Activity.

Как мне этого добиться?

Ответы [ 31 ]

808 голосов
/ 29 апреля 2010

Один из вариантов может позволить вашему пользовательскому классу реализовать интерфейс Serializable, а затем вы можете передать экземпляры объекта в дополнительные намерения, используя вариант putExtra(Serializable..) метода Intent#putExtra().

Псевдокод :

//To pass:
intent.putExtra("MyClass", obj);

// To retrieve object in second Activity
getIntent().getSerializableExtra("MyClass");

Примечание. Убедитесь, что в каждом вложенном классе вашего основного пользовательского класса реализован интерфейс Serializable, чтобы избежать любых исключений сериализации.Например:

class MainClass implements Serializable {

    public MainClass() {}

    public static class ChildClass implements Serializable {

        public ChildClass() {}
    }
}
285 голосов
/ 20 октября 2011

Реализуйте свой класс с Serializable. Давайте предположим, что это ваш класс сущности:

import java.io.Serializable;

@SuppressWarnings("serial") //With this annotation we are going to hide compiler warnings
public class Deneme implements Serializable {

    public Deneme(double id, String name) {
        this.id = id;
        this.name = name;
    }

    public double getId() {
        return id;
    }

    public void setId(double id) {
        this.id = id;
    }

    public String getName() {
        return this.name;
    }

    public void setName(String name) {
        this.name = name;
    }

    private double id;
    private String name;
}

Мы отправляем объект с именем dene из активности X в деятельность Y. Где-то в X деятельности;

Deneme dene = new Deneme(4,"Mustafa");
Intent i = new Intent(this, Y.class);
i.putExtra("sampleObject", dene);
startActivity(i);

В Y-деятельности мы получаем объект.

Intent i = getIntent();
Deneme dene = (Deneme)i.getSerializableExtra("sampleObject");

Вот и все.

112 голосов
/ 16 апреля 2012
  • Использование глобальных статических переменных не является хорошей практикой разработки программного обеспечения .
  • Преобразование полей объекта в примитивные типы данных могут быть беспокойным заданием .
  • Использование serializable - это нормально, но оно неэффективно на платформе Android.
  • Parcelable специально разработан для Android, и вы должны использовать его. Вот простой пример: Передача пользовательских объектов между действиями Android

Вы можете сгенерировать Parcelable-код для вашего класса, используя этот сайт .

91 голосов
/ 03 августа 2011

при вызове активности

Intent intent = new Intent(fromClass.this,toClass.class).putExtra("myCustomerObj",customerObj);

В toClass.java получить активность по

Customer customerObjInToClass = getIntent().getExtras().getParcelable("myCustomerObj");

Пожалуйста, убедитесь, что класс клиента реализует пакет

public class Customer implements Parcelable {

    private String firstName, lastName, address;
    int age;

    /* all your getter and setter methods */

    public Customer(Parcel in ) {
        readFromParcel( in );
    }

    public static final Parcelable.Creator CREATOR = new Parcelable.Creator() {
        public LeadData createFromParcel(Parcel in ) {
            return new Customer( in );
        }

        public Customer[] newArray(int size) {
            return new Customer[size];
        }
    };


    @Override
    public void writeToParcel(Parcel dest, int flags) {

        dest.writeString(firstName);
        dest.writeString(lastName);
        dest.writeString(address);
        dest.writeInt(age);
    }

    private void readFromParcel(Parcel in ) {

        firstName = in .readString();
        lastName  = in .readString();
        address   = in .readString();
        age       = in .readInt();
    }
83 голосов
/ 18 ноября 2013

По моему опыту есть три основных решения, каждое со своими недостатками и преимуществами:

  1. Реализация Parcelable

  2. Реализация Сериализуемый

  3. Использование какой-либо облегченной библиотеки событий (например, EventBus Greenrobot или Square Otto)

Parcelable - быстрый и стандартный для Android, но он имеет много стандартного кода и требует жестко закодированных строк для справки при извлечении значений из намерения (не строго типизированного).

Сериализуемый - пример нуля близок к нулю, но это самый медленный подход и также требует жестко закодированных строк при извлечении значений из намерения (не строго типизированный).

Шина событий - нулевой шаблон, самый быстрый подход и не требует жестко закодированных строк, но требует дополнительной зависимости (хотя обычно и небольшой, ~ 40 КБ)

Я опубликовал очень подробное сравнение этих трех подходов, включая критерии эффективности.

82 голосов
/ 31 октября 2013

Используйте gson , чтобы преобразовать ваш объект в JSON и передать его через намерение. В новом Activity конвертируйте JSON в объект.

В вашем build.gradle, добавьте это к вашим зависимостям

implementation 'com.google.code.gson:gson:2.8.4'

В вашей Деятельности преобразуйте объект в json-строку:

Gson gson = new Gson();
String myJson = gson.toJson(vp);
intent.putExtra("myjson", myjson);

В получающем действии конвертируйте строку json обратно в исходный объект:

Gson gson = new Gson();
YourObject ob = gson.fromJson(getIntent().getStringExtra("myjson"), YourObject.class);

Для Котлин это совершенно то же самое

Передать данные

val gson = Gson()
val intent = Intent(this, YourActivity::class.java)
intent.putExtra("identifier", gson.toJson(your_object))
startActivity(intent)

Получить данные

val gson = Gson()
val yourObject = gson.fromJson<YourObject>(intent.getStringExtra("identifier"), YourObject::class.java)
39 голосов
/ 29 апреля 2010

Вы также можете записать данные объекта во временные строки и целые числа и передать их в действие. Конечно, таким образом вы получаете транспортируемые данные, но не сам объект.

Но если вы просто хотите отобразить их, а не использовать объект в другом методе или что-то в этом роде, этого должно быть достаточно. Я сделал это так же, чтобы просто отобразить данные из одного объекта в другом упражнении.

String fName_temp   = yourObject.getFname();
String lName_temp   = yourObject.getLname();
String age_temp     = yourObject.getAge();
String address_temp = yourObject.getAddress();

Intent i = new Intent(this, ToClass.class);
i.putExtra("fname", fName_temp);
i.putExtra("lname", lName_temp);
i.putExtra("age", age_temp);
i.putExtra("address", address_temp);

startActivity(i);

Вы также можете передать их напрямую вместо временных иваров, но, на мой взгляд, так будет понятнее. Кроме того, вы можете установить для временных ивров значение null, чтобы они быстрее очищались сборщиком мусора.

Удачи!

Примечание: переопределите toString () вместо написания собственного метода печати. ​​

Как уже упоминалось в комментариях ниже, вот как вы вернете свои данные в другом упражнении:

String fName = getIntent().getExtras().getInt("fname");
35 голосов
/ 12 июня 2016

Я нашел простой и элегантный метод:

  • НЕТ Parcelable
  • НЕТ Сериализуемый
  • Нет статического поля
  • No Event Bus

Метод 1

Код для первого занятия:

    final Object objSent = new Object();
    final Bundle bundle = new Bundle();
    bundle.putBinder("object_value", new ObjectWrapperForBinder(objSent));
    startActivity(new Intent(this, SecondActivity.class).putExtras(bundle));        
    Log.d(TAG, "original object=" + objSent);

Код для второго занятия:

    final Object objReceived = ((ObjectWrapperForBinder)getIntent().getExtras().getBinder("object_value")).getData();
    Log.d(TAG, "received object=" + objReceived);

вы найдете, что objSent & objReceived имеют одинаковые hashCode, поэтому они идентичны.

Но почему мы можем передать объект Java таким образом?

На самом деле связыватель Android создаст глобальную ссылку JNI для объекта java и выпустит эту глобальную ссылку JNI, если для этого объекта java нет ссылки. binder сохранит эту глобальную ссылку JNI в объекте Binder.

* ПРЕДУПРЕЖДЕНИЕ: этот метод работает ТОЛЬКО в том случае, если два действия выполняются в одном и том же процессе, в противном случае генерируется исключение ClassCastException в (ObjectWrapperForBinder) getIntent (). GetExtras (). GetBinder ("object_value") *

определение класса ObjectWrapperForBinder

public class ObjectWrapperForBinder extends Binder {

    private final Object mData;

    public ObjectWrapperForBinder(Object data) {
        mData = data;
    }

    public Object getData() {
        return mData;
    }
}

Метод 2

  • для отправителя,
    1. использовать собственный нативный метод для добавления вашего Java-объекта в глобальную справочную таблицу JNI (через JNIEnv :: NewGlobalRef)
    2. положить возвращаемое целое число (на самом деле, JNIEnv :: NewGlobalRef return jobject, который является указателем, мы можем безопасно преобразовать его в int) в ваше намерение (через Intent :: putExtra)
  • для получателя
    1. получить целое число из Intent (через Intent :: getInt)
    2. использовать собственный нативный метод для восстановления вашего Java-объекта из глобальной справочной таблицы JNI (через JNIEnv :: NewLocalRef)
    3. удалить элемент из глобальной справочной таблицы JNI (через JNIEnv :: DeleteGlobalRef),

Но у метода 2 есть небольшая, но серьезная проблема: если получателю не удается восстановить объект Java (например, возникает какое-то исключение перед восстановлением объекта Java, или действие получателя вообще не существует), тогда объект Java станет сиротой или утечкой памяти, Способ 1 не имеет этой проблемы, потому что Android Binder будет обрабатывать это исключение

Метод 3

Для удаленного вызова java-объекта мы создадим контракт данных / интерфейс для описания java-объекта и будем использовать файл aidl

IDataContract.aidl

package com.example.objectwrapper;
interface IDataContract {
    int func1(String arg1);
    int func2(String arg1);
}

Код для первого занятия

    final IDataContract objSent = new IDataContract.Stub() {

        @Override
        public int func2(String arg1) throws RemoteException {
            // TODO Auto-generated method stub
            Log.d(TAG, "func2:: arg1=" + arg1);
            return 102;
        }

        @Override
        public int func1(String arg1) throws RemoteException {
            // TODO Auto-generated method stub
            Log.d(TAG, "func1:: arg1=" + arg1);
            return 101;
        }
    };
    final Bundle bundle = new Bundle();
    bundle.putBinder("object_value", objSent.asBinder());
    startActivity(new Intent(this, SecondActivity.class).putExtras(bundle));
    Log.d(TAG, "original object=" + objSent);

Код для второго занятия:

измените атрибут android: process в AndroidManifest.xml на непустое имя процесса, чтобы обеспечить выполнение второго действия в другом процессе

    final IDataContract objReceived = IDataContract.Stub.asInterface(getIntent().getExtras().getBinder("object_value"));
    try {
        Log.d(TAG, "received object=" + objReceived + ", func1()=" + objReceived.func1("test1") + ", func2()=" + objReceived.func2("test2"));
    } catch (RemoteException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }

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

Метод 4

метод 3 кажется недостаточно простым, потому что мы должны реализовать вспомогательный интерфейс. Если вы просто хотите выполнить простую задачу, а возвращаемое значение метода не нужно, мы можем использовать android.os.Messenger

Код для первого действия (отправитель):

public class MainActivity extends Activity {
    private static final String TAG = "MainActivity";

    public static final int MSG_OP1 = 1;
    public static final int MSG_OP2 = 2;

    public static final String EXTRA_MESSENGER = "messenger";

    private final Handler mHandler = new Handler() {

        @Override
        public void handleMessage(Message msg) {
            // TODO Auto-generated method stub
            Log.e(TAG, "handleMessage:: msg=" + msg);
            switch (msg.what) {
            case MSG_OP1:

                break;
            case MSG_OP2:
                break;

            default:

                break;
            }
            super.handleMessage(msg);
        }

    };
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        startActivity(new Intent(this, SecondActivity.class).putExtra(EXTRA_MESSENGER, new Messenger(mHandler)));
    }
}

Код для второго действия (получатель):

public class SecondActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_second);

        final Messenger messenger = getIntent().getParcelableExtra(MainActivity.EXTRA_MESSENGER);
        try {
            messenger.send(Message.obtain(null, MainActivity.MSG_OP1, 101, 1001, "10001"));
            messenger.send(Message.obtain(null, MainActivity.MSG_OP2, 102, 1002, "10002"));
        } catch (RemoteException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

    }
}

Все Messenger.send будут выполняться в обработчике асинхронно и последовательно.

На самом деле, android.os.Messenger также является вспомогательным интерфейсом, если у вас есть исходный код Android, вы можете найти файл с именем IMessenger.aidl

package android.os;

import android.os.Message;

/** @hide */
oneway interface IMessenger {
    void send(in Message msg);
}
25 голосов
/ 20 июня 2012

Я создал одноэлементный вспомогательный класс, который содержит временные объекты.

public class IntentHelper {

    private static IntentHelper _instance;
    private Hashtable<String, Object> _hash;

    private IntentHelper() {
        _hash = new Hashtable<String, Object>();
    }

    private static IntentHelper getInstance() {
        if(_instance==null) {
            _instance = new IntentHelper();
        }
        return _instance;
    }

    public static void addObjectForKey(Object object, String key) {
        getInstance()._hash.put(key, object);
    }

    public static Object getObjectForKey(String key) {
        IntentHelper helper = getInstance();
        Object data = helper._hash.get(key);
        helper._hash.remove(key);
        helper = null;
        return data;
    }
}

Вместо того, чтобы помещать ваши объекты в Intent, используйте IntentHelper:

IntentHelper.addObjectForKey(obj, "key");

Внутри вашей новой Деятельности вы можете получить объект:

Object obj = (Object) IntentHelper.getObjectForKey("key");

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

23 голосов
/ 20 мая 2013

Есть несколько способов получить доступ к переменным или объектам в других классах или в Activity.

A. База данных

B. Общие настройки.

C. Сериализация объекта.

D. Класс, который может содержать общие данные, может называться Common Utilities. Это зависит от вас.

Е. Передача данных через интерфейс Intents и Parcelable.

Это зависит от потребностей вашего проекта.

A. База данных

SQLite - это база данных с открытым исходным кодом, встроенная в Android. SQLite поддерживает стандартные функции реляционной базы данных, такие как синтаксис SQL, транзакции и подготовленные операторы.

1024 * Учебники *

B. Общие настройки

Предположим, вы хотите сохранить имя пользователя. Таким образом, теперь будет две вещи: ключ имя пользователя, значение значение.

Как хранить

 // Create object of SharedPreferences.
 SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(this);

 //Now get Editor
 SharedPreferences.Editor editor = sharedPref.edit();

 //Put your value
 editor.putString("userName", "stackoverlow");

 //Commits your edits
 editor.commit();

Используя putString (), putBoolean (), putInt (), putFloat () и putLong () вы можете сохранить желаемый тип данных.

Как получить

SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(this);
String userName = sharedPref.getString("userName", "Not Available");

http://developer.android.com/reference/android/content/SharedPreferences.html

C. Сериализация объекта

Объектная серизация используется, если мы хотим сохранить состояние объекта для отправки его по сети или вы также можете использовать его для своих целей.

Используйте Java bean-компоненты и сохраняйте их как одно из его полей и используйте для этого методы getter и setter.

JavaBeans - это классы Java, которые имеют свойства. Думать о свойства как частные переменные экземпляра. Так как они частные, единственный способ они могут быть доступны извне их класса через методы в классе. Методы, которые изменяют значение свойства, называются методами установки, а методы, которые извлекают значение свойства, называются методами получения.

public class VariableStorage implements Serializable  {

    private String inString;

    public String getInString() {
        return inString;
    }

    public void setInString(String inString) {
        this.inString = inString;
    }
}

Установите переменную в вашем почтовом методе, используя

VariableStorage variableStorage = new VariableStorage();
variableStorage.setInString(inString);

Затем используйте сериализацию объекта для сериализации этого объекта, а в другом классе десериализуйте этот объект.

При сериализации объект может быть представлен в виде последовательности байтов, которая включает в себя данные объекта, а также информацию о типе объекта и типах данных, хранящихся в объекте.

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

Если вы хотите учебник для этого обратитесь к:

D. CommonUtilities

Вы можете самостоятельно создать класс, который может содержать общие данные, которые вам часто нужны в вашем проекте.

Пример

public class CommonUtilities {

    public static String className = "CommonUtilities";

}

E. Передача данных через намерения

Пожалуйста, обратитесь к учебнику Android - Данные посылки для передачи между действиями с использованием классов Parcelable для этой опции передачи данных.

...