How can i correctly interact between Activities and fragments - android

I'm working with Steppers. So currently i have one Activity with 3 Fragments in which the user must complete with some information, like a Form.
There are a lot of information so i made 4 classes to separate that information.
In addition, some information is got it automatically so in fragments i ask for permissions...
For Example:
public class UserIds {
#Nullable
#Expose String phone;
#Expose String Email;
#Expose String phone2;
#Expose String ModCel;
#Expose String Doc;
//Setters, getters and another simple method
public class UserLocation {
#Nullable
#Expose String street;
#Expose int number;
....
//Setters, getters and another simple method
...
And so on with 2 classes more.
So, as you can see i'm working with retrofit too.
How can I correctly handle something like that?
I read about Parceler , Interfaces, EventBus...
Should I declare all objects instances in the Activity and then modify in each fragment ( Some objects are modified by differents fragments) or maybe create instances in each fragment, store the information and in when the Complete button is pressed, obtain the information? How should i save this objects in case of OnDestroy() call?
Another things to take into account is that finally, when the form is end. Other activity may have all the information and ask for more (yeah, a LOT OF INFORMATION IS NEEDED).
Finally, every time the user complete the form (with the complete button and then when the other activity ask for more, this data is sended to the server)

I chose the Parceler way and work perfectly. Maybe help somebody, i put #Parcel in each POJO class, then as i am handling with fragments with StepperAdapter (because of stepstone library) in the fragment which i want to save data i did this:
// Obtaing all the fragments
List<Fragment> steps = getFragmentManager().getFragments();
// save states
Bundle bundle = new Bundle();
Parcelable wrapped = Parcels.wrap(obj1);
Parcelable wrapped2 = Parcels.wrap(obj2);
bundle.putParcelable("OBJ1", wrapped);
bundle.putParcelable("OBJ2", wrapped2);
steps.get(fragment2reference).getArguments().putAll(bundle);
Then in the fragment that receive, you have to create a constructor and then you can receive the data (because of fragment was already created, so the bundle throw error)
//Constructor
public fragment2(){
super();
setArguments(new Bundle());
}
When fragment2 shows :
OBJ1 a = Parcels.unwrap(getArguments().getParcelable("OBJ1"));
OBJ2 b = Parcels.unwrap(getArguments().getParcelable("OBJ2"));
Hope somebody help!

Related

Get and observe three objects with Room/LiveData

I have following database relationship:
In words: One Order has 0-n Books, one Order is assigned to one Customer.
In my case, I have bookId. I want to launch some function when I get all associated items (Book, Order and Customer) and when I am assured all of them exists - I need to launch it only one time. I tried to solve it following way:
ViewModel:
private LiveData<Book> book;
private LiveData<Order> order;
private LiveData<Customer> customer;
public MyViewModel(Application app) {
...
book = bookRepository.getBookLiveData(id);
order = Transformations.switchMap(book, b -> orderRepository.getOrder(b.getIdOrder()));
customer = Transformations.switchMap(order, o -> customerRepository.getCustomer(o.getIdCustomer()));
}
However, this solution is uneffective and I believe this can be done some more elegant way with Room/LiveData.
I tried also another approach - creating following object BookOrderCustomer:
public class BookOrderCustomer {
#Embedded
public Book book;
#Embedded
public Order order;
#Embedded
public Customer customer;
}
But this did not work as expected,Dao's query always returned null.
Any idea how to solve this case? Thank you.
There is the MediatorLiveData, it could observe all your streams and merge data. I think this is a best approach.

Reducing number of event classes when using EventBus or Otto

I am about to start development on an Android app. I am interested in using Otto or EventBus in my app to assist with making asynchronous REST network calls and notifying the main thread when the calls have returned.The one major flaw with the use of these busses that I have found during research is that there are typically too many event classes that have to be created. Are there any patterns or approaches to reduce the number of event classes that have to be used?
The concept
The best way i have solved the issue of too many event classes is by using Static Nested Classes You can read up more about them here.
Now using the above concept here is how you would solve the problem:
So basically suppose you have a class called Doctor that you are using to create an object you are passing around with your application. However you want to send the same Object over the network and retrieve JSON in the context of that same object and feed it back to a subscriber to do something with. You would probably create 2 classes
DoctorJsonObject.java that contains information about the returned JSON data and
DoctorObject.java that has data you are passing around in your app.
You don't need to do that.
Instead do this:
public class Doctor{
static class JSONData{
String name;
String ward;
String id;
//Add your getters and setter
}
static class AppData{
public AppData(String username, String password){
//do something within your constructor
}
String username;
String password;
//Add your getters and setters
}
}
Now you have one Doctors Class that Encapsulates both the events for the post to the network and the post back from the network.
Doctor.JSONData represents data returned from the network in Json format.
Doctor.AppData represents "model" data being passed around in the app.
To use the class' AppData object then for the post event:
/*
You would post data from a fragment to fetch data from your server.
The data being posted within your app lets say is packaged as a doctor
object with a doctors username and password.
*/
public function postRequest(){
bus.post(new Doctor.AppData("doctors_username","doctros_password"));
}
The subscriber within you implementation that listens for this object and makes an http request and returns the Doctor.JSONData:
/*
retrofit implementation containing
the listener for that doctor's post
*/
#Subscribe
public void doctorsLogin(Doctor.AppData doc){
//put the Doctor.JSONObject in the callback
api.getDoctor(doc.getDoctorsName(), doc.getPassWord(), new Callback<Doctor.JSONObject>() {
#Override
public void success(Doctor.JSONObject doc, Response response) {
bus.post(doc);
}
#Override
public void failure(RetrofitError e) {
//handle error
}
});
}
}
With the above implementation you have encapsulated all Doctor Object related events within ONE Doctor class and accessed the different types of objects you need at different times using static inner classes. Less classes more structure.

Android refactor small method to return String from object breaks

So I have a class that has
String city, state;
public String getCity() {return city;} public String getState(){get state;}
if I change this class around where city and state are replaced with a Class City which contains the two strings name and state to be
City city;
public String getCity() { return city.getName();}
public String getState() {return city.getState();}
it stops working. It just doesn't transfer to my next intent after the splash screen, no errors. Does android use the constructor? or use some kind of direct field access instead of the getters that I'm not aware of?
thanks!
it seems so dumb I don't get it. I don't get any errors either.
The class (Tour) these fields are in, as well as the City class are the simplest of pojos I use to display in a list view. I load the List in a splash screen, then pass off an intent to the Activity with the list view of them. When city and state are both string variables in Tour it works. When I replace city and state with Class City which has name and state String member variables the next intent never shows. It just sits on the splash screen. That is the only change.
Intent intent = new Intent(SplashActivity.this, TourListActivity.class);
intent.putExtra(TourList.class.getCanonicalName(), tours);
startActivity(intent); //this line starts but nothing else is ever called
yeah. If I remove logcat filters I get this after my last app specific log message
02-12 20:28:43.975 826-31520/? W/ContextImpl﹕ Calling a method in the system process without a qualified user: android.app.ContextImpl.sendBroadcast:1510 com.android.server.InputMethodManagerService$4.run:2772 java.lang.Thread.run:841 <bottom of call stack> <bottom of call stack>
but as I read it that's just anything really. but how would that simple pojo change cause that?
My City class didn't implement Serializable. Every class you pass with an Intent must implement java.io.Serializable

JSONObject as class attribute storage/retrieval

In android, I'm using model classes with methods to handle the data manipulation. My data is brought in from webservices as json. I'm contemplating the possibility of using JSONObjects to store the values of class level attributes. But, I don't know of a way to use the JSONObj as the "holder" variable and create access methods. I don't want to predetermine these methods, as jsonRepository should hold the values, not always known at design time
For example, I'd like to have:
public class User {
private JSONObject jsonAttributes;
public User(String json) {
this.jsonAttributes= new JSONObject(json);
}
[IMPLICIT attribute access methods]
public string Prop1() returns jsonAttributes.getString("prop1");
public string Prop1(String newProp1) returns jsonAttributes.putString("prop1",newProp1);
public string Prop2() returns jsonRepository.getString("id");
public string Prop2(String newProp2) returns jsonAttributes.putString("prop2",newProp2);
....
from outside this class then, I would access the attributes simply...
User myUser = new User(someValidJson);
String myString = myUser.Prop1
Misguided? If not, how does one manage implicit property setting/getting?
As was mentioned in the comment above, why not create your user class, with all of the relevant memeber variables, and simply parse your JSON data in order to populate the ionformation in your user class.
There are a lot of ways you can do this, but I would consider using the builder pattern, as it is flexible, which could be useful if your JSON data changes in the future.

Saving data from activity(A) and reading in another Activity(history)

I have an activity that contain a list of data (TextView), i need to save this data that have been choose (onClick) in the list and been able to get and read it in another activity(history)
I understand that is exist a possibility with the "serializable" but i did not success to understand how it could help me.
i will be happy if someone can clarify this subject for me,example?.
Thank you for helping!!
If you're trying to pass a String to another activity, you can do this with putExtra and getStringExtra:
Intent intent = new Intent(this, OtherActivity.class);
intent.putExtra("parameter", myStringParameter);
startActivity(intent);
and then read it in OtherActivity's onCreate method:
String parameter = getIntent().getStringExtra("parameter");
The Serializable interface is useful for marshalling more complicated objects; you don't need this if you're just dealing with String.
Edit - if you need to store small amounts of data persistently you could use SharedPreferences:
final String TAG = "MyApplication";
SharedPreferences prefs = getSharedPreferences(TAG, MODE_PRIVATE);
prefs.edit().putString("parameter", myStringParameter).commit();
and then to read the preferences:
final String TAG = "MyApplication";
SharedPreferences prefs = getSharedPreferences(TAG, MODE_PRIVATE);
String parameter = prefs.getString("parameter", null);
This data will be available even after your application closes.
Yes, use classes that implement Serializable. See my answer on this question: How to pass several variables of different types from one function to another on android?
Create some model classes which will hold data:
public class Page implements Serializable {
private String name;
private String description;
//and so on...
public Page(String name, String description) {
this.name = name;
this.description = description;
}
public String getName() {
return name;
}
public String getDescription() {
return description;
}
}
Now you can create a Page object and fill it with data(name, description) via the constructor. Optionally make some setters.
Page p = new Page("James", "Hello World");
startActivity(new Intent(context, MyActivity.class).putExtra("Page", p));
Retrieve your Page in MyActivity in its onCreate method:
Page p = (Page)getIntent().getExtras().getSerializable("Page");
Toast.makeText(this, "Name: " + p.getName() + ", Description:" + p.getDescription(), Toast.LENGTH_LONG).show();
I am aware about the methods mentioned above. But just for alternative thoughts, since you are mentioning the history word, how about using SQLite for this purpose?
In first activity, you can save the data, and in second activity, retrieve the data.
You can share data between your activities in various ways depending on how much data you need to save and how long the data needs to be saved.
Intents : Useful when transferring small bits of data between 2-3 screens
Singleton / Static data store classes : useful when sharing lot of data between various activities
SQLite DB : Large amount of data to be shared , also useful to save the same between app launches
Shared Preferences : Small amount of data , between app launches.
For your use-case its best to use intents unless the data is shared between more than 2-3 activities where option 2 would be a better solution
Sending immutable stateful objects between activities (messaging) is commendable, IMHO. It can be argued that OOP is about messaging, not objects. Two suggestions. 1) use the fully qualified name for the name:value pair so do:
private void launchManagePassword() {
Intent i= new Intent(this, ManagePassword.class); // no param constructor
PasswordState outState= new PasswordState(lengthKey,timeExpire,isValidKey,timeoutType,"",model.getIsHashPassword());
Bundle b= new Bundle();
b.putSerializable("jalcomputing.confusetext.PasswordState", outState);
i.putExtras(b);
startActivityForResult(i,REQUEST_MANAGE_PASSWORD); // used for callback
}
This will minimize runtime casting errors.
2) When you have the app working well and the object interface is stabilized consider refactoring the code to a Parcel for speed.
JAL
EDIT: AS REQUESTED Code

Categories

Resources