Better way to pass data between Activities in Android - android

In my application I am fetching data from JavaScript, as it is not possible to return the data as an array or object, I am returning it as a String.
Now to organize the data I am creating a class which contains ArrayLists and other string variables and further I am creating array of my class objects variable to store multiple records.
public class Data {
ArrayList<String> m_empArrayList = new ArrayList();
ArrayList<String> m_depArrayList = new ArrayList();
String m_time;
String m_duration;
}
Data d = new Data();
What would be a good approach to pass the data between Activities? As Intents and ShredPrefrences are used to pass small units of data I am not considering it here.

Implement the Parcelable interface in your custom object and transmit it via an Intent.
Here is an example of a Parcelable object.
public class MyObject implements Parcelable {
private String someString = null;
private int someInteger = 0;
public MyObject() {
// perform initialization if necessary
}
private MyObject(Parcel in) {
someString = in.readString();
someInteger = in.readInt();
}
public static final Parcelable.Creator<MyObject> CREATOR =
new Parcelable.Creator<MyObject>() {
#Override
public MyObject createFromParcel(Parcel source) {
return new MyObject(source);
}
#Override
public MyObject[] newArray(int size) {
return new MyObject[size];
}
};
// Getters and setters
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(someString);
dest.writeInt(someInteger);
}
}
Here is what happens. If you implement the Parcelable interface you have to create a private constructor which takes a Parcel as a parameter. That Parcel holds all the serialized values.
You must implement the nested class Parcelable.Creator with the name CREATOR as this is going to be called by Android when recreating your object.
The method describeContents() is only of use in special cases. You can leave it as it is with a return value of 0.
The interesting action happens in writeToParcel() where you, as the name tells, write your data to a Parcel object.
Now you can just add your custom object directly to an Intent like in this example.
MyObject myObject = new MyObject();
Intent i = new Intent();
i.setExtra("MY_OBJECT", myObject);
// implicit or explicit destination declaration
startActivity(i);

you can use Application class present in Android to pass data between activities.
here is a good link..http://www.helloandroid.com/tutorials/maintaining-global-application-state

Related

How to pass sublist via intent

I am trying to pass the top50TrendsList to another activity via Intent as shown below in the code, but it is marked with red and I am getting an error message which says:
2nd parameter cant be cast to Serializable
despite the class Trend implements Serializable interface.
Please let me know how to pass top50TrendsList via Intent to another activity? Thanks.
code:
List<Trend> top50TrendsList = this.mTrendsList.subList(0, 2);
Collections.sort(this.mTrendsList);
Intent intentSendBroadcast = new Intent();
intentSendBroadcast.setAction(ActMain.CONST_BROADCAST_ACTION_ON_LIST_READY);
Bundle b = new Bundle();
b.putSerializable(TwitterTrendsAPIService.CONST_BUNDLE_KEY_SERIALIZED_LIST, top50TrendsList);
intentSendBroadcast.putExtras(b);
sendBroadcast(intentSendBroadcast);
Well your Trend should be Parcelable, and then you can do it like
intent.putParcelableArrayListExtra("myArrayList" , top50TrendsList);
Have a look at this to know about parceable, and how to make your class parceable.
Hope this helps.
ArrayList implements Serializable, not List. You are providing an object via List (interface) reference (and Java has single dispatch, so the overloaded method variant to invoke is being identified in compile time by reference type, not actual object type in runtime). Pass it via ArrayList reference and it will be ok for your goals I suppose.
Use Parcelable
public class Test implements Parcelable
{
List<String> list;
protected Test(Parcel in) {
list = in.createStringArrayList();
}
public Test()
{}
public static final Creator<Test> CREATOR = new Creator<Test>() {
#Override
public Test createFromParcel(Parcel in) {
return new Test(in);
}
#Override
public Test[] newArray(int size) {
return new Test[size];
}
};
public List<String> getList() {
return list;
}
public void setList(List<String> list) {
this.list = list;
}
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeStringList(list);
}
}
setList on Model class and pass to another actiivty:
List<String> list = new ArrayList<>();
Test test = new Test();
test.setList(list);
Intent intent = new Intent(this, Main2Activity.class);
intent.putExtra("list", test);
startActivity(intent);
For getting data from 2nd activity:
if (getIntent().hasExtra("list"))
{
List<String> list = new ArrayList<>();
Test test = getIntent().getParcelableExtra("list");
list.addAll(test.getList());
}
Your Trend class should implements Serializable like this
public class Trend implements
Serializable

