简体   繁体   中英

Problems with Gson and orientation changes

I'm trying to save a serialize/deserialize a List of POJOS with Gson. While normally this isn't such a special task, I'm getting an exception that I've never seen before:

01-11 14:17:22.556: E/AndroidRuntime(15941): java.lang.RuntimeException: 
Unable to start activity ComponentInfo{com.timkranen.playpalproject/com.timkranen.playpalproject.HomeActivity}: 
java.lang.RuntimeException: Unable to invoke no-args constructor for interface java.util.concurrent.locks.Lock. 
Register an InstanceCreator with Gson for this type may fix this problem.

I'm suspecting that it has something to with the fact that the List items are being loaded in an AsyncTask. Anyone have experience with this problem?

I've tried putting the logic that I execute within onSaveInstanceState (for saving) in a synchronized method, but that didn't help.

Edit

Here's some of my code to try and make it more clear. I've got a List that is called friendsList. The List is filled in this AsyncTask and is executed in onCreateView()

private class RetrieveFriends extends AsyncTask<Void, Integer, String> {

    @Override
    protected String doInBackground(Void... params) {
        // get friends
        if (friendProfiles == null || friendProfiles.size() == 0) {
            friendProfiles = new ArrayList<Profile>();
            if (currentProfile.getFriendUids() != null
                    && currentProfile.getFriendUids().size() > 0)
                for (String fUid : currentProfile.getFriendUids()) {
                    Profile friend = ProfileDataManager
                            .getProfileFromId(fUid);
                    friendProfiles.add(friend);
                }

            if (friendProfiles.size() == 0) {
                return "null";
            }
        }

        return "notnull";

    }

    @Override
    protected void onPostExecute(String result) {
        if (!result.equals("null")) {
            loadingFriendsBar.setVisibility(View.INVISIBLE);
            friendsList.setVisibility(View.VISIBLE);
            FriendListAdapter adapter = new FriendListAdapter(
                    containedActivity, R.layout.friendslist_row,
                    friendProfiles);
            friendsList.setAdapter(adapter);
        } else {
            loadingFriendsBar.setVisibility(View.INVISIBLE);
            friendMsg.setVisibility(View.VISIBLE);
        }
    }

}

Now in the onSaveInstanceState I serialize that List to JSON like this:

private synchronized void saveToState(Bundle state) {
    Gson gson = new Gson();
    Type listOfProfiles = new TypeToken<List<Profile>>() {
    }.getType();
    String json = gson.toJson(friendProfiles, listOfProfiles);
    state.putString("json_friendProfiles", json);
}

That method is called directly in onSaveInstanceState(). Retrieving it is the same:

private synchronized void retrieveFromState(String json) {
    Type listOfProfiles = new TypeToken<List<Profile>>() {
    }.getType();
    Gson gson = new Gson();
    friendProfiles = (List<Profile>) gson.fromJson(json,
            listOfProfiles);
}

The weird thing is, the state is correctly saved when navigating to a different Fragment. The error only occurs when I change the orientation.

Edit: On request here's the Profile class

