I am developing simple travel agent android application. When application starts it loads list of cities, in the next activity user will see source and destination spinners, once user selects proper source and destination, it will take to third activity, it displays available travels if user selects one it takes to fourth activity in which user selects seat and continues to book ticket. In order to maintain complete user session I am maintaining a UserSession class, which is as follows (removing unnecessary logic in the code which is irrelevant to problems)
public class UserSession implements Parcelable {
List<City> citiesList;
HashMap<String, City> cityMap; // Map city name to code
RouteSearchResult searchedRoutes;
String sourceCity;
String destinationCity;
LocalStop selectedBoardingPoint;
LocalStop selectedArrivalPoint;
#Override
public void writeToParcel(Parcel parcel, int flags) {
try {
parcel.writeList(citiesList);
parcel.writeMap(cityMap);
parcel.writeValue(searchedRoutes);
parcel.writeString(sourceCity);
parcel.writeString(destinationCity);
parcel.writeValue(selectedBoardingPoint);
parcel.writeValue(selectedArrivalPoint);
} catch (Exception e) {
e.printStackTrace();
}
}
}
public class City implements Parcelable {
String cityName;
String cityId;
#Override
public void writeToParcel(Parcel parcel, int flags) {
parcel.writeString(cityName);
parcel.writeString(cityId);
}
}
public class RouteSearchResult {
City source;
City destination;
String date;
List<RouteDetails> routeDetails;
}
I have the following problems
1) I am getting run time exception while writing
W/System.err( 817): java.lang.RuntimeException: Parcel: unable to marshal value com.travelagent.City#40605878
W/System.err( 817): at android.os.Parcel.writeValue(Parcel.java:1132)
W/System.err( 817): at android.os.Parcel.writeList(Parcel.java:519)
W/System.err( 817): at com.travelagent.UserSession.writeToParcel(UserSession.java:201)
W/System.err( 817): at android.os.Parcel.writeParcelable(Parcel.java:1151)
2) I commented parcel.writeList(citiesList); statement to see if I get any more problems, I got similar problem with parcel.writeValue(searchedRoutes);
I have following questions,
1) How to make a list of custom objects as parcelable?
2) Is it mandatory to make custom class like City also parcelable, which is part of actual class to be parceled.
3) Is there any approach to solve this kind of problems like passing user session to all activities.
Please help, I searched, but I couldn't find appropriate solution to make custom class parcelable.
My understanding is that you shouldn't use Parcels for this. From the android documentation on Parcels:
Parcel is not a general-purpose serialization mechanism. This class (and the corresponding Parcelable API for placing arbitrary objects into a Parcel) is designed as a high-performance IPC transport. As such, it is not appropriate to place any Parcel data in to persistent storage: changes in the underlying implementation of any of the data in the Parcel can render older data unreadable.
What you should do is create two static methods for each class you want to transfer to the next activity. Call one of them toBundle() and one of them fromBundle(). The first converts a given instance of you class to a bundle while the second creates a new instance of your class from a bundle. Every time you need to pass an object on to the next Activity, just bundle it up using the toBundle() method and add it to the Intent you're using to start the next Activity. In the next Activity, you can recreate the object by calling fromBundle().
Related
Clean Architecture Question
I have many form activities that has 1 common data that must be appended during submission, my question is, where will the logic must be placed?
Domain or Presentation Layer?
For Presentation:
I'll create a BaseFormActivity that has a method of inserting the needed data on a form that is child of BaseForm which contains the needed data globally.
BaseForm: (to be extended by all forms)
public class BaseForm {
private String globalData;
//getter setters...
}
BaseFormPresenter:
public class BaseFormPresenter extends BaseFormMvpView {
private final GetGlobalDataInteractor mGetData; //to be injected, this is a use case
public void getGlobalData() {
mGetData.execute()
.subscribe(data -> {
getMvpView().showGlobalData(data);
}); //just for the sake of simplicity
}
}
BaseFormActivity: (which is extended by all activity that handles form)
public abstract class BaseFormActivity implements BaseFormMvpView {
#Inject
BaseFormPresenter mPresenter;
//onCreate(), etc
}
SpecificFormActivity: (extends BaseFormActivity)
public class SpecificFormActivity extends BaseFormActivity {
private SpecificForm mForm; //extends BaseForm
//onCreate(), etc
#Override
public void showGlobalData(String data) {
mForm.setGlobalData(data);
}
//then ill just call the presenter to get the global data before submitting
}
For Domain:
SubmitSpecificFormInteractor: (Sorry for the coding, it is just a representation on what I'm thinking to do)
public class SubmitSpecificFormInteractor extends SingleUseCase<Return, Param> {
//to be injected
GlobalRepository mGlobalRepository;
SpecificFormRepository mFormRepository;
//some initialization
public Single<SomeResponse> buildObservable(#NonNull String specificFormData, String anotherSpecificFormData) {
return mGlobalRepository.getGlobalData()
.map(globalData -> SpecificFormDto.create(
specificFormData, anotherSpecificFormData, globalData)) //create the dto then append global data
.flatMap(specificFormDto -> mFormRepository.submit(specificFormDto)) //then submit data
}
}
I'm thinking of placing it on the domain layer (you can see that it is much isolated, but I'll have to do it on all form submissions, which is redundancy), but still I just want to make my decision solid. Refactoring is time consuming. Hope you understand my point here, specially on my pseudocode-like coding. Feel free to comment if there's something hard to understand. Thank you.
In Clean Architecture all business rules go to use case interactors. The main goal is to keep the business rules independent from any details - from any framework - that includes android as well.
The Clean Architecture then uses "interface adapters" (in UI part called "controllers" and "presenters") to map between data most convenient for the inner circles and data convenient for the frameworks.
So in ur case u should go for ur second proposal - even if that means that u have to call the interactor from multiple places and have to map some data types. it is worth the benefits (business rules free from details).
For a more detailed discussion about use case interactors, controllers and presenters pls refer to my posts here: https://plainionist.github.io/Implementing-Clean-Architecture-UseCases/
and here https://plainionist.github.io/Implementing-Clean-Architecture-Controller-Presenter/
What is the correct way of implementing the Parcelable interface in Android? According to the documentation you should implement the writeToParcel method and have a CREATOR.
public void writeToParcel(Parcel out, int flags) {
out.writeInt(mData);
}
But when I implement it without adding a CREATOR and leaving the writeToParcel() empty the app still seems to work correctly. Sometimes I would get a Bad Parcelable Exception but I can't work out the steps to replicate.
This is how I use to pass an object from activity to fragment
Bundle bundle = new Bundle();
bundle.putParcelable(PageFragment.PAGE_FILTER_KEY, page);
fragment.setArguments(bundle);
So, what is the purpose of adding stuff like out.writeInt(mData); what kind of problems can be expected if this is not done?
Parcelable implementation mainly have two process steps.
1 Writing your java object to Parcel which includes two methods.
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(cityName);
dest.writeString(macroName);
dest.writeString(id);
}
where describe content is for setting a flag for your contents. Most of time you just need to it untouched.
public void writeToParcel(Parcel dest, int flags) , you need to write you Java class object to parcel step by step according to fields in JAVA class. In above example my class has three strings. You can write almost all types of objects in parcel. You just need to chose appropriate one. Like writeString(),writeList() or writeObject() etc.
2. Second part is reading your java object back from parcel
This part required two things as well. First is CREATOR of your java class like following
public static final Creator<City> CREATOR = new Creator<City>() {
#Override
public City createFromParcel(Parcel in) {
return new City(in);
}
#Override
public City[] newArray(int size) {
return new City[size];
}
};
In above example my Java class is City. It makes read a City object from parcel. But it calls new City(in) constructor of City class. So now I need a constructor which accept a parcel object in arguments. Lets create that too..
protected City(Parcel in) {
cityName = in.readString();
macroName = in.readString();
id = in.readString();
}
Now we make a class complete full proof parcelable. One thing to notice, we need to read members in same sequence at protected City(Parcel in) we put them in parcel i.e. in writeToParcel() method.
On how to reproduce badParcelable exeption in simply letting android create java object from parcelable. For that you can choose Destroy activities from developer options on android device and put you app in background in that activity, so android kill your application process ID. Resume your app by recreating activity (onCreate + Bundle), you will get that exception if you does not implemented parcelable correctly.
But when I implement it without adding a CREATOR and leaving the writeToParcel() empty the app still seems to work correctly.
CREATOR is used when reading data back out of a Parcel and converting it back into objects.
writeToParcel() puts your data into the Parcel.
The only way that leaving those off will work correctly is in cases where your Parcelable is not actually being put into a Parcel or reconstituted from a Parcel. Examples include LocalBroadcastManager.
what is the purpose of adding stuff like out.writeInt(mData);
It would be the same purpose as adding stuff like out.write() with an OutputStream: it writes to the output. Your question is akin to asking "hey, if I don't write data to my file, what sorts of problems will I encounter?".
I'm a beginner to android development, and I'm trying to write my code in an MVC pattern, but I'm having trouble understanding how a model would work. As far as I can tell every time you start a new activity with an intent you are not able to pass a model along with it. As far as i can tell you'd have to reinitialize it each time you start a new activity. Am I missing something? I looked into Parcelable, but it seems that you loose your methods if you make your model Parcelable. right now I'm building a log in system, which checks my local sqllite db on start up if the user has already logged in, and if so it passes to another activity, otherwise it passes to the log in activity, but I wan't to keep that user model alive through all the activities. Is thee a way to do that?
You might want to also consider keeping a static reference around to the model data that you want to share across activities so that you don't have to keep serializing/deserializing the model when switching between activities. You can get away with using Parcelable if your models are small, but at some point, performance may become an issue.
I'm working on a project where we keep the models in a Singleton that we can access throughout the app, and although I generally hate Singleton's for how they can make unit testing more difficult, I have found this approach to perform better with larger models than trying to rely on Android's serialization mechanism.
Here's is a very rough example of what I mean (disclaimer: I have not actually run tested this code, but I hope this illustrates the concept):
You might have a singleton class that I terribly called Models
public class Models {
private static Models instance;
private boolean isInitialized = false;
private User user;
private OtherInterestingModel otherInterestingModel;
private Models() {
}
public static synchronized Models getInstance() {
if (instance == null) {
instance = new Models();
}
return instance;
}
public void loadModels() {
if (!isInitialized) {
/*
* One-time model initialization here.
*/
isInitialized = true;
}
}
public User getUser() {
return user;
}
public OtherInterestingModel getOtherInterestingModel() {
return otherInterestingModel;
}
}
In your LoginActivity, you can initialize the Models class, say, in your onCreate():
public class LoginActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Models.getInstance().loadModels();
User user = Models.getInstance().getUser();
OtherInterestingModelData otherData = Models.getInstance().getOtherInterestingModel();
// Do something with the model data...
}
/*
* This might be called after the user enters data and clicks a login button...
*/
private void login() {
startActivity(new Intent(this, AwesomeLoggedInActivity.class));
}
}
Once the user successfully logs into your app, you could have basically the same code in your main activity:
public class AwesomeLoggedInActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Models.getInstance().loadModels();
User user = Models.getInstance().getUser();
OtherInterestingModelData otherData = Models.getInstance().getOtherInterestingModel();
// Do something with the model data...
}
}
Notice that by having a Singleton, you avoided having to serialize the model data by passing it through the intent that started the main activity.
Yes, you can do that with the Parcelable interface.
You do not lose your class's methods when you implement the Parcelable interface. The interface simply defines a method for writing your member variables to a Parcel object when you need to pass the object around.
Once you retrieve the data from your Intent via getParcelableExtra(), the object is recreated from the Parcel and you can once again treat it as an instance of whatever class it is.
For example, if you have a User class that extends Parcelable, you can bundle it with an Intent by calling putExtra("user", myUser). myUser is then (behind the scenes) packed into a Parcel and attached to the Intent. In your next Activity, you can retrieve that User object with User myUser = (User) getParcelableExtra("user");, and the Parcel will be unpacked and returned to you. You wil once again have a fully functioning User object.
This has been asked a few times here on SO, but my case is a bit different.
I have class A that implements Parcelable. Class A contains some member data that can be parceled. It has its own CREATOR and implements writeToParcel(), describeContents(), and a constructor that accepts a Parcel.
There is class B that extends from class A. Class B has additional member data, but none of them need to be parceled. Basically, class B's parcelable data is the same as class A. If I try to put B in a Bundle, pass it to another Activity, and read it back, I would get a ClassCastException. I guess that's expected.
After a bit of trial-and-error, in order to make class B parcelable, I have to implement at least these two things:
public static final Parcelable.Creator<B> CREATOR
= new Parcelable.Creator<B>() {
public B createFromParcel(Parcel source) {
return new B(source);
}
public B[] newArray(int size) {
return new B[size];
}
};
public B(Parcel in) throws JSONException {
super(in);
}
So my concern is this. There are about half a dozen or more classes that extend from A and all have the same issue as B. It seems silly that each one of them has to add their own static CREATOR and a constructor that accepts a Parcel, only to pass it back to A. Everything else is identical. The only thing that makes it different is the name of the class. It beats the purpose of having inheritance in the first place.
For example, if there's another class C that extends B, I need to do the same:
public static final Parcelable.Creator<C> CREATOR
= new Parcelable.Creator<C>() {
public C createFromParcel(Parcel source) {
return new C(source);
}
public C[] newArray(int size) {
return new C[size];
}
};
public C(Parcel in) throws JSONException {
super(in);
}
Is there some sort of clever techniques in Java to automate this process? Perhaps using generic of some sort? If there's no other way, I might just as well just remove the inheritance lineage, and require each class to implement Parcelable themselves.
This is a little complicated, but the only way I can think of offhand involves reflection - Provided all of the subclasses have a constructor that takes a Parcel that then calls super(parcel), you could make the class name a part of the parcel - then in your createFromParcel method of A:
public A createFromParcel(Parcel source) {
Class clazz = Class.forName(source.readString());
Class[1] paramTypes = { Parcel.class };
Constructor ctor = clazz.getConstructor(paramTypes);
A myA = (A) ctor.newInstance(source);
return myA;
}
Note that this was written largely off the cuff and may need some tweaking before it runs (I know for sure it's missing checked exception handlers) - but hopefully the idea is clear
I have implemented the solution described by JRaymond and it works. It's a little complicated since it involves some reflection,but i hope it helps someone else. Now any class that is parcelable should extend this GlobalParcelable class which implements parcelable.
https://github.com/awadalaa/Android-Global-Parcelable
In Android application development, I frequently go through the word CallBack in many places. I want to know what it means to tell us technically - and how I can manage to use the callback in applications. I need a guide to understand it and use it.
i want to know what it means, tell
us technically
http://en.wikipedia.org/wiki/Callback_%28computer_science%29
"In object-oriented programming languages without function-valued arguments, such as Java, [callbacks] can be simulated by passing an abstract class or interface, of which the receiver will call one or more methods, while the calling end provides a concrete implementation. Such objects are effectively a bundle of callbacks, plus the data they need to manipulate. They are useful in implementing various design patterns such as Visitor, Observer, and Strategy."
how i can manage the callback of the
applications
I have no idea what this means.
Hmm. How about an example. You write a quicksort algorithm in C. The user who wants to use your algorithm must supply a compare method appropriate for what the user is sorting with your algorithm. The user must pass a function pointer to the user's compare method to your quicksort code. The quicksort code uses this address, the function pointer, to CALL BACK to the user's compare function. You provide a function prototype, no implementation, since you cannot possibly know how to determine the ordinality of what is being sorted. The user supplies the implementation of compare that makes sense for what the user is sorting. This implementation must match the function prototype. The function pointer is used by the quicksort alogorithm to reach back and touch the user's code.
This is actually about polymorphism.
In java, you can use an interface to do this. So for sorting, see the interface IComparer and IComparable.
A Callable interface can be used to run a piece of code as Runnable does. However, Callable can return the result and can throw checked an exception.
For more detail.
http://developer.android.com/reference/java/util/concurrent/Callable.html
By using Callable interfaces you can pass an argument as function I added a simple code snippet for understanding.
public class MainActivity<V> extends Activity {
Callable<String> doLogin=null;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
doLogin=new Callable<String>() { //created but not called now.
#Override
public String call() throws Exception {
//make some piece of code
return "something"; //or false
}
};
CheckSession checkSession=new CheckSession("sessionName");
String sessionKey="";
try { //we are sending callable to the DAO or any class we want
sessionKey=checkSession.getSessionKey(doLogin);
} catch (Exception e) {
e.printStackTrace();
}
}
}
public class CheckSession{
String sessionName="";
Callable<String> func=null;
public CheckSession(String sessionName) {
super();
this.sessionName = sessionName;
}
public String getSessionKey(Callable<String> doLogin) throws Exception{
func=doLogin;
return (String) func.call();
}
}