Como enviar objetos através do pacote

119

Preciso passar uma referência à classe que faz a maior parte do meu processamento por meio de um pacote.

O problema é que não tem nada a ver com intenções ou contextos e tem uma grande quantidade de objetos não primitivos. Como faço para empacotar a classe em um parcelable / serializable e passá-lo para um startActivityForResult?

Ahodder
fonte
2
"Eu preciso passar uma referência para a classe que faz a maior parte do meu processamento por meio de um pacote" - por quê?
CommonsWare
1
Eu tenho um objeto (DataManager), ele lida com um servidor e executa alguns back-ends para alguns GUIs. Sempre que uma nova conexão é estabelecida, quero que o usuário seja capaz de iniciar uma nova atividade que liste em ListView todas as conexões ativas e que o usuário escolha uma. Os dados resultantes serão vinculados a uma nova GUI. Esta é realmente apenas uma escolha de pele para o back-end.
ahodder
3
Se você estiver lidando com a mesma instância de um objeto em várias atividades, pode considerar o padrão singleton . Há um bom tutorial aqui .
sotrh

Respostas:

55

Descobrir que caminho seguir requer responder não apenas à pergunta-chave do CommonsWare sobre "por que", mas também à pergunta "para quê?" você está passando.

A realidade é que a única coisa que pode passar por pacotes são dados simples - todo o resto é baseado em interpretações do que esses dados significam ou apontam. Você não pode literalmente passar um objeto, mas o que você pode fazer é uma das três coisas:

1) Você pode dividir o objeto em seus dados constituídos e, se o que está do outro lado tiver conhecimento do mesmo tipo de objeto, pode montar um clone a partir dos dados serializados. É assim que a maioria dos tipos comuns passa pelos pacotes.

2) Você pode passar uma alça opaca. Se você estiver passando dentro do mesmo contexto (embora alguém possa perguntar por que se preocupar), esse será um identificador que você pode invocar ou desreferenciar. Mas se você passá-lo pelo Binder para um contexto diferente, seu valor literal será um número arbitrário (na verdade, esses números arbitrários contam sequencialmente desde a inicialização). Você não pode fazer nada além de mantê-lo sob controle, até que você o passe de volta ao contexto original, o que fará com que o Binder o transforme de volta ao identificador original, tornando-o útil novamente.

3) Você pode passar um identificador mágico, como um descritor de arquivo ou referência a certos objetos os / plataforma, e se definir os sinalizadores corretos, o Binder criará um clone apontando para o mesmo recurso para o destinatário, que pode realmente ser usado em O outro fim. Mas isso só funciona para alguns tipos de objetos.

Provavelmente, você está passando em sua classe apenas para que o outro lado possa acompanhá-la e devolvê-la a você mais tarde, ou está passando para um contexto onde um clone pode ser criado a partir de dados constituintes serializados ... ou então você está tentando fazer algo que simplesmente não vai funcionar e precisa repensar toda a abordagem.

Chris Stratton
fonte
1
Obrigado pela resposta do tour. Certo, tudo que preciso fazer é apenas passar uma referência de uma lista de objetos para minha nova atividade. A nova atividade pegará alguns dados da lista e exibirá um ListView selecionável. onSelect, a atividade retornará um resultado (alguns dados pertencentes ao objeto de clique) para a atividade do host. Se bem entendi, acredito que sua opção 2 lida com isso de forma mais adequada; como faço para obter essa alça opaca?
ahodder
Sua outra atividade não pode extrair nenhum dado de um objeto opaco para exibir. O que você provavelmente deseja fazer é criar e passar alguns objetos substitutos de um tipo suportado que contém cópias das informações que seriam exibidas.
Chris Stratton
158

Você também pode usar Gson para converter um objeto em um JSONObject e passá-lo no pacote. Para mim foi a maneira mais elegante que encontrei de fazer isso. Não testei como isso afeta o desempenho.

Em atividade inicial

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

Na Próxima Atividade

