Benefício de usar Parcelable em vez de serializar o objeto

97

Pelo que entendi, Bundlee Parcelablepertence à forma como o Android realiza a serialização. É usado, por exemplo, na passagem de dados entre atividades. Mas eu me pergunto, se há algum benefício em usar em Parcelablevez da serialização clássica no caso de salvar o estado dos meus objetos de negócios na memória interna, por exemplo? Será mais simples ou mais rápido do que a forma clássica? Onde devo usar a serialização clássica e onde melhor usar bundles?

Vladimir Ivanov
fonte

Respostas:

99

De "Pro Android 2"

NOTA: Ver Parcelable pode ter gerado a pergunta, por que o Android não está usando o mecanismo de serialização Java integrado? Acontece que a equipe do Android chegou à conclusão de que a serialização em Java é muito lenta para satisfazer os requisitos de comunicação entre processos do Android. Portanto, a equipe construiu a solução Parcelable. A abordagem Parcelable requer que você serialize explicitamente os membros de sua classe, mas, no final, você obtém uma serialização muito mais rápida de seus objetos.

Perceba também que o Android fornece dois mecanismos que permitem que você passe dados para outro processo. O primeiro é passar um pacote para uma atividade usando um intent, e o segundo é passar um Parcelable para um serviço. Esses dois mecanismos não são intercambiáveis ​​e não devem ser confundidos. Ou seja, o Parcelable não se destina a ser passado para uma atividade. Se você deseja iniciar uma atividade e passar alguns dados, use um pacote. Parcelable deve ser usado apenas como parte de uma definição AIDL.

Rajath
fonte
9
O que é "Pro Android 2"?
AlikElzin-kilaka,
79
O segundo parágrafo não é verdade, você pode passar um Parcelable como parâmetro para uma atividade usando o bundle ...
Ixx
3
Quando serializo meus objetos, crio um getBundlemétodo e o chamo de writeToParcelas dest.writeBundle(getBundle());e tenho as duas opções disponíveis no objeto automaticamente. Há recursos interessantes do Parcel para objetos
ativos
2
@lxx: Eu estava me perguntando por que alguém precisaria passar um objeto parcelável através do pacote para a atividade. IMO, se você fizer isso, estará adicionando mais um nível de serialização desnecessariamente e nada mais.
Levanta-se em
4
Philippe Breault fez um bom artigo sobre isso e também adicionou um teste de desempenho. developerphil.com/parcelable-vs-serializable
WonderCsabo
23

Serializableé comicamente lento no Android. Limite inútil em muitos casos, na verdade.

Parcele Parcelablesão fantasticamente rápidos, mas sua documentação diz que você não deve usá-lo para serialização de propósito geral para armazenamento, uma vez que a implementação varia com diferentes versões do Android (ou seja, uma atualização de sistema operacional pode quebrar um aplicativo que dependia dele).

A melhor solução para o problema de serialização de dados para armazenamento em uma velocidade razoável é rolar o seu próprio. Eu pessoalmente uso uma de minhas próprias classes de utilitário que tem uma interface semelhante Parcele que pode serializar todos os tipos padrão de forma muito eficiente (às custas da segurança de tipo). Aqui está uma versão resumida disso:

public interface Packageable {
    public void readFromPackage(PackageInputStream in)  throws IOException ;
    public void writeToPackage(PackageOutputStream out)  throws IOException ; 
}


public final class PackageInputStream {

    private DataInputStream input;

    public PackageInputStream(InputStream in) {
        input = new DataInputStream(new BufferedInputStream(in));
    }

    public void close() throws IOException {
        if (input != null) {
            input.close();
            input = null;
        }       
    }

    // Primitives
    public final int readInt() throws IOException {
        return input.readInt();
    }
    public final long readLong() throws IOException {
        return input.readLong();
    }
    public final long[] readLongArray() throws IOException {
        int c = input.readInt();
        if (c == -1) {
            return null;
        }
        long[] a = new long[c];
        for (int i=0 ; i<c ; i++) {
            a[i] = input.readLong();
        }
        return a;
    }

...

    public final String readString()  throws IOException {
        return input.readUTF();
    }
    public final <T extends Packageable> ArrayList<T> readPackageableList(Class<T> clazz) throws IOException {
        int N = readInt();
        if (N == -1) {
            return null;
        }
        ArrayList<T> list = new ArrayList<T>();
        while (N>0) {
            try {
                T item = (T) clazz.newInstance();
                item.readFromPackage(this);
                list.add(item);
            } catch (InstantiationException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
            N--;
        }
        return list;
    }

}



public final class PackageOutputStream {

    private DataOutputStream output;

    public PackageOutputStream(OutputStream out) {
        output = new DataOutputStream(new BufferedOutputStream(out));
    }

    public void close() throws IOException {
        if (output != null) {
            output.close();
            output = null;
        }
    }

    // Primitives
    public final void writeInt(int val) throws IOException {
        output.writeInt(val);
    }
    public final void writeLong(long val) throws IOException {
        output.writeLong(val);
    }
    public final void writeLongArray(long[] val) throws IOException {
        if (val == null) {
            writeInt(-1);
            return;
        }
        writeInt(val.length);
        for (int i=0 ; i<val.length ; i++) {
            output.writeLong(val[i]);
        }
    }

    public final void writeFloat(float val) throws IOException {
        output.writeFloat(val);
    }
    public final void writeDouble(double val) throws IOException {
        output.writeDouble(val);
    }
    public final void writeString(String val) throws IOException {
        if (val == null) {
            output.writeUTF("");
            return;
        }
        output.writeUTF(val);
    }

    public final <T extends Packageable> void writePackageableList(ArrayList<T> val) throws IOException {
        if (val == null) {
            writeInt(-1);
            return;
        }
        int N = val.size();
        int i=0;
        writeInt(N);
        while (i < N) {
            Packageable item = val.get(i);
            item.writeToPackage(this);
            i++;
        }
    }

}
Reuben Scratton
fonte
2
Qual é a diferença entre usar essa sua classe personalizada e apenas implementar a interface externalizável e fazer a mesma coisa?
Carrotman42
1
O pacote serializa os nomes dos campos também ... não é adequado para milhares de objetos.
Reuben Scratton
1
Desculpe, inventar outro serializador é uma droga - agora há apenas mais um "Parcelable" para lidar. Há muito por onde escolher, com uma biblioteca (a diferença é que a biblioteca é verificada, testada e usa um formato que outras pessoas usam): ProtocolBuffers, JSON, XML, etc. É uma pena que a biblioteca Android seja realmente uma merda nesse aspecto .
2
Acho que a frase inicial não é mais verdadeira (5 anos depois de feita). Há muito tempo que uso a serialização java sem problemas. Você pode encontrar algumas coisas potencialmente interessantes sobre esse tópico em uma postagem de blog que acabei de escrever. nemanjakovacevic.net/blog/english/2015/03/24/…
Nemanja Kovacevic
1
Sim, realmente não é mais um problema. Eu não usei nada mais do que Serializable (com implementações otimizadas de readObject / writeObject) por vários anos. Na verdade, eu olhei para um despejo hexadecimal de alguns objetos serializados apenas alguns dias atrás e fiquei satisfeito que não era um desperdício.
Reuben Scratton
11

Se você precisa de serialização para fins de armazenamento, por exemplo, mas deseja evitar a penalidade de velocidade de reflexão incorrida pela interface serializável , você deve criar explicitamente seu próprio protocolo de serialização com a interface externalizável .

Quando implementado corretamente, ele corresponde à velocidade do Parcelable e também leva em conta a compatibilidade entre as diferentes versões do Android e / ou da plataforma Java.

Este artigo também pode esclarecer as coisas:

Qual é a diferença entre Serializable e Externalizable em Java?

Em uma nota lateral, também é a técnica de serialização mais rápida em muitos benchmarks, vencendo Kryo, Avro, Protocol Buffers e Jackson (json):

http://code.google.com/p/thrift-protobuf-compare/wiki/Benchmarking

sobrancelha branca
fonte
7

Parece que hoje em dia a diferença não é tão perceptível, pelo menos não quando você o executa entre suas próprias atividades.

De acordo com os testes mostrados neste site , o Parcelable é cerca de 10 vezes mais rápido nos dispositivos mais novos (como o Nexus 10) e cerca de 17 vezes mais rápido nos antigos (como o wish Z)

então cabe a você decidir se vale a pena.

talvez para classes relativamente pequenas e simples, Serializable é bom, e para o resto, você deve usar Parcelable

desenvolvedor android
fonte
Acho que você está certo sobre o dinheiro, dizendo que a diferença está diminuindo. No entanto, descobri que usar serializável pode ser muito mais eficiente em termos de tamanho da matriz de bytes empacotada e pode ajudar a evitar TransactionTooLargeException. Eu adoraria ouvir seus comentários sobre esta postagem (minha) no blog nemanjakovacevic.net/blog/english/2015/03/24/…
Nemanja Kovacevic
Bem, você poderia apenas colocar o enorme objeto de consumo de memória em uma variável estática e defini-lo como nulo ao buscá-lo (em onCreate, por exemplo). A desvantagem é que ele não oferece suporte a multiprocessos e é uma maneira um pouco suja de fazer isso. Gostaria de saber se esse é o caso se você deseja passar um bitmap grande.
desenvolvedor Android de
4

Parcelable está principalmente relacionado ao IPC usando a infraestrutura Binder , onde os dados são passados ​​como Parcels .

Como o Android depende muito do Binder para a maioria, senão todas, as tarefas IPC, faz sentido implementar Parcelable na maioria dos lugares, e especialmente na estrutura, porque permite passar um objeto para outro processo, se necessário. Torna os objetos "transportáveis".

Mas se você tiver uma camada de negócios não específica do Android que usa extensivamente serializáveis ​​para salvar estados de objeto e só precisa armazená-los no sistema de arquivos, então acho que serializável está bem. Permite evitar o código Parcelable Boiler-plate.

Olivierg
fonte
Em quais exemplos você deseja armazenar um objeto real no sistema de arquivos saya? Por que não simplesmente obter o conteúdo dos objetos e armazenar o conteúdo real em um arquivo. Veja JSON por exemplo ou até mesmo xml. Você pode salvar objetos no formato JSON ou XML, objetos como em POJO / tipos de entidade que constroem um objeto de dados típico construído com principalmente estados e getters e setters para esse estado. Dessa forma, não há necessidade de serializar objetos com o propósito de armazená-los, já que você só se preocupa com o estado dos objetos
Jonathan
1

Com base neste artigo http://www.mooproductions.org/node/6?page=5 Parcelable deve ser mais rápido.

O que não foi mencionado no artigo é que não acho que objetos serializáveis ​​funcionarão em AIDL para serviços remotos.

Mike dg
fonte
1

Eu apenas uso GSON -> Serializar para String JSON -> Restaurar Objeto da String JSON.

FOO
fonte
Isso é bom para objetos pequenos, não tanto quando você tem grandes matrizes de objetos com muitas propriedades em cada um.
Nickmccomb
0

Além disso, Parcelable oferece implementação customizada em que o usuário tem a chance de particionar cada um de seus objetos substituindo writeToParcel (). No entanto, a serialização não faz essa implementação customizada, pois sua maneira de passar dados envolve a API de reflexão JAVA.

Ajay Deepak
fonte