Using Parcelable to pass highly nested classes between Activities

Suppose I want to store a custom object of type MyObject in an Intent. The way to do this is to make MyObject implement Parcelable. If one of the fields of MyObject is also a custom object of type Widget the obvious thing to do is to make Widget implement Parcelable too.
The trouble is that there is a huge amount of boilerplate involved when implementing Parcelable. You can get around this by not making Widget implement Parcelable but instead just giving it a constructor taking a Parcel and a method writeToParcel as follows:
public final class Widget {
private final int a;
private final String b;
Widget(Parcel in) {
a = in.readInt();
b = in.readString();
}
void writeToParcel(Parcel out) {
out.writeInt(a);
out.writeString(b);
}
}
You can then have a Widget field in a Parcelable object as follows:
public class MyObject implements Parcelable {
private final int x;
private final Widget w;
MyObject(int x, Widget w) {
this.x = x;
this.w = w;
}
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel out, int flags) {
out.writeInt(x);
w.writeToParcel(out);
}
public static final Parcelable.Creator<MyObject> CREATOR
= new Parcelable.Creator<MyObject>() {
#Override
public MyObject createFromParcel(Parcel in) {
return new MyObject(in.readInt(), new Widget(in));
}
#Override
public MyObject[] newArray(int size) {
return new MyObject[size];
}
};
}
Is this an acceptable approach? Is it considered unidiomatic android to have many custom classes in a project that can be written to and read from Parcels without them actually implementing Parcelable? Or does the fact that I am using a Parcelable to pass complex objects with many fields of custom types (which in turn have many fields of custom type etc etc), indicate that I shouldn't be using Parcelable in the first place?
I would (and did) go with Parceler: https://github.com/johncarl81/parceler
Parceler is a code generation library that generates the Android
Parcelable boilerplate source code. No longer do you have to implement
the Parcelable interface, the writeToParcel() or createFromParcel() or
the public static final CREATOR. You simply annotate a POJO with
#Parcel and Parceler does the rest.
It's really easy to use.
It is recommended to use Parcelable when dealing with passing custom Objects through intents in Android. There isn't an "easy" work around. Since you are dealing with just one extra level of a custom Object (Widget), I would recommend you make Widget Parcelable also. You can also check out this link to see why it is the better approach than using default Serialization. https://coderwall.com/p/vfbing/passing-objects-between-activities-in-android
If your classes are beans, the best solution is the accepted one. If not, I have found that you can (slightly) reduce the pain of implementing Parcelable by creating abstract classes ParcelablePlus and CreatorPlus like this.
ParcelablePlus:
abstract class ParcelablePlus implements Parcelable {
#Override
public final int describeContents() {
return 0;
}
}
CreatorPlus:
abstract class CreatorPlus<T extends Parcelable> implements Parcelable.Creator<T> {
private final Class<T> clazz;
CreatorPlus(Class<T> clazz) {
this.clazz = clazz;
}
#Override
#SuppressWarnings("unchecked")
public final T[] newArray(int size) {
// Safe as long as T is not a generic type.
return (T[]) Array.newInstance(clazz, size);
}
}
Then the Widget class becomes:
public final class Widget extends ParcelablePlus {
private final int a;
private final String b;
Widget(int a, String b) {
this.a = a;
this.b = b;
}
#Override
public void writeToParcel(Parcel out, int flags) {
out.writeInt(a);
out.writeString(b);
}
public static final Creator<Widget> CREATOR = new CreatorPlus<Widget>(Widget.class) {
#Override
public Widget createFromParcel(Parcel in) {
return new Widget(in.readInt(), in.readString());
}
};
}

