Cómo enviar objetos a través de un paquete

119

Necesito pasar una referencia a la clase que realiza la mayor parte de mi procesamiento a través de un paquete.

El problema es que no tiene nada que ver con intenciones o contextos y tiene una gran cantidad de objetos no primitivos. ¿Cómo empaqueto la clase en un parcelable / serializable y la paso a un startActivityForResult?

ahodder
fuente
2
"Necesito pasar una referencia a la clase que realiza la mayor parte de mi procesamiento a través de un paquete", ¿por qué?
CommonsWare
1
Tengo un objeto (DataManager), maneja un servidor y ejecuta algunos backends para algunas GUI. Cada vez que se establece una nueva conexión, quiero que el usuario pueda iniciar una nueva actividad que enumere en ListView todas las conexiones activas y que el usuario elija una. Los datos resultantes luego se vincularán a una nueva GUI. Esto realmente es solo un selector de aspectos para el back-end.
cambio
3
Si está tratando con la misma instancia de un objeto en múltiples actividades, es posible que desee considerar el patrón singleton . Hay un buen tutorial aquí .
sotrh

Respuestas:

55

Averiguar qué camino tomar requiere responder no solo a la pregunta clave de CommonsWare de "por qué", sino también a la pregunta de "¿a qué?" lo estás pasando.

La realidad es que lo único que puede pasar por paquetes son datos simples; todo lo demás se basa en interpretaciones de lo que significan o apuntan esos datos. No puede pasar literalmente un objeto, pero lo que puede hacer es una de tres cosas:

1) Puede dividir el objeto en sus datos constitutivos, y si lo que está en el otro extremo tiene conocimiento del mismo tipo de objeto, puede ensamblar un clon a partir de los datos serializados. Así es como la mayoría de los tipos comunes pasan a través de paquetes.

2) Puede pasar un mango opaco. Si lo pasa dentro del mismo contexto (aunque uno podría preguntarse por qué molestarse), ese será un identificador que puede invocar o desreferenciar. Pero si lo pasa a través de Binder a un contexto diferente, su valor literal será un número arbitrario (de hecho, estos números arbitrarios cuentan secuencialmente desde el inicio). No puede hacer nada más que realizar un seguimiento de él, hasta que lo devuelva al contexto original, lo que hará que Binder lo transforme de nuevo en el identificador original, haciéndolo útil nuevamente.

3) Puede pasar un identificador mágico, como un descriptor de archivo o una referencia a ciertos objetos de sistema operativo / plataforma, y ​​si establece las banderas correctas, Binder creará un clon apuntando al mismo recurso para el destinatario, que en realidad se puede usar en el otro extremo. Pero esto solo funciona para muy pocos tipos de objetos.

Lo más probable es que esté pasando su clase solo para que el otro extremo pueda realizar un seguimiento de ella y devolvérsela más tarde, o la está pasando a un contexto donde se puede crear un clon a partir de datos de constituyentes serializados ... o bien está tratando de hacer algo que simplemente no va a funcionar y necesita repensar todo el enfoque.

Chris Stratton
fuente
1
Gracias por la respuesta al tour. Tiene razón, todo lo que necesito hacer es pasar una referencia de una lista de objetos a mi nueva actividad. La nueva actividad tomará algunos datos de la lista y mostrará un ListView seleccionable. onSelect, la actividad devolverá un resultado (algunos datos pertenecientes al objeto de clic) a la actividad del host. Si lo comprendo correctamente, creo que su opción 2 maneja esto de la manera más apropiada; ¿Cómo consigo este mango opaco?
cita el
Su otra actividad no puede extraer ningún dato de un objeto opaco para mostrar. Lo que probablemente quiera hacer es crear y pasar algunos objetos sustitutos de un tipo admitido que contengan copias de la información que se mostrará.
Chris Stratton
158

También puede usar Gson para convertir un objeto en un JSONObject y pasarlo a un paquete. Para mí fue la forma más elegante que encontré de hacer esto. No he probado cómo afecta al rendimiento.

En actividad inicial

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

En la siguiente actividad

