In two situations, only one of them gives me this unique error. I pretty much followed the Android Tutorial character for character, and for some reason it only works in certain areas.
Intent shareIntent = new Intent();
shareIntent.setAction(Intent.ACTION_SEND);
shareIntent.putExtra(Intent.EXTRA_TEXT, getSharedNutritionText(mMultiSelector.getSelectedPositions()));
shareIntent.setType("text/plain");
startActivity(shareIntent);
Gives the following error:
12-13 02:10:07.838 8261-8261/com.example E/AndroidRuntime﹕ FATAL EXCEPTION: main
Process: com.skylan.homeworkpro, PID: 8261
java.lang.RuntimeException: Parcel: unable to marshal value com.example.NutritionInfo#2d54b4d1
at android.os.Parcel.writeValue(Parcel.java:1343)
at android.os.Parcel.writeList(Parcel.java:717)
at android.os.Parcel.writeValue(Parcel.java:1290)
at android.os.Parcel.writeArrayMapInternal(Parcel.java:644)
at android.os.BaseBundle.writeToParcelInner(BaseBundle.java:1313)
at android.os.Bundle.writeToParcel(Bundle.java:1034)
at android.os.Parcel.writeBundle(Parcel.java:669)
at android.app.FragmentState.writeToParcel(Fragment.java:138)
at android.os.Parcel.writeTypedArray(Parcel.java:1197)
at android.app.FragmentManagerState.writeToParcel(FragmentManager.java:376)
at android.os.Parcel.writeParcelable(Parcel.java:1363)
at android.os.Parcel.writeValue(Parcel.java:1268)
at android.os.Parcel.writeArrayMapInternal(Parcel.java:644)
at android.os.BaseBundle.writeToParcelInner(BaseBundle.java:1313)
at android.os.Bundle.writeToParcel(Bundle.java:1034)
at android.os.Parcel.writeBundle(Parcel.java:669)
at android.app.ActivityManagerProxy.activityStopped(ActivityManagerNative.java:2925)
at android.app.ActivityThread$StopInfo.run(ActivityThread.java:3299)
at android.os.Handler.handleCallback(Handler.java:739)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:135)
at android.app.ActivityThread.main(ActivityThread.java:5257)
at java.lang.reflect.Method.invoke(Native Method)
at java.lang.reflect.Method.invoke(Method.java:372)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:903)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:698)
Whereas in another one of my (almost) identical fragments, I have the following code:
Intent shareIntent = new Intent();
shareIntent.setAction(Intent.ACTION_SEND);
shareIntent.putExtra(Intent.EXTRA_TEXT, getSharedLabelText(mMultiSelector.getSelectedPositions()));
shareIntent.setType("text/plain");
startActivity(shareIntent);
Here's my NutritionInfo Class
public class NutritionInfo extends SugarRecord<NutritionInfo> {
public String nutritionName;
public String nutritionHeaderTitle;
public int nutritionCalories;
public boolean nutritionArchived;
public boolean nutritionChecked;
public NutritionInfo () {}
public NutritionInfo (String name, String headerTitle, int cal, boolean isArchived, boolean isChecked) {
nutritionName = name;
nutritionHeaderTitle = headerTitle;
nutritionCalories = cal;
nutritionArchived = isArchived;
nutritionChecked = isChecked;
}
}
Here's my Nutrition getter:
public class NutritionDisplayFragment extends BaseFragment implements ActionMode.Callback {
private String getSharedNutritionText(List<Integer> positions) {
String shareText;
if (positions.equals(null)) {
shareText = "";
} else {
shareText = "Food and carbs:\n";
for (int go = 0; go < positions.size(); go++) {
shareText = shareText +
nutritionList.get(go).nutritionTitle + " - \t" +
nutritionList.get(go).nutritionCarbs + "\n";
}
}
return shareText;
} ( . . . )
}
vs my Label getter:
public class LabelManagerFragment extends BaseFragment implements ActionMode.Callback {
private String getSharedLabelText(List<Integer> positions) {
String shareText = "";
if (positions.equals(null)) {
shareText = "";
} else {
for (int go = 0; go < positions.size(); go++) {
shareText = shareText +
labelList.get(go).labelName + "\t" +
labelList.get(go).labelYear + "\n";
}
}
return shareText;
} ( . . . )
}
And this gives no runtime error. Both get() methods return a simple string. Why does the first error complain about "com.package.NutritionInfo#84055e1", which isn't even the type of data being shared, as un-marshal-able and the other shares the string without a hitch?
For further clarity: I tried hardcoding "hello world" in for where the method is (the method that usually returns a simple String), and had the same issue.
Implement the Parcelable interface like follows:
public class NutritionInfo extends SugarRecord<NutritionInfo> implements Parcelable {
public String nutritionName;
public String nutritionHeaderTitle;
public int nutritionCalories;
public boolean nutritionArchived;
public boolean nutritionChecked;
public NutritionInfo() {
}
public NutritionInfo(String name, String headerTitle, int cal, boolean isArchived, boolean isChecked) {
nutritionName = name;
nutritionHeaderTitle = headerTitle;
nutritionCalories = cal;
nutritionArchived = isArchived;
nutritionChecked = isChecked;
}
protected NutritionInfo(Parcel in) {
nutritionName = (String) in.readValue(null);
nutritionHeaderTitle = (String) in.readValue(null);
nutritionCalories = in.readInt();
nutritionArchived = in.readByte() != 0;
nutritionChecked = in.readByte() != 0;
}
public static final Creator<NutritionInfo> CREATOR = new Creator<NutritionInfo>() {
#Override
public NutritionInfo createFromParcel(Parcel in) {
return new NutritionInfo(in);
}
#Override
public NutritionInfo[] newArray(int size) {
return new NutritionInfo[size];
}
};
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeValue(nutritionName);
dest.writeValue(nutritionHeaderTitle);
dest.writeInt(nutritionCalories);
dest.writeByte((byte) (nutritionArchived ? 1 : 0));
dest.writeByte((byte) (nutritionChecked ? 1 : 0));
}
}
Do the same for your sugar record. And if the super class implements Parcelable you do not need to declare again in NutritionInfo, only implement the stuff.
Related
This question already has answers here:
java.lang.RuntimeException: Parcel android.os.Parcel: Unmarshalling unknown type code
(2 answers)
Closed 5 years ago.
I'm trying to pass an int value from activity A to activity B.
Activity A:
Intent intent = new Intent(ActivityA.this, ActivityB.class);
intent.putExtra("list_size", list.size());
for (int i = 0; i < list.size(); i++) {
intent.putExtra("account" + i, accountList.get(i));
}
startActivityForResult(intent, 2);
Activity B:
private void init() {
Intent intent = getIntent();
int listSize = intent.getIntExtra("list_size", 0); // Error thrown here
for (int i = 0; i < listSize; i++) {
newList.add((Account) intent.getParcelableExtra("account" + i));
}
}
Account (this is the list item):
public class Account implements Parcelable {
private Long accountId;
private String accountName;
private Set<String> followedAccountsIds = new HashSet<>();
private Set<String> followersAccountsIds = new HashSet<>();
private Set<Integer> likedTagsIds = new HashSet<>();
private Set<Post> postsByAccount = new HashSet<>();
public Account() {
}
public Account(Long accountId, String accountName, Set<String> followedAccountsIds,
Set<String> followersAccountsIds, Set<Integer> likedTagsIds, Set<Post> postsByAccount) {
this.accountId = accountId;
this.accountName = accountName;
this.followedAccountsIds = followedAccountsIds;
this.followersAccountsIds = followersAccountsIds;
this.likedTagsIds = likedTagsIds;
this.postsByAccount = postsByAccount;
}
// Omitted getters and setters for brevity
protected Account(Parcel in) {
if (in.readByte() == 0) {
accountId = null;
} else {
accountId = in.readLong();
}
accountName = in.readString();
}
public static final Creator<Account> CREATOR = new Creator<Account>() {
#Override
public Account createFromParcel(Parcel in) {
return new Account(in);
}
#Override
public Account[] newArray(int size) {
return new Account[size];
}
};
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel parcel, int i) {
parcel.writeLong(accountId);
parcel.writeString(accountName);
}
}
Error that is thrown:
Caused by: java.lang.RuntimeException: Parcel android.os.Parcel#8b4252e: Unmarshalling unknown type code 6357091 at offset 140
at android.os.Parcel.readValue(Parcel.java:2453)
at android.os.Parcel.readArrayMapInternal(Parcel.java:2727)
at android.os.BaseBundle.unparcel(BaseBundle.java:269)
at android.os.BaseBundle.getInt(BaseBundle.java:867)
at android.content.Intent.getIntExtra(Intent.java:6637)
at com.david.songshareandroid.activities.ActivityB.init(ActivityB.java:33)
at com.david.songshareandroid.activities.ActivityB.onCreate(ActivityB.java:28)
at android.app.Activity.performCreate(Activity.java:6912)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1126)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2877)
In ActivityB when intent.getIntExtra("list_size", 0) is invoked, the error above occurs. If I remove the for loop in ActivityA, that is the objects are not getting passed to ActivityB, then the error does not occur anymore.
You are putting something but getting something else.
You are putting an Int
intent.putExtra("list_size", list.size());
Now on Target Activity do this:
Intent intent = getIntent();
int number = intent.getIntExtra("list_size",0);
and your SomeObject class must implement Parceable interface
First you taking Bundle Extras using intent.getExtras() which returns you bundle object which contains key-value pairs, and then you trying to find pair with key list_size which is not in there. So you should use intent.getIntExtra() instead.
UPD
I don't think that your parcel implementation should work because of Sets. Use Set.toArray() method, then in Parcel.writeTypedArray write it to the parcel.
I'm using the library Hawk to persist data. I'm facing a problem with the data retrieved.
When I do the next code to check the value of an object stored, but sais it can't cast the value.
Caused by: java.lang.ClassCastException: java.lang.String cannot be cast to com.test.app.model.AtvLanguage
The code:
if(Hawk.contains("ATV_LANGUAGE")){
atvLanguage = (AtvLanguage)Hawk.get("ATV_LANGUAGE"); // Fail point
}else{
atvLanguage.setLanguageCodes("", getString(R.string.str_en));
}
The put method:
public void updateSelectedLanguage(String iso6391, String iso6392){
AtvLanguage atvLanguageTmp = new AtvLanguage(iso6391, iso6392);
Hawk.put("ATV_LANGUAGE", atvLanguageTmp);
}
AtvLanguage pojo:
public class AtvLanguage implements Serializable {
#SerializedName("iso6391")
#Expose
private String iso6391;
#SerializedName("iso6392")
#Expose
private String iso6392;
public AtvLanguage() {
}
public AtvLanguage(String iso6391, String iso6392) {
setLanguageCodes(iso6391, iso6392);
}
public String getIso6391() {
return iso6391;
}
public void setIso6391(String iso6391) {
this.iso6391 = iso6391;
}
public String getIso6392() {
return iso6392;
}
public void setIso6392(String iso6392) {
this.iso6392 = iso6392;
}
// ISO 639-2 is the language code with 3 chars, like ENG or SPA.
public void setLanguageCodes(String iso6391, String iso6392){
if(iso6391.isEmpty() && !iso6392.isEmpty()){
if(iso6392.equalsIgnoreCase("ENG"))
setIso6391("en");
else if(iso6392.equalsIgnoreCase("ESP"))
setIso6391("es");
setIso6392(iso6392);
}else if(!iso6391.isEmpty() && iso6392.isEmpty()){
if(iso6391.equalsIgnoreCase("en"))
setIso6392("ENG");
else if(iso6391.equalsIgnoreCase("es"))
setIso6392("ESP");
setIso6391(iso6391);
}
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
AtvLanguage that = (AtvLanguage) o;
return new EqualsBuilder()
.append(getIso6391(), that.getIso6391())
.append(getIso6392(), that.getIso6392())
.isEquals();
}
#Override
public int hashCode() {
return new HashCodeBuilder(17, 37)
.append(getIso6391())
.append(getIso6392())
.toHashCode();
}
#Override
public String toString() {
return "AtvLanguage{" +
"iso6391='" + iso6391 + '\'' +
", iso6392='" + iso6392 + '\'' +
'}';
}
}
I seem to be getting a strange error in my app (see GitHub), which occurs when I pass objects to different activities that implement Parcelable.
I have checked other questions and answers here on Stack Overflow, but I was unable to find a solution. I've tried the answer here, for example - here it is for reference:
-keepclassmembers class * implements android.os.Parcelable {
static ** CREATOR;
}
I've also made sure that the method calls in writeToParcel are in order. Most other questions on Stack Overflow about this issue don't have answers.
Moreover, the reason I am asking a new question is because I think my problem is caused because of how I have used interfaces in my app (I will expand on this point later on). Other questions on Stack Overflow would not suit my particular scenario.
In the following, I have provided links to the code via GitHub, so that you can explore more of the code if required.
When I click on a button to launch a new activity (passing an object that implements Parcelable), there is a crash:
Process: com.satsuware.flashcards, PID: 4664
java.lang.RuntimeException: Unable to start activity ComponentInfo{com.satsuware.flashcards/com.satsumasoftware.flashcards.ui.FlashCardActivity}: java.lang.RuntimeException: Parcel android.os.Parcel#d2219e4: Unmarshalling unknown type code 6815860 at offset 200
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2416)
...
Caused by: java.lang.RuntimeException: Parcel android.os.Parcel#d2219e4: Unmarshalling unknown type code 6815860 at offset 200
at android.os.Parcel.readValue(Parcel.java:2319)
at android.os.Parcel.readListInternal(Parcel.java:2633)
at android.os.Parcel.readArrayList(Parcel.java:1914)
at android.os.Parcel.readValue(Parcel.java:2264)
at android.os.Parcel.readArrayMapInternal(Parcel.java:2592)
at android.os.BaseBundle.unparcel(BaseBundle.java:221)
at android.os.Bundle.getParcelable(Bundle.java:786)
at android.content.Intent.getParcelableExtra(Intent.java:5377)
at com.satsumasoftware.flashcards.ui.FlashCardActivity.onCreate(FlashCardActivity.java:71)
at android.app.Activity.performCreate(Activity.java:6237)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1107)
...
I call the aforementioned activity like so (also see GitHub):
Intent intent = new Intent(TopicDetailActivity.this, FlashCardActivity.class);
intent.putExtra(FlashCardActivity.EXTRA_TOPIC, mTopic);
intent.putExtra(FlashCardActivity.EXTRA_NUM_CARDS, mSelectedNumCards);
intent.putExtra(FlashCardActivity.EXTRA_CARD_LIST, mFilteredCards);
startActivity(intent);
The main part to consider is when I pass mTopic. This is a Topic interface that I created.
However, the Topic interface extends Parcelable and so the objects that implement Topic also include the constructor, CREATOR field, and the methods that a class implementing Parcelable would normally have to have.
You can view the relevant classes via the GitHub links, but I will provide the relevant parts of these classes below. Here is the Topic interface:
public interface Topic extends Parcelable {
int getId();
String getIdentifier();
String getName();
Course getCourse();
ArrayList<FlashCard> getFlashCards(Context context);
class FlashCardsRetriever {
public static ArrayList<FlashCard> filterStandardCards(ArrayList<FlashCard> flashCards, #StandardFlashCard.ContentType int contentType) {
ArrayList<FlashCard> filteredCards = new ArrayList<>();
for (FlashCard flashCard : flashCards) {
boolean isPaper2 = ((StandardFlashCard) flashCard).isPaper2();
boolean condition;
switch (contentType) {
case StandardFlashCard.PAPER_1:
condition = !isPaper2;
break;
case StandardFlashCard.PAPER_2:
condition = isPaper2;
break;
case StandardFlashCard.ALL:
condition = true;
break;
default:
throw new IllegalArgumentException("content type '" + contentType + "' is invalid");
}
if (condition) filteredCards.add(flashCard);
}
return filteredCards;
}
...
}
}
A class (object) that implements Topic:
public class CourseTopic implements Topic {
...
public CourseTopic(int id, String identifier, String name, Course course) {
...
}
#Override
public int getId() {
return mId;
}
#Override
public String getIdentifier() {
return mIdentifier;
}
...
protected CourseTopic(Parcel in) {
mId = in.readInt();
mIdentifier = in.readString();
mName = in.readString();
mCourse = in.readParcelable(Course.class.getClassLoader());
}
public static final Parcelable.Creator<CourseTopic> CREATOR = new Parcelable.Creator<CourseTopic>() {
#Override
public CourseTopic createFromParcel(Parcel in) {
return new CourseTopic(in);
}
#Override
public CourseTopic[] newArray(int size) {
return new CourseTopic[size];
}
};
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(mId);
dest.writeString(mIdentifier);
dest.writeString(mName);
dest.writeParcelable(mCourse, flags);
}
}
In one of the last lines of the code above, you can see I pass mCourse, which is a Course object I created. Here it is:
public class Course implements Parcelable {
...
public Course(String subject, String examBoard, #FlashCard.CourseType String courseType,
String revisionGuide) {
...
}
public String getSubjectIdentifier() {
return mSubjectIdentifier;
}
public String getExamBoardIdentifier() {
return mBoardIdentifier;
}
public ArrayList<Topic> getTopics(Context context) {
ArrayList<Topic> topics = new ArrayList<>();
String filename = mSubjectIdentifier + "_" + mBoardIdentifier + "_topics.csv";
CsvParser parser = CsvUtils.getMyParser();
try {
List<String[]> allRows = parser.parseAll(context.getAssets().open(filename));
for (String[] line : allRows) {
int id = Integer.parseInt(line[0]);
topics.add(new CourseTopic(id, line[1], line[2], this));
}
} catch (IOException e) {
e.printStackTrace();
}
return topics;
}
...
protected Course(Parcel in) {
mSubjectIdentifier = in.readString();
mBoardIdentifier = in.readString();
mCourseType = in.readString();
mRevisionGuide = in.readString();
}
public static final Creator<Course> CREATOR = new Creator<Course>() {
#Override
public Course createFromParcel(Parcel in) {
return new Course(in);
}
#Override
public Course[] newArray(int size) {
return new Course[size];
}
};
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(mSubjectIdentifier);
dest.writeString(mBoardIdentifier);
dest.writeString(mCourseType);
dest.writeString(mRevisionGuide);
}
}
I suspect something here may be causing the problem, and is the reason my scenario is different from those in other questions.
To be honest, I'm not exactly sure what may be causing the error, so explanations and guidance in answers would be much appreciated.
Edit:
After David Wasser's suggestions, I have updated parts of my code like so:
FlashCardActivity.java - onCreate(...):
Bundle extras = getIntent().getExtras();
extras.setClassLoader(Topic.class.getClassLoader());
mTopic = extras.getParcelable(EXTRA_TOPIC);
Course.java - writeToParcel(...):
dest.writeString(mSubjectIdentifier);
dest.writeString(mBoardIdentifier);
dest.writeString(mCourseType);
dest.writeInt(mRevisionGuide == null ? 0 : 1);
if (mRevisionGuide != null) dest.writeString(mRevisionGuide);
Course.java - Course(Parcel in):
mSubjectIdentifier = in.readString();
mBoardIdentifier = in.readString();
mCourseType = in.readString();
if (in.readInt() != 0) mRevisionGuide = in.readString();
I've added log messages using Log.d(...) to see if any variables are null when being passed in writeToParcel(...) and used David Wasser's method to properly handle this.
I still get the same error message, however.
Your problem is in LanguagesFlashCard. Here are your parcel/unparcel methods:
protected LanguagesFlashCard(Parcel in) {
mId = in.readInt();
mEnglish = in.readString();
mAnswerPrefix = in.readString();
mAnswer = in.readString();
mTier = in.readInt();
mTopic = in.readParcelable(Topic.class.getClassLoader());
}
As you can see, they don't match. The second item you write to the Parcel is an int, the second item you read from the Parcel is a String.
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(mId);
dest.writeInt(mTier);
dest.writeString(mEnglish);
dest.writeString(mAnswerPrefix);
dest.writeString(mAnswer);
dest.writeParcelable(mTopic, flags);
}
Kotlin code for sub data class like ImagesModel also parcelable used
data class MyPostModel(
#SerializedName("post_id") val post_id: String? = "",
#SerializedName("images") val images: ArrayList<ImagesModel>? = null
): Parcelable {
constructor(parcel: Parcel) : this(
parcel.writeString(post_id)
parcel.createTypedArrayList(ImagesModel.CREATOR)
)
override fun writeToParcel(parcel: Parcel, flags: Int) {
parcel.writeString(post_id)
parcel.writeTypedList(images)
}
}
I'm attempting to update an existing RealmObject (IncidentCard) which include a RealmList of type IncidentPhoto. The object is updated without any problems as long as I don't attempt to update the RealmList, when I include the list I get the following error message:
E/AndroidRuntime: FATAL EXCEPTION: main
E/AndroidRuntime: Process: com.trollvik.android.incidents247, PID: 31923
E/AndroidRuntime: java.lang.IllegalArgumentException: Each element of 'value' must be a valid managed object.
E/AndroidRuntime: at io.realm.IncidentCardRealmProxy.setPhotos(IncidentCardRealmProxy.java:218)
E/AndroidRuntime: at com.trollvik.android.incidents247.activities.EditCardActivity.saveIncidentCard(EditCardActivity.java:155)
E/AndroidRuntime: at com.trollvik.android.incidents247.activities.EditCardActivity$1.onClick(EditCardActivity.java:95)
E/AndroidRuntime: at android.view.View.performClick(View.java:5197)
E/AndroidRuntime: at android.view.View$PerformClick.run(View.java:20926)
E/AndroidRuntime: at android.os.Handler.handleCallback(Handler.java:739)
E/AndroidRuntime: at android.os.Handler.dispatchMessage(Handler.java:95)
E/AndroidRuntime: at android.os.Looper.loop(Looper.java:145)
E/AndroidRuntime: at android.app.ActivityThread.main(ActivityThread.java:5944)
E/AndroidRuntime: at java.lang.reflect.Method.invoke(Native Method)
E/AndroidRuntime: at java.lang.reflect.Method.invoke(Method.java:372)
E/AndroidRuntime: at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1388)
E/AndroidRuntime: at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1183)
This is the IncidentCard class:
public class IncidentCard extends RealmObject {
#PrimaryKey
private long id;
private String timestamp;
private String type;
private RealmList<IncidentPhoto> photos;
public IncidentCard() {
}
public IncidentCard(long id, String timestamp, String type){
this.id = id;
this.timestamp = timestamp;
this.type = type;
}
public IncidentCard(long id, String timestamp, String type, RealmList<IncidentPhoto> photos){
this.id = id;
this.timestamp = timestamp;
this.type = type;
this.photos = photos;
}
public long getId() {
return this.id;
}
public void setId(long id) {
this.id = id;
}
public String getTimestamp(){
return this.timestamp;
}
public void setTimestamp(String timestamp) {
this.timestamp = timestamp;
}
public String getType(){
return this.type;
}
public void setType(String type){
this.type = type;
}
public RealmList<IncidentPhoto> getPhotos() {
return this.photos;
}
public void setPhotos(RealmList<IncidentPhoto> photos) {
this.photos = photos;
}
}
This is the IncidentPhoto class:
public class IncidentPhoto extends RealmObject {
private String photoPath;
public IncidentPhoto() {
}
public IncidentPhoto(String photoPath) {
this.photoPath = photoPath;
}
public String getPhotoPath(){
return this.photoPath;
}
public void setPhotoPath(String photoPath){
this.photoPath = photoPath;
}
}
To query the Realm DB I created this helper class:
public class IncidentDbHelper {
private Realm realm;
public IncidentDbHelper(Context context) {
realm = Realm.getInstance(context);
}
public void setObject(IncidentCard incidentCard) {
realm.beginTransaction();
IncidentCard incident = realm.copyToRealmOrUpdate(incidentCard);
realm.commitTransaction();
}
public IncidentCard getObject(Long id) {
return realm.where(IncidentCard.class).equalTo("id", id).findFirst();
}
public void close(){
if (realm != null) {
realm.close();
}
}
}
When I add a new incident card I call this activity:
public class NewCardActivity extends AppCompatActivity {
private static final int REQUEST_IMAGE_CAPTURE = 1;
private static final String INSTANCE_STATE = "currentPhotoPath";
private static final String INSTANCE_STATE_LIST = "currentPhotoList";
private Context mContext;
private IncidentCard mIncidentCard;
private IncidentDbHelper mDbHelper;
private IncidentCardId mIncidentId;
private IncidentCardTimestamp mIncidentTimestamp;
private RealmConverter mRealmConverter;
private Resources mRes;
private PhotoPath mPath;
private String mCurrentPhotoPath;
private ArrayList<String> mCurrentPhotoList;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mContext = this;
mDbHelper = new IncidentDbHelper(mContext);
mIncidentCard = new IncidentCard();
mRealmConverter = new RealmConverter();
mCurrentPhotoList = new ArrayList<String>();
mIncidentId = new IncidentCardId();
mIncidentTimestamp = new IncidentCardTimestamp();
mRes = getResources();
mPath = new PhotoPath();
// If savedInstanceState is empty, ignore this code.
if(savedInstanceState != null){
mCurrentPhotoPath = savedInstanceState.getString(INSTANCE_STATE);
mCurrentPhotoList = savedInstanceState.getStringArrayList(INSTANCE_STATE_LIST);
}
}
protected void saveIncidentCard(){
Realm realm = Realm.getInstance(this);
Spinner spinner = (Spinner) findViewById(R.id.content_new_card_type);
String incidentType = spinner.getSelectedItem().toString();
realm.beginTransaction();
mIncidentCard.setId(mIncidentId.getNewId());
mIncidentCard.setTimestamp(mIncidentTimestamp.getNewTimestamp());
mIncidentCard.setType(incidentType);
mIncidentCard.setPhotos(mRealmConverter.toRealmList(mCurrentPhotoList));
realm.commitTransaction();
mDbHelper.setObject(mIncidentCard);
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_new_card, menu);
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.menu_new_photo:
dispatchTakePictureIntent();
default:
// If we got here, the user's action was not recognized.
// Invoke the superclass to handle it.
return super.onOptionsItemSelected(item);
}
}
private void dispatchTakePictureIntent() {
Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
File imageFile = null;
// Ensure that there's a camera activity to handle the intent
if (takePictureIntent.resolveActivity(getPackageManager()) != null) {
// Create the File where the photo should go
try {
imageFile = mPath.createImageFile();
mCurrentPhotoPath = imageFile.getAbsolutePath();
} catch (java.io.IOException e) {
Log.e(TAG, e.toString());
}
// Continue only if the File was successfully created
if (imageFile != null) {
takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT,
Uri.fromFile(imageFile.getAbsoluteFile()));
startActivityForResult(takePictureIntent, REQUEST_IMAGE_CAPTURE);
}
}
}
#Override
public void onSaveInstanceState(Bundle savedInstanceState) {
super.onSaveInstanceState(savedInstanceState);
savedInstanceState.putString(INSTANCE_STATE, mCurrentPhotoPath);
savedInstanceState.putStringArrayList(INSTANCE_STATE_LIST, mCurrentPhotoList);
}
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == REQUEST_IMAGE_CAPTURE && resultCode == RESULT_OK) {
mCurrentPhotoList.add(mCurrentPhotoPath);
}
}
#Override
protected void onDestroy() {
super.onDestroy();
mDbHelper.close();
}
}
While I capture new photos I save the paths of previously captured photos in a ArrayList of type String. Before I set the list of paths to the IncidentCard object I convert the list to a RealmList. This part seems to be working fine.
The problem occurs after I try to save an existing object in EditCardActivity:
public class EditCardActivity extends AppCompatActivity {
private static final String INTENT_EXTRA = "EXTRA_INCIDENT_ID";
private static final int REQUEST_IMAGE_CAPTURE = 2;
private static final String INSTANCE_STATE = "currentPhotoPath";
private static final String INSTANCE_STATE_LIST = "currentPhotoList";
private Context mContext;
private Long mIncidentId;
private IncidentCard mIncidentCard;
private IncidentDbHelper mDbHelper;
private IncidentCardTimestamp mIncidentTimestamp;
private RealmConverter mRealmConverter;
private Resources mRes;
private PhotoPath mPath;
Spinner mSpinnerType;
private String mCurrentPhotoPath;
private ArrayList<String> mCurrentPhotoList;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mRealmConverter = new RealmConverter();
mContext = this;
mDbHelper = new IncidentDbHelper(mContext);
mIncidentCard = new IncidentCard();
mCurrentPhotoList = new ArrayList<String>();
mIncidentTimestamp = new IncidentCardTimestamp();
mRes = getResources();
mPath = new PhotoPath();
// If savedInstanceState is empty, ignore this code.
if(savedInstanceState != null){
mCurrentPhotoPath = savedInstanceState.getString(INSTANCE_STATE);
mCurrentPhotoList = savedInstanceState.getStringArrayList(INSTANCE_STATE_LIST);
}
FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
fab.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
saveIncidentCard();
finish();
}
});
// Get Incident ID passed from Main Activity
Intent intent = getIntent();
mIncidentId = intent.getLongExtra(INTENT_EXTRA, 0);
mIncidentCard = mDbHelper.getObject(mIncidentId);
mTextViewId = (TextView) findViewById(R.id.content_edit_card_id);
mSpinnerType = (Spinner) findViewById(R.id.content_edit_card_type);
ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,
android.R.layout.simple_spinner_dropdown_item, items);
mSpinnerType.setAdapter(adapter);
String compareValue = mIncidentCard.getType();
if (!compareValue.equals(null)) {
int spinnerPosition = adapter.getPosition(compareValue);
mSpinnerType.setSelection(spinnerPosition);
}
mCurrentPhotoList = mRealmConverter.toArrayList(mIncidentCard.getPhotos());
}
protected void saveIncidentCard(){
Realm realm = Realm.getInstance(this);
Spinner spinner = (Spinner) findViewById(R.id.content_edit_card_type);
String incidentType = spinner.getSelectedItem().toString();
realm.beginTransaction();
mIncidentCard.setTimestamp(mIncidentTimestamp.getNewTimestamp());
mIncidentCard.setType(incidentType);
mIncidentCard.setPhotos(mRealmConverter.toRealmList(mCurrentPhotoList));
realm.commitTransaction();
mDbHelper.setObject(mIncidentCard);
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_edit_card, menu);
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.menu_edit_photo:
dispatchTakePictureIntent();
default:
// If we got here, the user's action was not recognized.
// Invoke the superclass to handle it.
return super.onOptionsItemSelected(item);
}
}
private void dispatchTakePictureIntent() {
Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
File imageFile = null;
// Ensure that there's a camera activity to handle the intent
if (takePictureIntent.resolveActivity(getPackageManager()) != null) {
// Create the File where the photo should go
try {
imageFile = mPath.createImageFile();
mCurrentPhotoPath = imageFile.getAbsolutePath();
} catch (java.io.IOException e) {
Log.e(TAG, e.toString());
}
// Continue only if the File was successfully created
if (imageFile != null) {
takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT,
Uri.fromFile(imageFile.getAbsoluteFile()));
startActivityForResult(takePictureIntent, REQUEST_IMAGE_CAPTURE);
}
}
}
#Override
public void onSaveInstanceState(Bundle savedInstanceState) {
super.onSaveInstanceState(savedInstanceState);
savedInstanceState.putString(INSTANCE_STATE, mCurrentPhotoPath);
savedInstanceState.putStringArrayList(INSTANCE_STATE_LIST, mCurrentPhotoList);
}
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == REQUEST_IMAGE_CAPTURE && resultCode == RESULT_OK) {
mCurrentPhotoList.add(mCurrentPhotoPath);
}
}
#Override
protected void onDestroy() {
super.onDestroy();
mDbHelper.close();
}
}
So, if I comment out mIncidentCard.setPhotos() everything seems to work fine, but when I try to set the photos to the IncidentCard object the IllegalArgumentException is triggered.
This is the method I created for converting ArrayLists to RealmLists:
public RealmList<IncidentPhoto> toRealmList(ArrayList<String> arrayList){
mRealmList = new RealmList<IncidentPhoto>();
for (int i = 0; i < arrayList.size(); i++){
IncidentPhoto incidentPhoto = new IncidentPhoto();
incidentPhoto.setPhotoPath(arrayList.get(i));
mRealmList.add(incidentPhoto);
}
return mRealmList;
}
I've been struggling with this for a while now and I don't understand what I'm doing wrong, so any help will be greatly appreciated.
Realm Exception 'value' is not a valid managed object
Realm Java Doc
The problem is, when calling setters to set a RealmList, every element in the list has to be managed by Realm already.
Similar question here Adding standalone-objects to a RealmList
You can modify toRealmList as below:
public RealmList<IncidentPhoto> toRealmList(Realm realm, ArrayList<String> arrayList) {
mRealmList = new RealmList<IncidentPhoto>();
for (int i = 0; i < arrayList.size(); i++){
// Create a IncidentPhoto object which is managed by Realm.
IncidentPhoto incidentPhoto = realm.createObject(IncidentPhoto.class);
incidentPhoto.setPhotoPath(arrayList.get(i));
mRealmList.add(incidentPhoto);
}
return mRealmList;
}
or
public RealmList<IncidentPhoto> toRealmList(Realm realm, ArrayList<String> arrayList) {
mRealmList = new RealmList<IncidentPhoto>();
for (int i = 0; i < arrayList.size(); i++){
IncidentPhoto incidentPhoto = new IncidentPhoto();
incidentPhoto.setPhotoPath(arrayList.get(i));
// Copy the standalone object to Realm, and get the returned object which is managed by Realm.
incidentPhoto = realm.copyToRealm(incidentPhoto);
mRealmList.add(incidentPhoto);
}
return mRealmList;
}
I want to pass an array of objects without using the preference
I use Intent!!!
The Object class Bts
public class Bts implements Serializable {
int idbts;
String nombts;
String ipaddress;
String logb;
String pawb;
int xbts;
int ybts;
public Bts(int idbts, String nombts, String ipaddress, String logb,
String pawb, int xbts, int ybts) {
super();
this.idbts = idbts;
this.nombts = nombts;
this.ipaddress = ipaddress;
this.logb = logb;
this.pawb = pawb;
this.xbts = xbts;
this.ybts = ybts;
}
//wthis setter and getter
}
The source activity
Ps:JSONParseBts filled listeBts.
public class ApresConnextionActivity extends Activity {
public Bts[] listeBts;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
super.setContentView(R.layout.apresconnect);
final Button btsButton = (Button) findViewById(R.id.btbts);
//boutonbts
btsButton.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
Log.w("myApp",""+listeBts.length);
Intent i = new Intent(ApresConnextionActivity.this, BtsBoutonActivity.class);
i.putExtra("tabbts",listeBts);
startActivity(i);
}
});
new JSONParseBts().execute();
}
public class JSONParseBts extends AsyncTask<String, String, JSONObject> { ... }
}
And distination Activity:
public class BtsBoutonActivity extends Activity {
cellule[] listecellule;
Bts[] listeBts;
int i,xx=0;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
super.setContentView(R.layout.btsbouton);
Bundle b = getIntent().getExtras();
if (b != null)
{
Bts[] listeBts = (Bts[])b.getSerializable("tabbts") ;
Log.w("myApp",""+listeBts.length);
}else{
Log.w("myApp","you have problem");
}
/* Intent intent = getIntent();
listeBts = (Bts[])intent.getSerializableExtra("tabbts");*/
final Button[] b = new Button[listeBts.length];
LinearLayout ll3 = (LinearLayout)findViewById(R.id.linearLayout2); // Btn
for(i = 0; i < listeBts.length; i++){
//Log.w("myApp",listeBts[i].toString());
b[i] = new Button(BtsBoutonActivity.this);
b[i].setText(listeBts[i].getNombts());
xx =listeBts[i].getIdbts();
Log.w("myApp",""+xx);
b[i].setId(xx);
ll3.addView(b[i]);
final Button btbts = (Button) findViewById(xx);
btbts.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
Toast saved_message=Toast.makeText(BtsBoutonActivity.this,btbts.getText()+" "+btbts.getId(),1);
saved_message.show();
}});
}
}
}
The error is:
05-10 16:29:25.096: E/AndroidRuntime(819): java.lang.RuntimeException: Unable to start activity ComponentInfo{pfe.essat.com/pfe.essat.com.BtsBoutonActivity}: java.lang.ClassCastException: java.lang.Object[] cannot be cast to pfe.essat.objet.com.Bts[]
You can use a wrapper class that contains the Serializable array, I just tested this and it works.
First, create your simple wrapper class:
import java.io.Serializable;
public class BtsWrapper implements Serializable{
Bts[] array;
public BtsWrapper(Bts[] a){
array = a;
}
}
Then pass the Serializable wrapper class in the Intent:
btsButton.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
Log.w("myApp",""+listeBts.length);
Intent i = new Intent(ApresConnextionActivity.this, BtsBoutonActivity.class);
BtsWrapper bWrapper = new BtsWrapper(listeBts); //added
i.putExtra("tabbts",bWrapper); //modified
startActivity(i);
}
});
Then, get the Serializable wrapper from the extras, and just extract the array:
Bundle b = getIntent().getExtras();
if (b != null)
{
//Bts[] listeBts = (Bts[])b.getSerializable("tabbts") ;
BtsWrapper bWrapper = (BtsWrapper) b.getSerializable("tabbts"); //added
Bts[] listeBts = bWrapper.array; //added
Log.w("myApp",""+listeBts.length);
}else{
Log.w("myApp","you have problem");
}
I believe there is no such putExtra API existent that accepts Array of some Serializable objects. See http://developer.android.com/reference/android/content/Intent.html#putCharSequenceArrayListExtra(java.lang.String, java.util.ArrayList) onwards. There is also no getter available for extracting Array of some Serializable objects from Intent.
You could perhaps make your Bts class Parcelable and use public Intent putExtra (String name, Parcelable[] value) instead.
Create Constants.java
public class Constants {
public static String BTS_EXTRA_KEY = "bts_extra";
}
Fix your Bts class to be like this
import android.os.Parcel;
import android.os.Parcelable;
public class Bts implements Parcelable {
private String nombts;
private String ipaddress;
private String logb;
private String pawb;
private int idbts;
private int xbts;
private int ybts;
public Bts(Parcel in) {
// Construct from parcel
String[] data = new String[4];
int[] dataInt = new int[3];
in.readStringArray(data);
in.readIntArray(dataInt);
nombts = data[0];
ipaddress = data[1];
logb = data[2];
pawb = data[3];
idbts = dataInt[0];
xbts = dataInt[0];
ybts = dataInt[0];
}
public Bts(int idbts, String nombts, String ipaddress, String logb, String pawb, int xbts, int ybts) {
super();
this.idbts = idbts;
this.nombts = nombts;
this.ipaddress = ipaddress;
this.logb = logb;
this.pawb = pawb;
this.xbts = xbts;
this.ybts = ybts;
}
public String getNombts() {
return nombts;
}
public void setNombts(String nombts) {
this.nombts = nombts;
}
public String getIpaddress() {
return ipaddress;
}
public void setIpaddress(String ipaddress) {
this.ipaddress = ipaddress;
}
public String getLogb() {
return logb;
}
public void setLogb(String logb) {
this.logb = logb;
}
public String getPawb() {
return pawb;
}
public void setPawb(String pawb) {
this.pawb = pawb;
}
public int getIdbts() {
return idbts;
}
public void setIdbts(int idbts) {
this.idbts = idbts;
}
public int getXbts() {
return xbts;
}
public void setXbts(int xbts) {
this.xbts = xbts;
}
public int getYbts() {
return ybts;
}
public void setYbts(int ybts) {
this.ybts = ybts;
}
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel parcel, int i) {
parcel.writeStringArray(new String[]{nombts, ipaddress, logb, pawb});
parcel.writeIntArray(new int[]{idbts, xbts, ybts});
}
public static final Parcelable.Creator CREATOR = new Parcelable.Creator() {
public Bts createFromParcel(Parcel in) {
return new Bts(in);
}
public Bts[] newArray(int size) {
return new Bts[size];
}
};
}
Now to pass this data to another activity, you must have the data in an ArrayList
public void onBtnClick(View v){
Intent i = new Intent(MainActivity.this, SecondActivity.class);
ArrayList<Bts> listOfBts = new ArrayList<Bts>();
listOfBts.add(new Bts(123, "323", "23423", "3423", "frlaldf", 45, 324));
listOfBts.add(new Bts(124, "323", "23423", "3423", "frlaldf", 45, 324));
listOfBts.add(new Bts(125, "323", "23423", "3423", "frlaldf", 45, 324));
listOfBts.add(new Bts(126, "323", "23423", "3423", "frlaldf", 45, 324));
listOfBts.add(new Bts(127, "323", "23423", "3423", "frlaldf", 45, 324));
listOfBts.add(new Bts(128, "323", "23423", "3423", "frlaldf", 45, 324));
i.putParcelableArrayListExtra(Constants.BTS_EXTRA_KEY, listOfBts);
startActivity(i);
}
And in the other activity
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
printBts();
}
private void printBts(){
Bundle extras = getIntent().getExtras();
ArrayList<Bts> btsList = extras.getParcelableArrayList(Constants.BTS_EXTRA_KEY);
for(Bts b : btsList){
Log.d("SecondActivity", "" + b.getIdbts());
}
}
Result:
05-10 21:14:51.414 19703-19703/si.kseneman.btstest D/SecondActivity﹕ 123
05-10 21:14:51.414 19703-19703/si.kseneman.btstest D/SecondActivity﹕ 124
05-10 21:14:51.414 19703-19703/si.kseneman.btstest D/SecondActivity﹕ 125
05-10 21:14:51.414 19703-19703/si.kseneman.btstest D/SecondActivity﹕ 126
05-10 21:14:51.414 19703-19703/si.kseneman.btstest D/SecondActivity﹕ 127
05-10 21:14:51.414 19703-19703/si.kseneman.btstest D/SecondActivity﹕ 128