Sequential AsyncTasks where each following use result from previous - java

I thought a bit about this and I was wondering which could be the best pattern to accomplish my request.
Real case:
I have many web calls which must be sequential and where every call must use the result of the previous. This is the concept of what I mean:
Suppose to have this db structure:
User [id, server-side created info, infos]
House [id, server-side created info, user ref, infos]
Room [id, server-side created info, house ref, infos]
Suppose that with my app (offline) an user can create his user entity, N house entities and N room entities for each house.
Then, once done, the user is syncing data with server.
This is what should be done:
For each user, send user to webapi and read the server-side info.
For each house of the sent user, read the server-side info, send the house to webapi and read the server-side info.
For each room of the sent house, read the server-side info, send the room to webapi and read the server-side info.
This might be done this way:
Create an AtomicInteger that is the count of all sync to be done.
Init this value with the sum of users, houses, rooms.
void syncDone() {
if(totalSyncCounter.decrementAndGet() == 0){
//sync finished, prompt user
}
}
Create AsyncTasks
class SendUserToDbAsyncTask extends AsyncTask<User, Void, Integer> {
UserSavedCallback _callback;
public SendUserToDbAsyncTask(UserSavedCallback _callback) {
this._callback = _callback;
}
#Override
protected void onPreExecute() {
super.onPreExecute();
}
#Override
protected Integer doInBackground(User... users) {
//save user and return the server-side value
return serverSideInfo;
}
#Override
protected void onPostExecute(Integer res) {
super.onPostExecute(res);
_callback.onResult(res);
}
}
class SendHouseToDbAsyncTask extends AsyncTask<House, Void, Integer> {
HouseSavedCallback _callback;
public SendHouseToDbAsyncTask(HouseSavedCallback _callback) {
this._callback = _callback;
}
#Override
protected void onPreExecute() {
super.onPreExecute();
}
#Override
protected Integer doInBackground(House... houses) {
//save user and return the server-side value
return serverSideInfo;
}
#Override
protected void onPostExecute(Integer res) {
super.onPostExecute(res);
_callback.onResult(res);
}
}
class SendRoomToDbAsyncTask extends AsyncTask<User, Void, Integer> {
RoomSavedCallback _callback;
public SendRoomToDbAsyncTask(RoomSavedCallback _callback) {
this._callback = _callback;
}
#Override
protected void onPreExecute() {
super.onPreExecute();
}
#Override
protected Integer doInBackground(Room... rooms) {
//save user and return the server-side value
return serverSideInfo;
}
#Override
protected void onPostExecute(Integer res) {
super.onPostExecute(res);
_callback.onResult(res);
}
}
Logic:
void DoStuffs() {
mySyncCounter = [users + houses + rooms];
for (User user : users) {
new SendUserToDbAsyncTask(new UserSavedCallback() {
void onResult(){
syncDone();
for (House house : user.houses) {
new SendHouseToDbAsyncTask(new HouseSavedCallback() {
void onResult(){
syncDone();
for (Room room : house.rooms) {
new SendRoomToDbAsyncTask(new RoomSavedCallback() {
void onResult(){
syncDone();
}).execute(room);
}
}).execute(house);
}
}).execute(user);
}
}
Obiouvsly it is just an example hand-wrote here on SO. I just want to make you get the point. I know I can make a single callback for all, init it outside the method, just create a single asynctask for everything, etc... but I'm not looking for any suggestion about optimizing this specific code, I just want to know how to perform multiple sequential async operations where every following operation use the return of the previous.
What is the best way for performing this kind of operations?
Is there a "cleaner" way of doing it?
Thanks

If I understand correctly.
You could use ExecutorService to make calls to the sync server, and Semaphore to stop the thread as you like.
Only you will have to use AsyncTask anyway, because the calls would only fail with ExecutorService

Related

Java Android LiveData calls Room query dependent on other LiveData

UPDATE:::
I've updated the question to include demo other LiveData that were also required:
so we have userLD that we need the value of to get the goalWeeklyLD, and we need the goalWeeklyLD value to get the remaining 4 LiveData values as they come from Room querys that use goalWeekly.dateproperties in the query
:::::
I've hit a problem where I have a fragment that has to populate LiveData that uses a query dependent on another LiveData value.
how can i get my live data to work correctly when it is dependent on other results?
Without using The Transitions.map() the view model throws an error because the values of the other live data are still null.
with the Transitions.map() in the view model the activities observer throws an error because the LiveData is still null.
I could possibly cheat my way past this by using a horrendously big nested query to return all i need in one custom DTO. but i'd rather understand whats going on here and how to handle this sort of situation properly.
Hopefully some code will make this clear
The Activity:
public class SomeFragment extends Fragment {
public static SomeFragment newInstance() {
return new SomeFragment();
}
#Override
public void onActivityCreated(#Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
someViewModel = ViewModelProviders.of(this).get(SomeViewModel.class);
//getting user details from previous activity
Intent intent = getActivity().getIntent();
if (intent != null){
if (intent.hasExtra(USER_ID)){
user = new User(intent.getStringExtra(USERNAME));
user.setId(intent.getLongExtra(USER_ID,0));
someViewModel.setUserLD(user);
}
}
someViewModel.getUserLD().observe(this, new Observer<User>() {
#Override
public void onChanged(#Nullable User userVal) {
user = userVal;
}
});
someViewModel.getGoalWeeklyLD().observe(this, new Observer<User>() {
#Override
public void onChanged(#Nullable User userVal) {
user = userVal;
}
});
//the below Observer calls throw an error because LiveData is null. makes sense.
//but how can i say "don't try and observe these until the transition.map has ran (because then it wont be null after if my understanding is right)" or something to that effect
someViewModel.getFirstLD(user.getId()).observe(this, new Observer<XObject>() {
#Override
public void onChanged(#Nullable Grades avgSportGradeVal) {
//Update UI
}
});
someViewModel.getSecondLD(user.getId()).observe(this, new Observer<XObject>() {
#Override
public void onChanged(#Nullable Grades avgBoulderGradeVal) {
// Update UI
}
});
someViewModel.getThriLD(user.getId()).observe(this, new Observer<XObject>() {
#Override
public void onChanged(#Nullable Grades avgBoulderGradeVal) {
// Update UI
}
});
someViewModel.getFourthLD(user.getId()).observe(this, new Observer<XObject>() {
#Override
public void onChanged(#Nullable Grades avgBoulderGradeVal) {
// Update UI
}
});
}}
The View Model:
public class SomeViewModel extends AndroidViewModel {
DaoRepository daoRepository;
MutableLiveData<User> userLD;
LiveData<XObject> firstLD;
LiveData<XObject> secondLD;
LiveData<XObject> thirdLD;
LiveData<XObject> fourthLD;
public MutableLiveData<User> getUserLD() {
return userLD;
}
public void setUserLD(User user){
userLD.setValue(user);
}
public LiveData<XObject> getFirstLD(long userId) {
return goalWeeklyLD;
}
public LiveData<XObject> getSecondLD(long userId) {
return goalWeeklyLD;
}
public LiveData<XObject> getThirdLD(long userId) {
return goalWeeklyLD;
}
public LiveData<XObject> getForthLD(long userId) {
return goalWeeklyLD;
}
public SomeViewModel(#NonNull Application application) {
super(application);
daoRepository = new DaoRepository(application);
userLD = new MutableLiveData<>();
//so the first LiveData waits for the user to be populated before getting its LiveData becasue we need the userId for our Room query to run
firstLD = Transformations.map(userLD, user -> daoRepository.getMostRecentGoalWeekly(user.getId()).getValue());
//the remaining live data uses values from the first...
setupOtherTransformMaps(userLD.getValue())
}
public void setupOtherTransformMaps(long userId) {
//the secondLD, thirdLD and fourthLD all depends on values from the first (in runs a query that uses its dateExpired)
secondLD = Transformations.map(firstLD, first ->
daoRepository.getAvgGradeRouteInPeriod(userId, first.getDateCreated(),first.getDateExpires()).getValue());
thirdLD = Transformations.map(firstLD, first ->
daoRepository.getAvgGradeRouteInPeriod(userId, first.getDateCreated(),first.getDateExpires()).getValue());
fourthLD = Transformations.map(firstLD, first ->
daoRepository.getAvgGradeRouteInPeriod(userId, first.getDateCreated(),first.getDateExpires()).getValue());
}}
Thankfully Google was smart and created a component which lets you combine variable number of LiveData into a single LiveData, and only emit events when you choose to do so!
This is called MediatorLiveData.
In your case though, you only need to channel 1 LiveData (userLD) into 1 another LiveData, that will emit each time userLd has a new value.
So you can use a predefined MediatorLiveData that does exactly this, specifically Transformations.switchMap.
firstLD = Transformations.switchMap(userLD, user -> daoRepository.getMostRecentGoalWeekly(user.getId()));
EDIT: Yup, you seem to need to expose these LiveData separately from one another, but they all depend on the first query to execute.
So you need to replace Transformations.map { ...getValue() with Transformations.switchMap and you'll be good to go.
public SomeViewModel(#NonNull Application application) {
super(application);
CustomApplication app = (CustomApplication) application;
daoRepository = app.daoRepository();
userLD = new MutableLiveData<>();
firstLD = Transformations.switchMap(userLD, user -> daoRepository.getMostRecentGoalWeekly(user.getId()));
secondLD = Transformations.switchMap(firstLD, first ->
daoRepository.getAvgGradeRouteInPeriod(userId, first.getDateCreated(),first.getDateExpires()));
thirdLD = Transformations.switchMap(firstLD, first ->
daoRepository.getAvgGradeRouteInPeriod(userId, first.getDateCreated(),first.getDateExpires()));
fourthLD = Transformations.switchMap(firstLD, first ->
daoRepository.getAvgGradeRouteInPeriod(userId, first.getDateCreated(),first.getDateExpires()));
}

RxJava and list of `Single` observable

I'm new in RxJava so I still have many doubts.
I'm creating this:
#Override
public Single<Result> saveUser(final User user) {
return Single.create(new Single.OnSubscribe<Result>() {
#Override
public void call(final SingleSubscriber<? super Result> singleSubscriber) {
if(user.isValid()){
save(user);
//Set result ok
singleSubscriber.onSuccess(result);
} else {
//This sets an error.
singleSubscriber.onError(error);
}
}
});
}
Depending of the success of the operation, the Single emits it's data and who is subscribed receives the data.
The problem now is that at some point I need to store a list of users. Something like:
public void saveUsers(List<User> listOfUsers){
for (User user : listOfUsers) {
saveUser(user);
}
}
How can I create an Single so I can be subscribed to the initial Single
I would rather create flatmap out of observable list.
public void saveUsers(List<User> listOfUsers){
Observable.from(listOfUsers).flatMap((User user)->{
if(user.isValid()){
save(user);
//Set result ok
return Observable.just(result);
}else
return Observable.error(new RuntimeException("..."));
}).retry(2);
}
If you make your saveUsers method blocking, call Observable#toBlocking.

Trying to get a value out of the OnPostExecute

i've got something blowing my mind all day long.
The question is, I have an AsyncTask that returns me an User Object, with all its attributes. I know that I have to return from the doInBackground method and receive in the OnPostExecute to work with my data. The fact is that i want to extract that user out of the AsyncTask method because i have to work with it in my main thread.
My AsyncTask class is placed in the MainActivity.class.
i've heard about using interfaces to get my value back but i can't understand the way to do it.
public class FetchUserDataAsyncTask extends AsyncTask<Void, Void, User> {
RequestHandler rh = new RequestHandler(); //this is the class i use to do de server conection
User user;
User ret_user;
public FetchUserDataAsyncTask(User user){
this.user = user;
}
#Override
protected void onPostExecute(User user) {
super.onPostExecute(user);
//I WANT THIS USER IN MY MAIN THREAD, TO WORK WITH ITS ATTRIBUTES
}
#Override
protected User doInBackground(Void... params) {
try {
HashMap<String, String> dataToSend = new HashMap<>();
dataToSend.put("username", user.username);
dataToSend.put("password", user.password);
ret_user = rh.sendGetRequest("myadresstophp.php", dataToSend);
} catch (Exception e) {
e.printStackTrace();
}
return ret_user;
}
and the call (when user press the log in button), a few lines above.
new FetchUserDataAsyncTask(userk).execute();
I was hoping to do something like that: (i know its not the way to do it)
User user = new FetchUserDataAsyncTask(userk).execute();
Thank you all, have a nice day!
At first declare an Interface in your project somewhere having the required functions, then implement that interface in the (AsyncTask)calling class ,then declare one Interface object in the AsyncTask. Create the constructor of AsyncTask as follows:
public FetchUserDataAsyncTask(User user,InterfaceClass object){
this.user = user;
this.interfaceObject=object;
}
And then do the following in onPostExecute:
#Override
protected void onPostExecute(User user) {
super.onPostExecute(user);
interfaceObject.function(user); //call the function of the calling class
}
You can create an interface, pass it toAsyncTask (in constructor), and then call method in onPostExecute()
For example:
Your interface:
public interface OnTaskCompleted{
void onTaskCompleted();
}
Your Activity:
public class YourActivity implements OnTaskCompleted{
// your Activity
}
And your AsyncTask:
public class YourTask extends AsyncTask<Object,Object,Object>{
//change Object to required type
private OnTaskCompleted listener;
public YourTask(OnTaskCompleted
listener){
this.listener=listener;
} // required methods
protected void onPostExecute(Object
o){
// your stuff
listener.onTaskCompleted();
}
}
Here you go, this is in general how it would work. This is based on this answer and modified to be specific to your existing code. Basically assign your member variable based on what the user entered, and compare that value to the one you get from the server:
public class MyClass extends Activity {
//member variable for the username the user enters:
User userEnteredUser;
public class FetchUserDataAsyncTask extends AsyncTask<Void, Void, User> {
RequestHandler rh = new RequestHandler(); //this is the class i use to do de server conection
User user;
User ret_user;
public FetchUserDataAsyncTask(User user){
this.user = user;
}
#Override
protected void onPostExecute(User user) {
super.onPostExecute(user);
//I WANT THIS USER IN MY MAIN THREAD, TO WORK WITH ITS ATTRIBUTES
processValue(user); //added
}
#Override
protected User doInBackground(Void... params) {
try {
HashMap<String, String> dataToSend = new HashMap<>();
dataToSend.put("username", user.username);
dataToSend.put("password", user.password);
ret_user = rh.sendGetRequest("myadresstophp.php", dataToSend);
} catch (Exception e) {
e.printStackTrace();
}
return ret_user;
}
}
private void getValue()
{
EditText et = (EditText) findViewById(R.id.username);
userEnteredUser.username = et.getText().toString(); //something like this.... replace with your syntax
new FetchUserDataAsyncTask(userEnteredUser).execute();
}
private void processValue(User userFromServer)
{
if (userEnteredUser.equals(userFromServer)) {
//Users match! Yay!
}
}
}
onPostExecute runs on the UI thread by default (main thread). Do what ever you need to be do on the main thread in onPostExecute
I suggest you read up more from the link provided below for a better understanding on AsyncTask
http://developer.android.com/reference/android/os/AsyncTask.html
I'm uncertain what you mean by "main thread". If you mean "UI Thread", which is the primary thread used to execute in your app, then Shashank's reply is correct.
However, I suspect from the context of the question that you actually mean that you want the results returned to the code that initiated the AsyncTask. Let's call that the "invoking object" In that case, what I would do is define a callback in your invoking object whose only purpose is to receive the result of this AsyncTank. You could call it onUserDataFetched(User user).
So, to do this using Interfaces, you could define an Interface that contains a single method:
public interface FetchUserDataListener {
public void onUserDataFetched(User user);
}
Then make sure that your InvokingObject implements that interface, and pass your InvokingObject (which implements FetchUserData, and thus can be considered that object type) to your AsyncTask when you instantiate it.
So, you invoking object code would look like this:
public class InvokingObject implements FetchUserData {
public void someMethodToInvokeFetchUserData() {
//....
new FetchUserDataAsyncTask(userk, this).execute();
//....
}
#Override
public void onUserDataFetched(User user) {
//Compare the result to the data in your invoking object
}
}
Then pass that callback to your AsyncTask when you construct it:
And your AsyncTask Code would look like this:
public class FetchUserDataAsyncTask extends AsyncTask<Void, Void, User> {
FetchUserDataListener mFetchUserDataListener;
public FetchUserDataAsyncTask(User user, FetchUserDataListener listner){
this.user = user;
mFetchUserDataListener = listener
}
//...
#Override
protected void onPostExecute(User user) {
super.onPostExecute(user);
listener.onUserDataFetched(user)
}
//...
}

Setting Singleton property value in Firebase Listener

I'm currently testing out Firebase along with a Singleton model I plan to use to access during the lifecycle of the whole app. I'm now stuck with something that seems really trivial but I can't figure it out for the life of me. I have a sample of the model I use: Bookmarks in firebase.
public class BookSingleton {
private static BookSingleton model;
private ArrayList<BookMark> bookmarks = new ArrayList<BookMark>();
public static BookSingleton getModel()
{
if (model == null)
{
throw new IllegalStateException("The model has not been initialised yet.");
}
return model;
}
public ArrayList<Bookmark> theBookmarkList()
{
return this.bookmarks;
}
public void setBookmarks(ArrayList<Bookmark> bookmarks){
this.bookmarks = bookmarks;
}
public void loadModelWithDataFromFirebase(){
Firebase db = new Firebase(//url);
Firebase bookmarksRef = fb.child(//access correct child);
final ArrayList<Bookmark> loadedBookmarks = new ArrayList<Bookmark>();
bookmarksRef.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
//getting all properties from firebase...
Bookmark bookmark = new Bookmark(//properties here);
loadedBookmarks.add(bookmark);
}
}
//bookmarks still exist here at this point
setBookmarks(loadedBookmarks);
}
#Override
public void onCancelled(FirebaseError firebaseError) {
}
});
//by now loadedBookmarks is empty
//this is probably the issue?
//even without this line bookmarks is still not set in mainactivity
setBookmarks(loadedBookmarks);
}
Now when I start the mainActivity with the instance of the Singleton set I get a null error because clearly the function I wrote to load the model data from firebase sets nothing.
Something like this:
MainActivity
public class MainActivity extends AppCompatActivity {
private BookSingleton theModel;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Load the model
theModel = BookSingleton.getModel(this);
//manually setting this works
// ArrayList<Book> bookSamples = new ArrayList<Book>;
// bookSamples.add(aBookSample);
theModel.loadModelWithSampleData(bookSamples);
//should have set the singleton model property Bookmarks to the results from firebase
theModel.loadModelWithDataFromFirebase();
//returns 0
Log.d(TAG, "" + theModel.theBookmarkList().size());
setContentView(R.layout.activity_main);
//......rest of code
How can I make this work?
Firebase loads and synchronizes data asynchronously. So your loadModelWithDataFromFirebase() doesn't wait for the loading to finish, it just starts loading the data from the database. By the time your loadModelWithDataFromFirebase() function returns, the loading hasn't finished yet.
You can easily test this for yourself with some well-placed log statements:
public void loadModelWithDataFromFirebase(){
Firebase db = new Firebase(//url);
Firebase bookmarksRef = fb.child(//access correct child);
Log.v("Async101", "Start loading bookmarks");
final ArrayList<Bookmark> loadedBookmarks = new ArrayList<Bookmark>();
bookmarksRef.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
Log.v("Async101", "Done loading bookmarks");
//getting all properties from firebase...
Bookmark bookmark = new Bookmark(//properties here);
loadedBookmarks.add(bookmark);
}
#Override
public void onCancelled(FirebaseError error) { throw error.toException(); }
});
Log.v("Async101", "Returning loaded bookmarks");
setBookmarks(loadedBookmarks);
}
Contrary to what you likely expect, the order of the log statements will be:
Start loading bookmarks
Returning loaded bookmarks
Done loading bookmarks
You have two choice for dealing with the asynchronous nature of this loading:
squash the asynchronous bug (usually accompanied by muttering of phrases like: "it was a mistake, these people don't know what they're doing")
embrace the asynchronous beast (usually accompanied by quite some hours of cursing, but after a while by peace and better behaved applications)
Take the blue pill - make the asynchronous call behave synchronously
If you feel like picking the first option, a well placed synchronization primitive will do the trick:
public void loadModelWithDataFromFirebase() throws InterruptedException {
Firebase db = new Firebase(//url);
Firebase bookmarksRef = fb.child(//access correct child);
Semaphore semaphore = new Semaphore(0);
final ArrayList<Bookmark> loadedBookmarks = new ArrayList<Bookmark>();
bookmarksRef.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
Bookmark bookmark = new Bookmark(//properties here);
loadedBookmarks.add(bookmark);
semaphore.release();
}
#Override
public void onCancelled(FirebaseError error) { throw error.toException(); }
});
semaphore.acquire();
setBookmarks(loadedBookmarks);
}
Update (20160303): when I just tested this on Android, it blocked my app. It works on a regular JVM fine, but Android is more finicky when it comes to threading. Feel free to try and make it work... or
Take the red pill - deal with the asynchronous nature of data synchronization in Firebase
If you instead choose to embrace asynchronous programming, you should rethink your application's logic.
You currently have "First load the bookmarks. Then load the sample data. And then load even more."
With an asynchronous loading model, you should think like "Whenever the bookmarks have loaded, I want to load the sample data. Whenever the sample data has loaded, I want to load even more."
The bonus of thinking this way is that it also works when the data may be constantly changing and thus synchronized multiple times: "Whenever the bookmarks change, I want to also load the sample data. Whenever the sample data changes, I want to load even more."
In code, this leads to nested calls or event chains:
public void synchronizeBookmarks(){
Firebase db = new Firebase(//url);
Firebase bookmarksRef = fb.child(//access correct child);
final ArrayList<Bookmark> loadedBookmarks = new ArrayList<Bookmark>();
bookmarksRef.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
Bookmark bookmark = new Bookmark(//properties here);
loadedBookmarks.add(bookmark);
setBookmarks(loadedBookmarks);
loadSampleData();
}
#Override
public void onCancelled(FirebaseError error) { throw error.toException(); }
});
}
In the above code we don't just wait for a single value event, we instead deal with all of them. This means that whenever the bookmarks are changed, the onDataChange is executed and we (re)load the sample data (or whatever other action fits your application's needs).
To make the code more reusable, you may want to define your own callback interface, instead of calling the precise code in onDataChange. Have a look at this answer for a good example of that.
TL;DR: Embrace Firebase Asynchronicity
As I mentioned in another post, you can deal with the asynchronous nature of Firebase using promises. It would be like this:
public Task<List<Data>> synchronizeBookmarks(List<Bookmark> bookmarks) {
return Tasks.<Void>forResult(null)
.then(new GetBook())
.then(new AppendBookmark(bookmarks))
.then(new LoadData())
}
public void synchronizeBookmarkWithListener() {
synchronizeBookmarks()
.addOnSuccessListener(this)
.addOnFailureListener(this);
}
com.google.android.gms.tasks
Google API for Android provides a task framework (just like Parse did with Bolts), which is similar to JavaScript promises concept.
First you create a Task for downloading the bookmark from Firebase:
class GetBook implements Continuation<Void, Task<Bookmark>> {
#Override
public Task<Bookmark> then(Task<Void> task) {
TaskCompletionSource<Bookmark> tcs = new TaskCompletionSource();
Firebase db = new Firebase("url");
Firebase bookmarksRef = db.child("//access correct child");
bookmarksRef.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
tcs.setResult(dataSnapshot.getValue(Bookmark.class));
}
});
tcs.getTask();
}
}
Now that you got the idea, supose that setBookmarks and loadSampleData are also asynchronous. You also can create them as Continuation tasks (just like the previous one) that will run in sequence:
class AppendBookmark(List<Bookmark> bookmarks) implements
Continuation<List<Bookmark>, Task<Bookmark> {
final List<Bookmark> bookmarks;
LoadBookmarks(List<Bookmark> bookmarks) {
this.bookmark = bookmark;
}
#Override
Task<List<Bookmark>> then(Task<Bookmark> task) {
TaskCompletionSource<List<Bookmark>> tcs = new TaskCompletionSource();
bookmarks.add(task.getResult());
tcs.setResult(this.bookmarks);
return tcs.getTask();
}
}
class LoadSampleData implements Continuation<List<Bookmark>, List<Data>> {
#Override
public Task<List<Data>> then(Task<List<Bookmark>> task) {
// ...
}
}
You have to initialize your Singleton when the class is loaded.
Put this on your code:
private static BookSingleton model = new BookSingleton();
private BookSingleton() {
}
public static BookSingleton getModel() { return model == null ? new BookSingleton() : model;
}

Android, get data from web and update UI (Multi-threading and MVC design pattern)

Currently developing a weather application . I wanna strictly observe 2 rules:
MVC Design pattern
Multithreading when I deals with network.
The problem is combining these things into a whole, here are parts of my code:
Weather Class (represent a Weather object) :
public class Weather {
private int mTimeInSeconds;
private String mTimeZone;
private int mTemperature;
private String mSummary;
public Weather(int timeInSeconds, String timeZone, int temperature, String summary) {
mTimeInSeconds = timeInSeconds;
mTimeZone = timeZone;
mTemperature = temperature;
mSummary = summary;
}
public String getTime() {
SimpleDateFormat formatter = new SimpleDateFormat("h:mm a");
formatter.setTimeZone(TimeZone.getTimeZone(mTimeZone));
Date dateTime = new Date(mTimeInSeconds * 1000);
String timeAsString = formatter.format(dateTime);
return timeAsString;
}
public int getTemperature() {
return mTemperature;
}
public String getSummary() {
return mSummary;
}
}
Worker Class (do all "dirty" work):
public class Worker {
private final OkHttpClient mClient = new OkHttpClient();
private String apiKey = "Secret!11";
private double latitude = 37.8267;
private double longitude = -122.423;
private String forecastUrl = "https://api.forecast.io/forecast/"
+apiKey
+"/"
+latitude
+","
+longitude;
public void getCurrentWeather() throws Exception {
final Request request = new Request.Builder()
.url(forecastUrl)
.build();
mClient.newCall(request).enqueue(new Callback() {
#Override
public void onFailure(Request request, IOException e) {
Log.v(MainActivity.TAG, e.getMessage());
}
#Override
public void onResponse(Response response) throws IOException {
try {
JSONObject weatherData = new JSONObject(response.body().string());
JSONObject currentlyWeather = weatherData.getJSONObject("currently");
Log.v(MainActivity.TAG, currentlyWeather.toString());
} catch (JSONException e) {
Log.v(MainActivity.TAG, e.getMessage());
}
}
});
}
}
Based on my understanding of MVC I put all data and logic around that data in Model (Worker and Weather classes). I wanna to achieve something like this in
MainActivity.java:
...
Worker mWorker = new Worker();
Weather mWeather = mWorker.getWeatherData();
...
2 questions:
Is this the correct design of MVC? (I mean, that all code which somehow work with data separated from the controller which only update view's)
If yes, how I can implement this? I need to return Weather object
from Worker, but I can't do this because it's happen on separate thread, I wanna return Weather object to main thread, but have no idea how to implement this.
As far as I know, your Model is your Weather.java class, your MainActivity (and additional added Activities) are your Controller and
finally your layout.xml files are your View. So AFAIK yes,
this is correctly implemented followed the MVC pattern.
It sounds as your Worker.java class should be of an AsyncTask
implementation.
An example using AsyncTask (This is just a stub implementation, see the links below to see fully working examples):
private class WorkerTask extends AsyncTask<String, Void, String> {
#Override
protected String doInBackground(String... params) {
//Network calls go here. This will be done in the background,
//in a separate thread
//Finally, you return your result here to onPostExecute
return result;
}
#Override
protected void onPostExecute(String result) {
//This method runs on the main UI thread. Here you update your UI o return data to your caller class.
}
#Override
protected void onPreExecute() {
//Called before execution
}
#Override
protected void onProgressUpdate(Void... values) {
//If you want to update your UI with your current progress, that code goes here
}
}
Finally, you will execute your AsyncTask like this:
new WorkerTask().execute("your_url");
More examples using AsyncTask here, here and here.
Official documentation is found here.
Regarding AsyncTask and the MVC design pattern
A downside of this is that the AsyncTask has to be implemented in your MainActivity (or wherever you want to make use of it) which contradicts the MVC design.
A solution to this would be to create a "HTTP caller class" which does the network calls, and which is called in your AsyncTask doInBackground method.
Using this approach, you would be good to go. You would have your controller (MainActivity) executing the AsyncTask (but not doing any actual network calls in MainActivity) creating a new HttpCallerClass, then calling a method which handles the networking. Hence, MVC is preserved.

Categories

Resources