public class Profile {

private String mEmail;
private String mPassword;
private String uid;

// optional properties
private String name;
private String location;
private String about;
private ParseFile image; // not certain of data type

private List<String> friendUids;

public String getName() {
    if (name == null || name.equals("")) {
        return "Name unknown";
    }
    return name;
}

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

public String getLocation() {
    if (location == null || location.equals("")) {
        return "Location unknown";
    }
    return location;
}

public void setLocation(String location) {
    this.location = location;
}

public String getAbout() {
    if (about == null || about.equals("")) {
        return "About unknown";
    }
    return about;
}

public void setAbout(String about) {
    this.about = about;
}

public void setUid(String Uid) {
    this.uid = Uid;
}

public String getUid() {
    return this.uid;
}

public String getPassword() {
    return mPassword;
}

public String getEmail() {
    return mEmail;
}

public Profile(String email, String password) {
    this.mEmail = email;
    this.mPassword = password;
}

/*
 * Saves a Profile and returns the profiles UID This is ONLY APPLICABLE for
 * NEW profiles use the update method to update existing profile data
 */
public void saveToParse(SaveCallback saveCallBack) {
    if (ProfileDataManager.IsRegistered(this) != true) {
        ParseObject pObject = new ParseObject("Profiles");
        pObject.put("email", this.mEmail);
        pObject.put("password", this.mPassword);
        pObject.saveInBackground(saveCallBack);
    } else {
        saveCallBack.done(new ParseException(ErrorCodes.ALREADY_REGISTERED,
                "AlreadyRegistered"));
    }
}

public void update() {
    ParseQuery<ParseObject> query = ParseQuery.getQuery("Profiles");
    query.getInBackground(this.uid, new GetCallback<ParseObject>() {

        @Override
        public void done(ParseObject object, ParseException e) {
            if (e == null) {
                // update the object
                object.put("email", Profile.this.mEmail);
                object.put("password", Profile.this.mPassword);
                if (Profile.this.name != null) {
                    object.put("name", Profile.this.name);
                }
                if (Profile.this.location != null) {
                    object.put("location", Profile.this.location);
                }
                if (Profile.this.about != null) {
                    object.put("about", Profile.this.about);
                }
                if (Profile.this.image != null) {
                    object.put("profileImage", Profile.this.image);
                }
                if (Profile.this.friendUids != null
                        && Profile.this.friendUids.size() != 0) {
                    object.put("friends", Profile.this.friendUids);
                }

                object.saveInBackground();
            }
        }
    });
}

/*
 * Use updateWithCallBack when you want to update an object but want to show
 * the updated data immediatly using a callback, when calling this method
 * make sure that currentProfile in HomeActivity is set to the new Profile!
 */
public void updateWithCallBack(final SaveCallback callBack) {
    ParseQuery<ParseObject> query = ParseQuery.getQuery("Profiles");
    query.getInBackground(this.uid, new GetCallback<ParseObject>() {

        @Override
        public void done(ParseObject object, ParseException e) {
            if (e == null) {
                // update the object
                object.put("email", Profile.this.mEmail);
                object.put("password", Profile.this.mPassword);
                if (Profile.this.name != null) {
                    object.put("name", Profile.this.name);
                }
                if (Profile.this.location != null) {
                    object.put("location", Profile.this.location);
                }
                if (Profile.this.about != null) {
                    object.put("about", Profile.this.about);
                }
                if (Profile.this.image != null) {
                    object.put("profileImage", Profile.this.image);
                }
                if (Profile.this.friendUids != null
                        && Profile.this.friendUids.size() != 0) {
                    object.put("friends", Profile.this.friendUids);
                }
                object.saveInBackground(callBack);
            }
        }
    });
}

// retrieves the image, when done calls callback
public void retrieveProfileImage(GetDataCallback callBack) {
    this.image.getDataInBackground(callBack);
}

public ParseFile getProfileImage() {
    return this.image;
}

public void setProfileImage(ParseFile image) {
    this.image = image;
}

public void saveProfileImage(Bitmap image) {
    ByteArrayOutputStream stream = new ByteArrayOutputStream();
    image.compress(Bitmap.CompressFormat.JPEG, 50, stream);
    byte[] byteArray = stream.toByteArray();
    String imgid = this.getUid() + "_profile_image.jpeg";
    String fileNameForImage = this.getUid() + "_profile_image.jpeg";
    this.image = new ParseFile(fileNameForImage, byteArray);
}

public List<String> getFriendUids() {
    return this.friendUids;
}

public void addFriend(String uid) {
    if (this.friendUids != null) {
        friendUids.add(uid);
    } else {
        friendUids = new ArrayList<String>();
        friendUids.add(uid);
    }
}

public void setFriends(Object friends) {
    ArrayList<String> f = (ArrayList<String>) friends;
    this.friendUids = f;
}

}

Gson object

private Gson gson = new GsonBuilder().
        setExclusionStrategies(new ParseExclusion()).
        create();

Exclusion Class

private class ParseExclusion implements ExclusionStrategy {

    public boolean shouldSkipClass(Class<?> arg0) {
        return false;
    }

    public boolean shouldSkipField(FieldAttributes f) {
        return (f.getDeclaredClass() == Lock.class);
    }

}

Finally:

Type type = new TypeToken<List<Profile>>() {}.getType();
List<Profile>) friendProfiles = new ArrayList<Profile>();
friendProfiles = gson.fromJson(json,type);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM