Приведение Serilizable к получению HashMap

Я пытаюсь отправить некоторую работу из IntentService в BroadcastReceiver, используя .putExtra() и sendBroadcast(), поэтому у меня есть собственный класс под названием "Message", который расширяет HashMap‹String,String› и реализует Serializable.

public class Message extends HashMap<String,String> implements Serializable{
    public MessageID ID;
    public int Encode(byte[] buff,int off);
    public int Decode(byte[] buff,int off);
    //...
}

И я отправляю это так:

public static void ProcessMessage(Message msg) {
    Intent broadcastIntent = new Intent();
    broadcastIntent.setAction(Receiver.BROADCAST);
    broadcastIntent.addCategory(Intent.CATEGORY_DEFAULT);
    broadcastIntent.putExtra("MESSAGE",(Serializable)msg);
    parentService.sendBroadcast(broadcastIntent);
    Print("Broadcasting intent to receiver ("+Receiver.BROADCAST+") from: "+parentService.toString());
}

И получать так:

public void onReceive(Context context, Intent intent) {
    Sys.Print("Receiver handling: "+intent.getAction());
    if(intent.getAction().equals(BROADCAST)){
        try {
            Message msg = (Message) intent.getSerializableExtra("MESSAGE");
            Sys.Print("Receiver handling " + msg.ID.toString());
        } catch(Exception ex){
            Sys.Print("Failed handling message, reason: "+ex.getStackTrace().toString());
        }
    }
}

Но тут я всегда получаю это: "Failed handling message, reason: java.lang.ClassCastException: java.util.HashMap"

Есть идеи, что может быть не так?

Трассировки стека:

com.myapp.Receiver.onReceive(Receiver.java:24)
android.app.LoadedApk$ReceiverDispatcher$Args.run(LoadedApk.java:709)
android.os.Handler.handleCallback(Handler.java:587)
android.os.Handler.dispatchMessage(Handler.java:92)
android.os.Looper.loop(Looper.java:138)
android.app.ActivityThread.main(ActivityThread.java:3701)
java.lang.reflect.Method.invokeNative(Native Method)
java.lang.reflect.Method.invoke(Method.java:507)
com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:878)
com.android.internal.os.ZygoteInit.main(ZygoteInit.java:636)
dalvik.system.NativeStart.main(Native Method)

Итак, наконец, если у кого-то была похожая проблема, я решил ее так:

public class Message implements Parcelable {
    public HashMap<String,String> Data;
    public Message(){
        Data=new HashMap<>();
    }
    public int Encode(byte[] buff,int off);
    public int Decode(byte[] buff,int off);
    public void Add(String i,String v);
    public String At(String i);
    public boolean ContainsKey(String i);
    @Override
    public int describeContents() {
        return 0;
    }
    @Override
    public void writeToParcel(Parcel out, int flags) {
        out.writeMap(this.Data);
    }
    public static final Parcelable.Creator CREATOR = new Parcelable.Creator() {
        public Message createFromParcel(Parcel in) {
            return new Message(in);
        }
        public Message[] newArray(int size) {
            return new Message[size];
        }
    };
    public Message(Parcel in) {
        Data=new HashMap<>();
        in.readMap(this.Data,String.class.getClassLoader());
    }
}

person Community    schedule 12.04.2015    source источник
comment
поместите всю трассировку стека, пожалуйста. Вы используете одну и ту же JVM с обеих сторон?   -  person Zielu    schedule 12.04.2015
comment
Я запускаю его прямо на устройстве.   -  person    schedule 12.04.2015


Ответы (2)


Вам не понравится ответ.

Экстра хранится в файле Bundle. Android выполняет некоторые «оптимизации» содержимого Bundles и пытается понять, как сериализовать/десериализовать запись, если он знает ее тип. Итак, если вы поместите что-нибудь, что реализует интерфейс Map, в Bundle, когда вы получите его обратно, у вас будет HashMap :-(

См. мой ответ на этот вопрос для подробного объяснения механики.

Чтобы решить вашу проблему, вам нужно, чтобы ваш пользовательский класс использовал (т.е. содержал) HashMap, а не был (т.е. наследовал от) HashMap.

person David Wasser    schedule 12.04.2015

Это ошибка (пока не исправлена) в Android SDK. В отчете об ошибке это ArrayList, но это также применимо к классам, которые реализуют Map или List.

В отчете об ошибке кто-то предложил использовать в качестве исправления следующий держатель:

public class SerializableHolder implements Serializable {
    private Serializable content;
    public Serializable get() {
        return content;
    }
    public SerializableHolder(Serializable content) {
        this.content = content;
    }
}

но я бы предложил вместо этого реализовать Parcelable, который сделан для Android и намного быстрее, чем Serializable.

person ddmps    schedule 12.04.2015
comment
Теперь я всегда сталкиваюсь с тем, что getParcelableExtra(MESSAGE) всегда возвращает null, и я делаю broadcastIntent.putExtra("MESSAGE", (Parcelable) msg); - person ; 12.04.2015
comment
Ошибка не имеет ничего общего с реализацией Serializable. Ошибка связана с оптимизацией Android для классов, реализующих интерфейсы Map или List. См. мой ответ на этот вопрос. - person David Wasser; 12.04.2015
comment
@DavidWasser Итак, вы говорите, что реализация Parcelable вместо Serializable не исправит это? - person ddmps; 12.04.2015
comment
@jakubinf Вы реализовали необходимые методы для Parcelable? См. stackoverflow.com/a/7181792/1690982. - person ddmps; 12.04.2015
comment
Неа. Даже если класс реализует Parcelable вместо Serializable, любая попытка получить экземпляр этого класса из Bundle приведет к HashMap. - person David Wasser; 12.04.2015
comment
Да, но даже если бы я реализовал HashMap‹› и Parcelable вместе, Parcelable никогда не вызывал фактический конструктор сообщений и пытался вызвать несуществующий конструктор HashMap, поэтому, вероятно, определенно лучше использовать HashMap только как член и реализовать несколько дополнительных методов. к основному классу. - person ; 12.04.2015