Reducing number of event classes when using EventBus or Otto - android

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.

Related

Changes are not observable by viewmodel

I have created an app which is relied on my local server which fetch profile image and information about user..Code works fine without any problem but when I change my data in the local server (for example profile picture )the updated profile is not reflecting in the application until activity is restarted but this should not be happened because live data should reflect the change immediately as soon as changes occurred in the database.
below is the code of live data class
private MutableLiveData<Profile> profileMutableLiveData;
public void init(String token){
if (profileMutableLiveData!=null){
return;
}
repository=Repository.getInstance();
profileMutableLiveData=repository.getProfile(token);
}
public LiveData<Profile> getProfile(){
return profileMutableLiveData;
}
here is my Repository code
public class Repository {
private static Repository instance;
public static Repository getInstance(){
if (instance==null){
instance=new Repository();
}
return instance;
}
public MutableLiveData<Profile> getProfile(String token){
MutableLiveData<Profile> data=new MutableLiveData<>();
RetrofitApi retrofitApi=RetrofitInstance.getInstance();
Call<Profile> call=retrofitApi.getProfile(token);
call.enqueue(new Callback<Profile>() {
#Override
public void onResponse(Call<Profile> call, Response<Profile> response) {
Profile profile=response.body();
if (response.isSuccessful()){
data.setValue(profile);
}
}
#Override
public void onFailure(Call<Profile> call, Throwable t) {
}
});
return data;
}
}
Code in main activity to observe changes....
actually I am showing profile image in navigation drawer ... like telegram app
viewModelClass = new ViewModelProvider(this).get(ViewModelClass.class);
viewModelClass.init(token);
viewModelClass.getProfile().observe(this, new Observer<Profile>() {
#Override
public void onChanged(Profile profile) {
Picasso.get().load("http://192.168.43.216:8000" + profile.getProfile_photo()).into(profileImage);
fName = profile.getFirst_name();
lName = profile.getLast_name();
image = profile.getProfile_photo();
nameView.setText("Hello " + profile.getFirst_name());
}
});
}
The code is working fine but I want the data must be updated as soon as changes made in my server...
but data is updated when I restart the activity or opening app again after closing the activity...
May be the problem - is that you begin to observe in your activity one instance of MutableLiveData, and then you replace it with another one.
In your ViewModel:
profileMutableLiveData=repository.getProfile(token);
you override it instead of setting new value with "postValue"
In your Repository:
MutableLiveData<Profile> data=new MutableLiveData<>();
you make another instance of LiveData
You can try to change your return value from a Repository to a "Profile" and set it as a new value of MutableLiveData in your ViewModel with "postValue"
UPDATED
I've read your question more carefully. I think my answer above wouldn't give you what you expect (in case you expect Retrofit should update LiveData instantly like ROOM does)
So my thoughts:
You expect too much using LiveData+Retrofit. Just using them doesn't mean you'll get on-line updates of your data on your server. To achieve that you have to change mechanism of your interaction with your server, not just fix few lines in code you've shown.
There is mechanism LiveData+ROOM that works with local DB (Sqlite) in a way, that you expect from LiveData+Retrofit. But there is no magic there. Room is using mechanic, that built-in in Sqlite for notifying (triggering) when there are some changes in DB tables occur. But Retrofit doesn't implement similar mechanism with Rest Api and actually it's not its responsibility.
To achieve what you want you can look at several possibilities:
To use some Cloud Service API, that contains that built-in mechanism for notifying your device when data changes (Firebase, for example)
To implement some kind of periodic synchronisation of your app data with server. After this synchronisation you'll have all data on device and depending on where you put your data you could observe changes with LiveData+Room or FileObserver.
To simplify your case and refresh your data from the server at activity explicitly after click on Button "Refresh" on your activity. In that case you can implement steps that I wrote at first version of my answer.

Handle Dynamic Datatype in retrofit in android

I am using retrofit in my android application but my service sometimes return object data type and sometime array datatype. how can i handle this ? i used object in place of data type in android but am not able to use it properly.
Create an Interface,inside the Interface whenever your service returns a List do like this:
public Interface EndPointInterface{
#GET(Constants.URL_GET_PHARMACY_REPORT)
Call<List<PharmacyReport>> getPharmacyReport(#Query(Constants.PATIENT_ID) String patientId);
}
else if your service returns an Object proceed like this:
public Interface EndPointInterface{
#GET(Constants.URL_FETCH_STORE_INFO)
Call<Store> getStoreInfoByBeaconUUID(#Query(Constants.BEACON_UUID) String beaconUUID);
}

Retrofit, Otto and storing models in RAM

In our project we are forbidden to store data from server (confidentional information). But once downloaded and parsed models are used in multiple fragments. Data on server updates once a day and forced logout occurs in midnight. I want to store parced models in memory and remove it at exit from app.
In current implementation I have two single-instance classes - RestClient and DataStorage, which in constructors are registered to Otto bus. I instance them in Application class:
#Override
public void onCreate() {
super.onCreate();
new RestClient(this);
new DataStorage();
}
DataStorage for every model have metods:
#Subscribe
public void onModelComplete(Model model) {
this.model = model;
}
#Produce
public Model produceModel() {
return model;
}
Fragments send events throw bus to RestClient and receive results from RestClient or "produced" results from DataStorage (if data is received while app in background).
The problem is that when the user exits the application, data is not deleted because the application does not die. How to implement storing in memory with clearing data on exit without killing process?

Avoid getting data from the server if the same page is loaded for the 2nd time

My application works this way:
ListView---->onListItemClick---->detailspage---->backpressed---->goes
back to the list---->click the same item again---->same detailspage
loads again.
The details page gets a lot of data from a server and populate its views.
So, it takes 2-4 secs every time an item is clicked in the listview. I have seen apps where they wont load any data if the same page is called 2nd time.
How can I do that?
Currently in my app its like this:
onCreate call AsyncTask to get data and populate the view
nothing in onResume, onPause, onStart, onStop, onDestroyed
You can keep the data for that domain object in a singleton and then when you enter your details page there are two ways to go.
If for instance you had a list of Person class.
public class Person {
private String name;
private Image img;
...
}
Then you could have a PersonCache that was a singleton caching the data for the last Person selected in your list:
public class PersonCache {
private Person cachedPerson;
private static PersonCache instance;
private PersonCache(){
...
}
public PersonCache getInstance(){
if(instance == null){
instance = new PersonCache();
}
return instance;
}
public Person getCachedPerson(){
return cachedPerson;
}
public void setCachedPerson(Person p){
cachedPerson = p;
}
}
So in onCreate when you finish fetching your JSON data you create a Person object and call setCachedPerson.
If you know that the data in the details page won't have been updated:
In onCreate in details page you check if the object that has been selected is the same as the one cached in your singleton (if the objects have unique ids in your database you can look at those to check if it's the same).
If you don't know whether there's new data:
You can use the If-Modified-Since technique when making your GET request in your AsyncTask.
Basically what you do is add a header parameter
key: If-Modified-Since
value: Sat, 29 Oct 1994 19:43:31 GMT
If the server has no new data it can respond with 304 and send no response body but if it has new data it will respond with 200 and send the data just like normal.
Implementing this would require some implementation on the server side as well.
Here's some more info on the technique:
http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html (section 14.25)

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.

Categories

Resources