Parcelable, what is newArray for?

I am implementing Parcelable in order to transmit some simple data throughout an Intent.
However, There is one method in the Parcelable interface that I don't understand at all : newArray().
It does not have any relevant documentation & is not even called in my code when I parcel/deparcel my object.
Sample Parcelable implementation :
public class MyParcelable implements Parcelable {
private int mData;
public int describeContents() {
return 0;
}
public void writeToParcel(Parcel out, int flags) {
out.writeInt(mData);
}
public static final Parcelable.Creator<MyParcelable> CREATOR
= new Parcelable.Creator<MyParcelable>() {
public MyParcelable createFromParcel(Parcel in) {
return new MyParcelable(in);
}
public MyParcelable[] newArray(int size) {
return new MyParcelable[size];
}
};
private MyParcelable(Parcel in) {
mData = in.readInt();
}
}
So, my question is : what is this method for ? and when is it called ?
Is there any point in doing something else than return new MyParcelable[size]; in that method ?
this is a function to be called when you try to deserialize an array of Parcelable objects and for each single object createFromParcel is called.
It is there to prepare the typed array without all the generics stuff. That's it.
Returning just the standard return new MyParcelable[size]; is fine.
It is normal, that you never call it yourself. However, by calling something like Bundle.getParcelableArray() you end up in this method indirectly.
newArray is responsible to create an array of our type of the appropriate size

Pass ArrayList<? implements Parcelable> to Activity

