I'm currently writing an app that pulls an array of longs from a server via json, and then passes that list from one activity into another. The basic skeleton looks like this:
public void onResponse(Map result)
{
ArrayList handles= (ArrayList)result.get("fileHandles");
Intent intent = new Intent(FirstActivity.this, SecondActivity.class);
intent.putExtra("handles", handles);
}
So the first problem becomes evident, the only methods for putExtra are putIntegerArrayListExtra, putStringArrayListExtra, putCharSequenceArrayListExtra, and putParcelableArrayListExtra. Thinking Long would probably be parcelable, I was wrong it doesn't work (even if I use ArrayList<Long>). Next I thought I'd just pass a long [], however I see no straight-forward conversion to go from ArrayList<Long> to long [] that intent.putExtra will accept. This was the solution I finally ended up with:
ArrayList handles= (ArrayList)result.get("fileHandles");
long [] handleArray = new long[handles.size()];
for (int i = 0; i < handles.size(); i++)
{
handleArray[i] = Long.parseLong(handles.get(i).toString());
}
Obviously this seemed a little bit ridiculous to me, but every other conversion I tried seemed to complain for one reason or another. I've thought about re-thinking my serialization to have the problem taken care of before I get to this point, but I find it odd that passing ArrayList<Long> from activity to activity could be so difficult. Is there a more obvious solution I'm missing?
You can use it as a Serializable extra. ArrayList is Serializable and Long extends Number which also implements Serializable:
// Put as Serializable
i.putExtra("handles", handles);
// Unfortunately you'll get an unsafe cast warning here, but it's safe to use
ArrayList<Long> handles = (ArrayList<Long>) i.getSerializableExtra("handles");
Two options: Use Parcelable or Serializable
in:
Intent intent = new Intent(FirstActivity.this, SecondActivity.class);
intent.putExtra("handles", new FileHandles(handles));
out:
FileHandles handles = intent.getParcelableExtra("handles");
object:
import android.os.Parcel;
import android.os.Parcelable;
import java.util.ArrayList;
import java.util.List;
public class FileHandles implements Parcelable {
private final List<Long> fileHandles;
public FileHandles(List<Long> fileHandles) {
this.fileHandles = fileHandles;
}
public FileHandles(Parcel in) {
int size = in.readInt();
long[] parcelFileHandles = new long[size];
in.readLongArray(parcelFileHandles);
this.fileHandles = toObjects(size, parcelFileHandles);
}
private List<Long> toObjects(int size, long[] parcelFileHandles) {
List<Long> primitiveConv = new ArrayList<Long>();
for (int i = 0; i < size; i++) {
primitiveConv.add(parcelFileHandles[i]);
}
return primitiveConv;
}
public List<Long> asList() { // Prefer you didn't use this method & added domain login here, but stackoverflow can only teach so much..
return fileHandles;
}
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(fileHandles.size());
dest.writeLongArray(toPrimitives(fileHandles));
}
private static long[] toPrimitives(List<Long> list) {
return toPrimitives(list.toArray(new Long[list.size()]));
}
public static long[] toPrimitives(Long... objects) {
long[] primitives = new long[objects.length];
for (int i = 0; i < objects.length; i++)
primitives[i] = objects[i];
return primitives;
}
public static final Parcelable.Creator CREATOR = new Parcelable.Creator() {
#Override
public FileHandles createFromParcel(Parcel in) {
return new FileHandles(in);
}
#Override
public FileHandles[] newArray(int size) {
return new FileHandles[size];
}
};
}
Serializable (forced to use ArrayList which implements Serializable (List does not)
in:
Intent intent = new Intent(FirstActivity.this, SecondActivity.class);
intent.putExtra("handles", new ArrayList<Long>());
out:
ArrayList handles = (ArrayList) intent.getSerializableExtra("handles");
Related
I got an error as below:
Error:(133, 15) error: method setMonkeyBuisness in class QuoteBank cannot be applied to given types;
required: ArrayList<QuoteQuestion>
found: ArrayList<Parcelable>
reason: actual argument ArrayList<Parcelable> cannot be converted to
ArrayList<QuoteQuestion> by method invocation conversion
Both QuoteQuestion and QuoteBank implement Parcelable and all their methods. I cannot type cast parcelable either.
Am I using Parcelable array list correctly?
Here is some part of my code for QuoteBank:
public class QuoteBank implements Parcelable{
public static final String ARRAY_LIST_KEY = "arrayListKey";
private ArrayList<QuoteQuestion> monkeyBuisness;
public QuoteBank(){
}
#Override
public void writeToParcel(Parcel dest, int flags) {
Bundle bundle = new Bundle();
bundle.putParcelableArrayList(ARRAY_LIST_KEY, monkeyBuisness);
dest.writeBundle(bundle);
}
public static final Parcelable.Creator<QuoteBank> CREATOR = new Creator<QuoteBank>() {
#Override
public QuoteBank createFromParcel(Parcel source) {
Bundle bundle = source.readBundle();
QuoteBank qb = new QuoteBank();
qb.setMonkeyBuisness(bundle.getParcelableArrayList(ARRAY_LIST_KEY));
return qb;
}
public void setMonkeyBuisness(ArrayList<QuoteQuestion> monkeyBuisness) {
this.monkeyBuisness = monkeyBuisness;
}
Here is QuoteQuestion code:
public class QuoteQuestion implements Parcelable{
public static final String QUOTE_TYPE = "quoteType";
public static final String QUOTE_NUMBER = "quoteNumber";
public static final String QUOTE_ARRAY = "quoteArray";
public static final String SPEAKER_ARRAY = "speakerArray";
public static final String ANSWER_INDEX_ARRAY = "answerIndexArray";
public static final String ANSWER_CHOICE_ARRAY = "answerChoiceArray";
public static final String CONTEXT_KEY = "contextKey";
public static final String CHOSEN_ANSWER = "chosenAnswer";
public static final String WORD_SPLIT = "wordSplit";
private int quoteNumber;
private String quoteType;
private ArrayList<String> quote;
private ArrayList<String> speaker;
private ArrayList<Integer> answerIndex;
private ArrayList<String> answerChoice;
private String context;
private String chosenAnswer;
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel dest, int flags) {
Bundle bundle = new Bundle();
// insert the key value pairs to the bundle
bundle.putInt(QUOTE_NUMBER, quoteNumber);
bundle.putString(QUOTE_TYPE, quoteType);
bundle.putStringArrayList(QUOTE_ARRAY, quote);
bundle.putStringArrayList(SPEAKER_ARRAY, speaker);
bundle.putIntegerArrayList(ANSWER_INDEX_ARRAY, answerIndex);
bundle.putStringArrayList(ANSWER_CHOICE_ARRAY, answerChoice);
bundle.putString(CONTEXT_KEY, context);
bundle.putString(CHOSEN_ANSWER, chosenAnswer);
bundle.putStringArrayList(WORD_SPLIT, wordSplitTypeA);
// write the key value pairs to the parcel
dest.writeBundle(bundle);
}
public static final Parcelable.Creator<QuoteQuestion> CREATOR = new Creator<QuoteQuestion>() {
#Override
public QuoteQuestion createFromParcel(Parcel source) {
// read the bundle containing key value pairs from the parcel
Bundle bundle = source.readBundle();
QuoteQuestion quoteQuestion = new QuoteQuestion();
quoteQuestion.setQuoteNumber(bundle.getInt(QUOTE_NUMBER));
quoteQuestion.setQuoteType(bundle.getString(QUOTE_TYPE));
quoteQuestion.setQuote(bundle.getStringArrayList(QUOTE_ARRAY));
quoteQuestion.setSpeaker(bundle.getStringArrayList(SPEAKER_ARRAY));
quoteQuestion.setAnswerIndex(bundle.getIntegerArrayList(ANSWER_INDEX_ARRAY));
quoteQuestion.setAnswerChoice(bundle.getStringArrayList(ANSWER_CHOICE_ARRAY));
quoteQuestion.setContext(bundle.getString(CONTEXT_KEY));
quoteQuestion.setChosenAnswer(bundle.getString(CHOSEN_ANSWER));
quoteQuestion.setWordSplitTypeA(bundle.getStringArrayList(WORD_SPLIT));
return quoteQuestion;
}
#Override
public QuoteQuestion[] newArray(int size) {
return new QuoteQuestion[size];
}
};
Also I have a second question while here - It seems all big multi UI apps will have almost all classes implement parcelable? as it is the only way to get data around the app? Is this best practice?
Split your statement into two, using a variable to hold the properly-typed ArrayList, like this:
ArrayList<QuoteQuestion> qq = bundle.getParcelableArrayList(ARRAY_LIST_KEY);
qb.setMonkeyBuisness(qq);
Why this works, whereas casting does not? I have no idea. If anyone knows, please replace this paragraph!
As for the second question, about implementing Parcelable all over the place: The temporary-by-design nature of all Activities and heavy usage of Intents can lead to requiring Parcelable in many places. Certain app design patterns can help mitigate the problem. For example, following MVC techniques, your application data can live within the Model, accessible via a custom class derived from Application. This allows most Activities to avoid saving & restoring bundled data, as they are simply Views of the Model, which persists across device rotations etc. This is a much bigger topic of course, with many different approaches, but hopefully this sparks some ideas.
It should suffice to write:
qb.setMonkeyBuisness(bundle.<QuoteQuestion>getParcelableArrayList(ARRAY_LIST_KEY));
I'm attempting to transfer one object containing an ArrayList of other Objects to another Activity in Android. The idea is to add those to an ArrayList and display them so that the Task in question can be performed.
Task ta = new Task(n, o, t, v, subList);
Intent i = new Intent(this, ActivityList.class);
i.putExtra("task", ta);
startActivity(i);
This is the intent. Task is Parcelable, and the subList is an ArrayList of class Subtask which is also Parcelable.
Surely, since ArrayList always implement Serializable, Parcelling them shouldn't be a problem? The constructor arguments are: String, byte, byte, byte, ArrayList. The bytes are to be used as booleans.
Here is the Parcel code for Task if you need it:
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(taskName);
dest.writeByte((byte) (soundCueOne ? 1 : 0));
dest.writeByte((byte) (soundCueTwo ? 1 : 0));
dest.writeByte((byte) (vibrCue ? 1 : 0));
dest.writeSerializable(myList);
}
private Task(Parcel in) {
this.taskName = in.readString();
this.soundCueOne = in.readByte() != 0;
this.soundCueTwo = in.readByte() != 0;
this.vibrCue = in.readByte() != 0;
this.myList = (ArrayList<Subtask>) in.readSerializable();
}
public static final Parcelable.Creator CREATOR = new Parcelable.Creator() {
public Task createFromParcel(Parcel in) {
return new Task(in);
}
public Task[] newArray(int size) {
return new Task[size];
}
};
Can anyone see what is wrong with the code? It's obviously somewhere, maybe even in the Subtask class, since the emulator crashes as soon as the Task is constructed and it tries to Parcel it.
If I see correctly, then the problem is that you are trying to use the ArrayList<T> as a Serializable, even though it is not a Serializable - it is a Parcelable.
Therefore, replace
dest.writeSerializable(myList);
with
dest.writeTypedList(myList);
and replace
this.myList = (ArrayList<Subtask>) in.readSerializable();
with
this.myList = in.readTypedList(new ArrayList<Subtask>(), Subtask.CREATOR);
I have found this way to do this
Student student = new Student (18,"Z r");
Intent i = new Intent(this, B.class);
i.putExtra("studentObject", student);
startActivity(i);
The problem is that if the object changed in the first activity No change took place in the another activity.
I thought how to make it like a constructor that no copy of the object is pass but the object it self.
Thanks
How about if you configure the "object" as a singleton of the entire application? This way, everybody (your app) sees the changes... See some insights here: http://developer.android.com/guide/faq/framework.html#3
For example, in some other file (Student.java):
public class Student {
public String Name;
}
Create a custom application class:
public MyApp extends Application {
private Student obj = new Student();
public Student getMyObject() {
return obj;
}
}
Anywhere in your application (e.g. SomeActivity.java):
Student appStudent = ((MyApp) getActivity().getApplicationContext()).getMyObject();
appStudent.Name = "New Name"; // "global" update
You could also look into a BroadcastReceiver. With a BroadcastReceiver you can send a message from one Activity to another and with an interface you can pass the object from one Activity to the other.
I think this is a great example, where a BroadcastReceiver is created to check the internet connection of the device. But you can easily convert this in a BroadcastReceiver with your own custom action to send the object.
Implement parcelable in your student class and you can copy the student into the intent.
How can I make my custom objects Parcelable?
Code works with parcelable classes
> Student student = new Student (18,"Zar E Ahmer"); Intent i = new
> Intent(this, B.class); i.putExtra("studentObject", student);
> startActivity(i);
Below is an example of bean class I use that implements parcelable. Here you would replace KmlMarkerOptions with Student
#SuppressLint("ParcelCreator")
public class KmlMarkerOptions implements Parcelable {
public MarkerOptions markeroptions = new MarkerOptions();
public String href = "";
public int hrefhash =-1;
public String id = "";
public long imageId = -1;
public int locationId = -1;
public int markerSize = -1;
public KmlMarkerOptions(){
}
public KmlMarkerOptions(Parcel in) {
this.markeroptions = in.readParcelable(null);
this.href = in.readString();
this.hrefhash = in.readInt();
this.id = in.readString();
this.imageId = in.readLong();
this.locationId = in.readInt();
this.markerSize = in.readInt();
}
#SuppressWarnings("rawtypes")
public static final Parcelable.Creator CREATOR = new Parcelable.Creator() {
public KmlSummary createFromParcel(Parcel in) {
return new KmlSummary(in);
}
public KmlSummary[] newArray(int size) {
return new KmlSummary[size];
}
};
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeParcelable(markeroptions, 0);
dest.writeString(href);
dest.writeInt(hrefhash);
dest.writeString(id);
dest.writeLong(imageId);
dest.writeInt(locationId);
dest.writeInt(markerSize);
}
}
I'm working with custom parcelable class. I need to pass this class as array to another activity and new fragments. I googled, but I found arrayList but not arrays. Please mind that I need to pass arrays not Array lists. How to achieve that? Please mind again, performance is a big issue. So any solutions with less performance consumption is very welcome.
Any helps would be very appreciated.
You can pass your arrayList as below snipts.
TestFragment testFragment = new TestFragment();
Bundle args = new Bundle();
args.putSerializable("parsedData", (Serializable)mTestModelList);
testFragment.setArguments(args);
And Get these parcable data in Model as like below:
mTestModelList = (List<TestModel>)getArguments().get("parsedData");
YES, Serialisation is slow bit than Parcelable.
And you can implement that using parceler
Of course, the ParcelWrapper can be added to an Android Bundle to transfer from Activity to Activity using this:
Bundle bundle = new Bundle();
bundle.putParcelable("example", Parcels.wrap(example));
And dereferenced in the onCreate() method:
Example example = Parcels.unwrap(getIntent().getParcelableExtra("example"));
You can get more detail of performance related to Parcelable vs Serializable here.
Here is the write answer
// write parcelable array
final Bundle arguments = new Bundle();
MyParcelable[] myArray = new MyParcelable[10];
arguments.putParcelableArray("key"myArray);
// read parcelable array
MyParcelable[] myArray = (MyParcelable[])getArguments().getParcelableArray("key");
use like following class of your parcealble array
public class ParcelableLaptop implements Parcelable {
private Laptop laptop;
public Laptop getLaptop() {
return laptop;
}
public ParcelableLaptop(Laptop laptop) {
super();
this.laptop = laptop;
}
private ParcelableLaptop(Parcel in) {
laptop = new Laptop();
laptop.setId(in.readInt());
laptop.setBrand(in.readString());
laptop.setPrice(in.readDouble());
laptop.setImageBitmap((Bitmap) in.readParcelable(Bitmap.class
.getClassLoader()));
}
/*
* you can use hashCode() here.
*/
#Override
public int describeContents() {
return 0;
}
/*
* Actual object Serialization/flattening happens here. You need to
* individually Parcel each property of your object.
*/
#Override
public void writeToParcel(Parcel parcel, int flags) {
parcel.writeInt(laptop.getId());
parcel.writeString(laptop.getBrand());
parcel.writeDouble(laptop.getPrice());
parcel.writeParcelable(laptop.getImageBitmap(),
PARCELABLE_WRITE_RETURN_VALUE);
}
/*
* Parcelable interface must also have a static field called CREATOR,
* which is an object implementing the Parcelable.Creator interface.
* Used to un-marshal or de-serialize object from Parcel.
*/
public static final Parcelable.Creator<ParcelableLaptop> CREATOR =
new Parcelable.Creator<ParcelableLaptop>() {
public ParcelableLaptop createFromParcel(Parcel in) {
return new ParcelableLaptop(in);
}
public ParcelableLaptop[] newArray(int size) {
return new ParcelableLaptop[size];
}
};
}
now make Arraylist of ParcelableLaptop and put in the bUndle with argument
intent.putParcelableArrayListExtra("laptop", parcelableLaptopArray);
Where parcelableLaptop is your Arraylist of the Model. And to get the List:
Intent intent = getIntent();
ArrayList<ParcelableLaptop> parcelableLaptop = (ParcelableLaptop) intent
.getParcelableArrayListExtra("laptop");
I want to convert an array from one type to another. As shown below, I loop over all objects in the first array and cast them to the 2nd array type.
But is this the best way to do it? Is there a way that doesn't require looping and casting each item?
public MySubtype[] convertType(MyObject[] myObjectArray){
MySubtype[] subtypeArray = new MySubtype[myObjectArray.length];
for(int x=0; x < myObjectArray.length; x++){
subtypeArray[x] = (MySubtype)myObjectArray[x];
}
return subtypeArray;
}
You should be able to use something like this:
Arrays.copyOf(myObjectArray, myObjectArray.length, MySubtype[].class);
However this may just be looping and casting under the hood anyway.
See here.
I would suggest working with List instead of Array if possible.
Here is how to do it:
public class MainTest {
class Employee {
private int id;
public Employee(int id) {
super();
this.id = id;
}
}
class TechEmployee extends Employee{
public TechEmployee(int id) {
super(id);
}
}
public static void main(String[] args) {
MainTest test = new MainTest();
test.runTest();
}
private void runTest(){
TechEmployee[] temps = new TechEmployee[3];
temps[0] = new TechEmployee(0);
temps[1] = new TechEmployee(1);
temps[2] = new TechEmployee(2);
Employee[] emps = Arrays.copyOf(temps, temps.length, Employee[].class);
System.out.println(Arrays.toString(emps));
}
}
Just remember you cannot do it the other way around, i.e. you cannot convert Employee[] to a TechEmployee[].
Something like this is possible if you fancy
public MySubtype[] convertType(MyObject[] myObjectArray){
MySubtype[] subtypeArray = new MySubtype[myObjectArray.length];
List<MyObject> subs = Arrays.asList(myObjectArray);
return subs.toArray(subtypeArray);
}