String jsonMyObject;
Bundle extras = getIntent().getExtras();
if (extras != null) {
   jsonMyObject = extras.getString("myObject");
}
MyObject myObject = new Gson().fromJson(jsonMyObject, MyObject.class);
Laranjeiro
fonte
3
Como é uma questão de passar coisas entre as atividades, isso não acontece com frequência suficiente para ter um grande impacto no desempenho geral do aplicativo. Dito isso, duvido que funcione serializar o DataManager da postagem original, pois parece que ele tem conexões de soquete e outras classes semelhantes.
britzl de
4
Além disso, o Google sugere esta solução em vez de serializar: verifique a última seção "Alternativas recomendadas" desta página de documento
TechNyquist
3
Apenas como um aviso, eu segui essa técnica por um tempo, mas há um limite de memória para o que você pode passar como String, então certifique-se de que seus dados não sejam muito grandes.
jiduvah
Consulte blog.madadipouya.com/2015/09/21/… para saber como adicionar suporte Gson ao seu projeto.
geekQ
Solução inteligente, tiro o chapéu para você!
Rohit Mandiwal de
20

A interface Parcelable é uma boa maneira de passar um objeto com um Intent.

Como posso tornar meus objetos personalizados Parceláveis? é uma boa resposta sobre como usar o Parcelable

Os documentos oficiais do Google também incluem um exemplo

Chris
fonte
1
ou eles também podem ser serializáveis.
Jeffrey Blattman
1
Mas reduz consideravelmente o desempenho 10x !! Confira este benchmark: developerphil.com/parcelable-vs-serializable
saiyancoder
2
O comentário de +1 @ Mati, no entanto, para colocá-lo em contexto 10x quando aplicado a um único objeto é o equivalente a 1 ms. Portanto, talvez não seja tão ruim quanto parece.
pinoyyid,
1
Aceita. O problema é quando você lida com coleções, que é um caso de uso muito comum se você estiver obtendo recursos de uma API Rest. Mas, para um único objeto, não deve ser algo notório. De qualquer forma, se todo o código clichê está atrapalhando seu caminho, você pode tentar esta biblioteca que gera tudo para você: github.com/johncarl81/parceler . Uma abordagem muito boa!
saiyancoder
Link quebrado: 404 (não encontrado)
Gallal
14

Você pode usar o estado do aplicativo global .

Atualizar:

Personalize e adicione ao seu AndroidManifest.xml:

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

E então tenha uma aula em seu projeto como esta:

package com.example;

import android.app.Application;

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

E porque " Ele pode ser acessado via getApplication () de qualquer atividade ou serviço ", você o usa assim:

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

Espero que ajude.

Neil
fonte
1
Obrigado pela resposta, mas como?
ahodder de
Acredito que você apenas subclasse Application e possa armazenar o que quiser. As alterações xml de que você precisa são mencionadas no link acima.
Mark Storer
9
Como princípio geral de design, é uma boa ideia evitar globais, a menos que você realmente precise deles. Neste caso, existem boas alternativas.
dhaag23
Ok, acho que entendi o que você está dizendo. Basta estender Application e inserir uma variável contendo o objeto que precisa ser passado; Revisei a página de referência e não vi as alterações xml necessárias.
ahodder
Eu também queria escrever isso como uma resposta. Esta é definitivamente uma das maneiras de fazer isso. Mas lembre-se de que esses objetos permanecem na memória, a menos que você os desvie (ou o contexto do aplicativo seja destruído) e podem estar ocupando espaço quando você não precisa deles.
Igor Čordaš
12

Você também pode fazer seus objetos Serializable e usar o Pacote getSerializable e putSerializable métodos.

dhaag23
fonte
1
Eu tentei isso e rapidamente percebi que não seria prático. Não acho que a maioria dos objetos armazenados na classe passada (threads) sejam serializáveis. :) obrigado.
ahodder
10

Solução possível:

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

Classe CustomObject:

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

Objetos subpersonalizados:

class SubCustomObject1 implements Serializable{ }

class SubCustomObject2  implements Serializable{ }
PraKhar
fonte
7

Outra maneira de enviar objetos por meio do pacote é usando o bundle.putByteArray
código de amostra