String jsonMyObject;
Bundle extras = getIntent().getExtras();
if (extras != null) {
   jsonMyObject = extras.getString("myObject");
}
MyObject myObject = new Gson().fromJson(jsonMyObject, MyObject.class);
Laranjeiro
fuente
3
Dado que se trata de pasar cosas entre actividades, no ocurre con la frecuencia suficiente para tener un gran impacto en el rendimiento general de la aplicación. Dicho esto, dudo que funcione serializar el DataManager de la publicación original, ya que parece que tiene conexiones de socket y otras clases similares.
britzl
4
También Google sugiere esta solución en lugar de serializar: consulte la última sección "Alternativas recomendadas" de esta página de documentos
TechNyquist
3
Solo como advertencia, seguí esta técnica por un tiempo, pero hay un límite de memoria para lo que puede pasar como una Cadena, así que asegúrese de que sus datos no sean demasiado grandes.
jiduvah
Consulte blog.madadipouya.com/2015/09/21/… para saber cómo agregar soporte Gson a su proyecto.
geekQ
Solución inteligente, ¡felicitaciones a usted!
Rohit Mandiwal
20

El Parcelable interfaz es una buena forma de pasar un objeto con un Intent.

¿Cómo puedo hacer que mis objetos personalizados sean parcelables? es una respuesta bastante buena sobre cómo usar Parcelable

Los documentos oficiales de Google también incluyen un ejemplo.

chris
fuente
1
o también se pueden serializar.
Jeffrey Blattman
1
¡Pero reduce considerablemente el rendimiento 10 veces! Consulte este punto de referencia: developerphil.com/parcelable-vs-serializable
saiyancoder
2
+1 @ comentario de Mati, sin embargo, ponerlo en contexto 10x cuando se aplica a un solo objeto es el equivalente a 1 ms. Entonces, tal vez no sea tan malo como parece.
pinoyyid
1
De acuerdo. El problema es cuando se trata de colecciones, que es un caso de uso muy común si obtiene recursos de una API Rest. Pero para un solo objeto, no debería ser algo notorio. De todos modos, si todo el código repetitivo se interpone en su camino, puede probar esta biblioteca que genera todo eso para usted: github.com/johncarl81/parceler . ¡Un enfoque realmente agradable!
saiyancoder
Enlace roto: 404 (no encontrado)
Gallal
14

Puede utilizar el estado de la aplicación global .

Actualizar:

Personalice y luego agregue esto a su AndroidManifest.xml:

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

Y luego tenga una clase en su proyecto como esta:

package com.example;

import android.app.Application;

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

Y como " Se puede acceder a través de getApplication () desde cualquier actividad o servicio ", lo usa así:

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

Espero que ayude.

Neil
fuente
1
Gracias por la respuesta, pero ¿cómo?
despedida el
Creo que solo subclase Aplicación y luego puede almacenar lo que quiera. Los cambios xml que necesita se mencionan en el enlace anterior.
Mark Storer
9
Como principio general de diseño, es una buena idea evitar los globales a menos que realmente los necesite. En este caso existen buenas alternativas.
dhaag23
Ok, creo que entiendo lo que estás diciendo. Simplemente extienda la aplicación y agregue una variable que contenga el objeto que debe pasarse; Revisé la página de referencia y no vi los cambios xml necesarios.
aodder
También quería escribir esto como respuesta. Esta es definitivamente una de las formas de hacerlo. Pero tenga en cuenta que estos objetos permanecen en la memoria a menos que los elimine (o se destruya el contexto de la aplicación) y pueden estar ocupando espacio cuando no los necesita.
Igor Čordaš
12

También puede hacer que sus objetos sean serializables y usar los métodos getSerializable y putSerializable del Bundle .

dhaag23
fuente
1
Lo intenté y rápidamente me di cuenta de que no sería práctico. No creo que la mayoría de los objetos almacenados en la clase pasada (subprocesos) sean serializables. :) gracias aunque.
cita el
10

Solución posible:

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

Clase CustomObject:

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

Objetos subpersonalizados:

class SubCustomObject1 implements Serializable{ }

class SubCustomObject2  implements Serializable{ }
PraKhar
fuente
7

Una forma más de enviar objetos a través del paquete es mediante el bundle.putByteArray
código de muestra

public class DataBean implements Serializable {
private Date currentTime;

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

public Date getCurrentTime() {
    return currentTime;
 }
}