I have searched a few topics but not found a solution to my problem.
public class Series implements Parcelable {
private String name;
private int numOfSeason;
private int numOfEpisode;
/** Constructors and Getters/Setters have been removed to make reading easier **/
public Series(Parcel in) {
String[] data = new String[3];
in.readStringArray(data);
this.name = data[0];
this.numOfSeason = Integer.parseInt(data[1]);
this.numOfEpisode = Integer.parseInt(data[2]);
}
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeStringArray(new String[] { this.name,
String.valueOf(this.numOfSeason),
String.valueOf(this.numOfEpisode) });
}
private void readFromParcel(Parcel in) {
name = in.readString();
numOfSeason = in.readInt();
numOfEpisode = in.readInt();
}
public static final Parcelable.Creator CREATOR = new Parcelable.Creator() {
#Override
public Series createFromParcel(Parcel in) {
return new Series(in);
}
#Override
public Series[] newArray(int size) {
return new Series[size];
}
};
}
In my MainActivity I have an ArrayList. To make the list dynamically editeable I need to pass it to another activity where I can edit it.
ArrayList<Series> listOfSeries = new ArrayList<Series>();
public void openAddActivity() {
Intent intent = new Intent(this, AddActivity.class);
intent.putParcelableArrayListExtra(
"com.example.episodetracker.listofseries",
(ArrayList<? extends Parcelable>) listOfSeries);
startActivity(intent);
}
I need to cast the list, otherwise Eclipse gives me the following Error message.
The method putParcelableArrayListExtra(String, ArrayList) in the type Intent is not applicable for the arguments (String, List)
Is this the correct way to do it?
ArrayList<Series> list = savedInstanceState
.getParcelableArrayList("com.example.episodetracker.listofseries");
This is the way I try to read the data in another activity.
It's crashing on the line above. namely the getParcelableArrayList part.
The problem is in writing out to the parcel and reading in from the parcel ...
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(name);
dest.writeInt(numOfSeason);
dest.writeInt(numOfEpisode);
}
private void readFromParcel(Parcel in) {
name = in.readString();
numOfSeason = in.readInt();
numOfEpisode = in.readInt();
}
What you write out has to match what you read in...
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Intent i = new Intent(this,SecondActivity.class);
ArrayList<testparcel> testing = new ArrayList<testparcel>();
i.putParcelableArrayListExtra("extraextra", testing);
startActivity(i);
}
/**********************************************/
public class SecondActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ArrayList<testparcel> testing = this.getIntent().getParcelableArrayListExtra("extraextra");
}
}
The above code is having onCreate() from two different activities. The first one launches the second one; and it works fine I was able to pull the parcelable without issue.
You should use the putParcelableArrayListExtra() method on the Intent class.
I've used putParcelableArrayList(<? extends Parcelable>) from a Bundle Object. Not directly from an Intent Object.(I don't really know what's the difference). but i use to use in this way:
ArrayList<ParcelableRow> resultSet = new ArrayList<ParcelableRow>();
resultSet = loadData();
Bundle data = new Bundle();
data.putParcelableArrayList("search.resultSet", resultSet);
yourIntent.putExtra("result.content", data);
startActivity(yourIntent);
Later on your new activity you can populate the data recently inserted on the Bundle object like this:
Bundle data = this.getIntent().getBundleExtra("result.content");
ArrayList<ParcelableRow> result = data.getParcelableArrayList("search.resultset");
Just remember that your ArrayList<> must contain only parcelable objects. and just to make sure that your have passed the data you may check if the data received is null or not, just to avoid issues.
Maybe this helps someone.. else my problem was that I used write and readValue but it should match type like writeInt, readInt writeString, readString and so forth
I am doing it in this way:
var intent = Intent(this#McqActivity,ResultActivity::class.java)
intent.putParcelableArrayListExtra("keyResults", ArrayList(resultList))
// resultList is of type mutableListOf<ResultBO>()
startActivity(intent)
And for making the class Parcelable . I simply do two operation first I used #Parcelize Annotation above my data class and secondly I inherit it with Parcelable like below..
import kotlinx.android.parcel.Parcelize
import android.os.Parcelable
#Parcelize // Include Annotion
data class ResultBO(val questionBO: QuestionBO) : Parcelable {
constructor() : this(QuestionBO())
}
And at receiving end
if (intent != null) {
var results = intent.getParcelableArrayListExtra<Parcelable>("keyResults")
}
You can pass Parcelable ArrayList
Sender Activity ->
startActivity(Intent(this, SenderActivity::class.java).apply { putExtra("getList",list)})
Receiver Activity ->
private lateinit var list: ArrayList<List>
list = this.intent.extras?.getParcelableArrayList("getList")!!
And you will get all Arraylist in list.

Transferring object data from one activity to another activity

I am having a class EmployeeInfo as the following:
public class EmployeeInfo {
private int id; // Employee ID
private String name; // Employee Name
private int age;// Employee Age
public int getEmployeeID() {
return id;
}
public void setEmployeeID(int id) {
this.id = id;
}
public String getEmployeeName() {
return name;
}
public void setEmployeeName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age= age;
}
}
ArrayList<EmployeeInfo> employeeInfo object contains the emplyoyee info data for multiple employees.
I want to transfer the data( ArrayList employeeInfo ) from Activity1 to Activity2.
Is using Parcelable the only way to transfer the data from Activity1 to Activity2?
If not , what are the alternatives.
If yes ,kindly provide the prototype code of Parcelable along with the sample code on how to transfer the object data from Activity1 to Activity2.
Here is my implementation of Parceleble:
public class ProfileData implements Parcelable {
private int gender;
private String name;
private String birthDate;
public ProfileData(Parcel source) {
gender = source.readInt();
name = source.readString();
birthDate = source.readString();
}
public ProfileData(int dataGender, String dataName, String dataBDate) {
gender = dataGender;
name = dataName;
birthDate = dataBDate;
}
// Getters and Setters are here
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel out, int flags) {
out.writeInt(gender);
out.writeString(name);
out.writeString(birthDate);
}
public static final Parcelable.Creator<ProfileData> CREATOR
= new Parcelable.Creator<ProfileData>() {
public ProfileData createFromParcel(Parcel in) {
return new ProfileData(in);
}
public ProfileData[] newArray(int size) {
return new ProfileData[size];
}
};
}
and how I transfer data:
Intent parcelIntent = new Intent().setClass(ActivityA.this, ActivityB.class);
ProfileData data = new ProfileData(profile.gender, profile.getFullName(), profile.birthDate);
parcelIntent.putExtra("profile_details", data);
startActivity(parcelIntent);
and take data:
Bundle data = getIntent().getExtras();
ProfileData profile = data.getParcelable("profile_details");
You can simply let your EmployeeInfo class implement Serializable. Or you can send data like this
intent.putExtra("id", employInfo.getEmployeeID());
intent.putExtra("name", employInfo.getEmployeeName());
intent.putExtra("age", employInfo.getAge());
If you need to transfer a list of your custom classes, i'd use the first approach. So you would be able to put entire list as Serializable.
However they said that everyone should use Parcelable instead because it's "way faster". Tbh, I'd never used it, because it needs more effort and I doubt somebody can realize the difference in speed in a regular application w/o a load of data sending via intent
Good question. Looking at the docs and doing armchair coding:
It may be possible to pass an object between Activities by calling putExtras(Bundle) and myBundle.putSerializable. The object and the entire object tree would need to implement serializable.
JAL
EDIT: The answer is yes:
It is possible to pass an immutable object between Activities by calling putExtras(Bundle) and myBundle.putSerializable. The object and the entire object tree would need to implement serializable. This is a basic tenet of Object Oriented Programming, passing of stateful messages.
First we create the immutable object by declaring a new class:
package jalcomputing.confusetext;
import java.io.Serializable;
/*
* Immutable messaging object to pass state from Activity Main to Activity ManageKeys
* No error checking
*/
public final class MainManageKeysMessage implements Serializable {
private static final long serialVersionUID = 1L;
public final int lengthPassword;
public final long timeExpire;
public final boolean isValidKey;
public final int timeoutType;
public MainManageKeysMessage(int lengthPassword, long timeExpire, boolean isValidKey, int timeoutType){
this.lengthPassword= lengthPassword;
this.timeExpire= timeExpire;
this.isValidKey= isValidKey;
this.timeoutType= timeoutType;
}
}
Then we create an immutable stateful instance of the class, a message, in the parent activity, and send it in an intent as in:
private void LaunchManageKeys() {
Intent i= new Intent(this, ManageKeys.class); // no param constructor
// push data (4)
MainManageKeysMessage message= new MainManageKeysMessage(lengthPassword,timeExpire,isValidKey,timeoutType);
Bundle b= new Bundle();
b.putSerializable("jalcomputing.confusetext.MainManageKeysMessage", message);
i.putExtras(b);
startActivityForResult(i,REQUEST_MANAGE_KEYS); // used for callback
}
Finally, we retrieve the object in the child activity.
try {
inMessage= (MainManageKeysMessage) getIntent().getSerializableExtra("jalcomputing.confusetext.MainManageKeysMessage");
lengthPassword= inMessage.lengthPassword;
timeoutType= inMessage.timeoutType;
isValidKey= inMessage.isValidKey;
timeExpire= inMessage.timeExpire;
} catch(Exception e){
lengthPassword= -1;
timeoutType= TIMEOUT_NEVER;
isValidKey= true;
timeExpire= LONG_YEAR_MILLIS;
}
Well there is another way to transfer an object.We can use application to transfer object and this is way is far better way in my opinion.
First of all create your custom application in your main package.
public class TestApplication extends Application {
private Object transferObj;
#Override
public void onCreate() {
super.onCreate();
// ACRA.init(this);
}
public Object getTransferObj() {
return transferObj;
}
public void setTransferObj(Object transferObj) {
this.transferObj = transferObj;
}
}
Now use setTransfer and get transfer methods to move abjects from one activity to other like:
To Transfer:
((TestApplication) activity.getApplication()).setTransferObj(Yous object);
ToRecieve:
Object obj=((TestApplication) activity.getApplication()).getTransferObj();
NOTE
Always remember to make entry of this application in manifest application tag:
<application
android:name=".TestApplication">
</application>
You can convert your object to jsonstring using Gson or Jakson and pass using intent as string and read the json in another activity.

Categories

Resources