public class DataBean implements Serializable {
private Date currentTime;

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

public Date getCurrentTime() {
    return currentTime;
 }
}

coloque o objeto do DataBean no pacote:

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

Convertendo objetos em matrizes 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();
    }

Obtenha o objeto de volta do pacote:

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 obter objetos de matrizes 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 isso ajude outros amigos.

Rupesh Yadav
fonte
isso parece suave e fácil de olhar para o código. Mas eu sinto que há algo mais sobre por que o SDK não oferece algo assim para passar objetos. Você pode me dizer mais alguma coisa sobre essa solução?
Mario Lenci
3
Não há necessidade de todo aquele código! Use bundle.putSerializable (objectImplementingSerializable) - isso embaixo do que você está reimplementando aqui de novo ...
Risadinha
3

1. Um exemplo muito direto e fácil de usar, faça o objeto a ser passado implementar Serializable.

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

2.passar o objeto no pacote

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

3. receber objeto passado do pacote como serializável e, em seguida, lançar para objeto.

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

Esta é uma resposta muito tardia à minha própria pergunta, mas continua recebendo atenção, então sinto que devo abordá-la. Muitas dessas respostas estão corretas e funcionam perfeitamente com o trabalho. No entanto, isso depende das necessidades do aplicativo. Essa resposta será usada para descrever duas soluções para esse problema.

Inscrição

O primeiro é o Aplicativo , pois foi a resposta mais falada aqui. O aplicativo é um bom objeto para colocar entidades que precisam de uma referência a um Contexto. Um `ServerSocket` sem dúvida precisaria de um contexto (para E / S de arquivo ou atualizações simples de` ListAdapter`). Eu, pessoalmente, prefiro esse caminho. Eu gosto de aplicativos, eles são úteis para a recuperação de contexto (porque podem se tornar estáticos e provavelmente não causam vazamento de memória) e têm um ciclo de vida simples.

Serviço

O serviço é o segundo. Na verdade, um `Serviço` é a melhor escolha para o meu problema porque é para isso que os serviços são projetados:
Um serviço é um componente de aplicativo que pode executar operações de longa duração em
o plano de fundo e não fornece uma interface de usuário.
Os serviços são organizados porque têm um ciclo de vida mais definido e mais fácil de controlar. Além disso, se necessário, os serviços podem ser executados externamente ao aplicativo (ou seja, na inicialização). Isso pode ser necessário para alguns aplicativos ou apenas um recurso interessante.

Esta não é uma descrição completa de nenhum dos dois, mas deixei links para os documentos para aqueles que desejam investigar mais. No geral, Serviceé o melhor para a instância que eu precisava - executando um ServerSocket no meu dispositivo SPP.

Ahodder
fonte
0

Eu me deparei com essa pergunta quando estava procurando uma maneira de passar um objeto Date. No meu caso, como foi sugerido entre as respostas, usei Bundle.putSerializable () mas isso não funcionaria para algo complexo como o DataManager descrito no post original.

Minha sugestão que dará um resultado muito semelhante a colocar o referido DataManager no aplicativo ou torná-lo um Singleton é usar a injeção de dependência e vincular o DataManager a um escopo Singleton e injetar o DataManager onde for necessário. Você não apenas obterá o benefício de maior testabilidade, mas também obterá um código mais limpo, sem todo o código clichê "passar dependências entre classes e atividades". (Robo) Guice é muito fácil de trabalhar e o novo framework Dagger também parece promissor.

Britzl
fonte
1
Bem, com algo como uma data, você poderia simplesmente passar o valor longo. Mas, o resto parece bom. Obrigado.
ahodder
0

outra maneira simples de passar um objeto usando um pacote:

  • no objeto de classe, crie uma lista estática ou outra estrutura de dados com uma chave
  • ao criar o objeto, coloque-o na lista / estrutura de dados com a chave (por exemplo, o carimbo de data / hora longo quando o objeto é criado)
  • crie o método static getObject (long key) para obter o objeto da lista
  • no pacote passe a chave, para que você possa obter o objeto mais tarde de outro ponto do código
Shaolin
fonte