poner Object of DataBean en Bundle:

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);
}

Conversión de objetos en matrices de bytes

/**
 * 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();
    }

Recuperar objeto del paquete:

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();
    }
}

Método para obtener objetos de matrices de bytes:

/**
 * 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;
    }

Espero que esto ayude a otros amigos.

Rupesh Yadav
fuente
esto se ve suave y fácil en el código. Pero creo que hay algo más sobre por qué el SDK no ofrece algo como esto para pasar objetos. ¿Puedes contarme algo más sobre esta solución?
Mario Lenci
3
¡No hay necesidad de todo ese código! Use bundle.putSerializable (objectImplementingSerializable): esto hace debajo de lo que está reimplementando aquí nuevamente ...
Risadinha
3

1.Un ejemplo muy directo y fácil de usar, hacer que el objeto a pasar implemente Serializable.

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

2.pasar objeto en paquete

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

3.Obtener el objeto del paquete como serializable y luego convertirlo en Objeto.

Object object = (Object) getArguments().getSerializable("object");
king_T
fuente
0

Esta es una respuesta muy tardía a mi propia pregunta, pero sigue recibiendo atención, así que siento que debo abordarla. La mayoría de estas respuestas son correctas y manejan el trabajo perfectamente. Sin embargo, depende de las necesidades de la aplicación. Esta respuesta se utilizará para describir dos soluciones a este problema.

Solicitud

La primera es la Aplicación , ya que ha sido la respuesta más comentada aquí. La aplicación es un buen objeto para colocar entidades que necesitan una referencia a un contexto. Un `ServerSocket` indudablemente necesitaría un contexto (para E / S de archivos o actualizaciones simples de` ListAdapter`). Yo, personalmente, prefiero esta ruta. Me gustan las aplicaciones, son útiles para la recuperación de contexto (porque pueden volverse estáticas y no es probable que causen una pérdida de memoria) y tienen un ciclo de vida simple.

Servicio

El Servicio` es segundo. Un `Servicio` es en realidad la mejor opción para mi problema porque para eso están diseñados los servicios:
Un servicio es un componente de la aplicación que puede realizar operaciones de larga duración en
el fondo y no proporciona una interfaz de usuario.
Los servicios están ordenados porque tienen un ciclo de vida más definido que es más fácil de controlar. Además, si es necesario, los servicios pueden ejecutarse externamente a la aplicación (es decir, en el arranque). Esto puede ser necesario para algunas aplicaciones o simplemente una función interesante.

Esta no fue una descripción completa de ninguno de los dos, pero dejé enlaces a los documentos para aquellos que quieran investigar más. En general, Servicees mejor para la instancia que necesitaba: ejecutar un ServerSocket en mi dispositivo SPP.

ahodder
fuente
0

Me encontré con esta pregunta cuando estaba buscando una forma de pasar un objeto Date. En mi caso, como se sugirió entre las respuestas, usé Bundle.putSerializable () pero eso no funcionaría para algo complejo como el DataManager descrito en la publicación original.

Mi sugerencia que dará un resultado muy similar a poner dicho DataManager en la Aplicación o convertirlo en Singleton es usar Dependency Injection y vincular el DataManager a un alcance Singleton e inyectar el DataManager donde sea necesario. No solo obtiene el beneficio de una mayor capacidad de prueba, sino que también obtendrá un código más limpio sin todo el código de la placa de caldera que "pasa dependencias entre clases y actividades". (Robo) Guice es muy fácil de trabajar y el nuevo framework Dagger también parece prometedor.

britzl
fuente
1
Bueno, con algo como una fecha, podría simplemente pasar el valor largo. Pero el resto suena bien. Gracias.
2013
0

otra forma simple de pasar un objeto usando un paquete:

  • en el objeto de clase, cree una lista estática u otra estructura de datos con una clave
  • cuando crea el objeto, colóquelo en la lista / estructura de datos con la clave (por ejemplo, la marca de tiempo larga cuando se crea el objeto)
  • cree el método static getObject (clave larga) para obtener el objeto de la lista
  • en el paquete, pase la clave, para que pueda obtener el objeto más tarde desde otro punto del código
shaolin
fuente