What Design-Pattern would be intelligent in which following Components(simplified) exists:
3 Components
- GUI
- Data fetcher
- Database
I have no Access to the Server in the Internet, its just a Data-Source. The Data which lays in the Internet is always the newer one, the local-Database is just a copy(cache) of the one in the Internet. The GUI can request and update of the local cache, the Service-like-component fetches then asynchronously the newest data, which could take awhile.
The GUI shows only Data from the local Database, which he can fetch synchronously.
So my Question is, what classes would you use for the maybe longterm running Service with Progressbar capabilities ? Are there better Designs for this kind of "problem"? Are there better practices?
On the Service Like Component:
An interface (method) to kick off the update process. Typically this service would return a jobId to indicate a job that is being processed in the background
Another interface (method) to get the status based on a particular jobId. The implementation of this service could return a status, percentCompleted or any other relevant info that will tell the caller the current status of the update process. The implementation of this method needs to be able to report an incremental progress (such as reporting the incremental store in the local storage) in order to make an accurate progress bar, otherwise the best the UI could do is show the spinning progress bar.
Note that if such incremental reporting is not possible to implement then the Update process should probably use a typical AsyncTask usage which execute update in the background and report to the user when it is finished. If this operation might take a while, you could implement the completion of the update via Android notification bar or push notification.
Assuming you have the interface to get the progress of the update, you could utilize the AsyncTask onProgressUpdate to report the progress of the update. The method is specifically designed for that.
Your steps are roughly as follow:
Execute the interface to update via one AsyncTask. Since your update is happening asynchronously, this particular task should return fairly quickly with a status reporting whether the execution is running successfully or fail because some exception along with the jobId that it is currently executing
Launch another AsyncTask that is pinging the status of the update and report the progress via onProgressUpdate. The AsyncTask roughly looks like
public class GetUpdateStatusAsyncTask extends AsyncTask {
protected Integer doInBackground(Integer... param) {
// this is the jobId that you get from the previous AsyncTask that you use to kick off the
// the update process
int jobId = param[0];
double percentCompleted = fetchStatus(jobId);
while(percentCompleted != 100.00) {
publishProgress(percentCompleted);
}
// return status code
return 1;
}
protected void onProgressUpdate(Double... progress) {
// set the progressBar here
}
protected void onPostExecute(Long result) {
// done, handle status code
}
private double fetchStatus(int jobId) {
double percentCompleted = 0.0;
// call interface to get the update based on the jobId
// return percentCompleted status or make a more complex object if you need
// more detail info
return percentCompleted;
}
}
Related
I am working with WorkManager Alpha 05.
I'm developing a Service that enqueues task on demand of other applications.
It has two methods:
createTask (Create a new task, given a name and a set of data, it returns and ID)
checkTaskStatus (The application asks the services given a ID, the status of the task)
The communication is done via bound services using messages. That means both client and services has the correct implementations to communicate information.
Method 1 is working fine.
I have problems with method 2.
WorkManager.getInstance().getStatusById(taskID)
.observe(LifecycleOwner, Observer {
status -> if (status !=null){
val myResult = status.state.toString()
statusString = myResult
Log.d("Task Status",myResult)
}
})
The observer is logging the status correctly, but I can't send back that message to the client. Is there a way to check the status in a sync way?
I don't really need to have the task attached to a LiveData.
Seems like SynchronousWorkManager was removed on October 11:
Removed WorkManager.synchronous() and WorkContinuation.synchronous() and all related methods. Added ListenableFuture as the return type of many methods in the API. This is a breaking API change.
How to use ListenableFuture:
You can now synchronously get and observe by using ListenableFutures. For example, WorkManager.enqueue() used to return void; it now returns a ListenableFuture. You can call ListenableFuture.addListener(Runnable, Executor) or ListenableFuture.get() to run code once the operation is complete.
More info can be found here.
The WorkManager instance has a synchronous method which returns the SynchronousWorkManager, This will give you a set of methods to perform synchronous operations. Take into account that this is meant to be used in a background thread.
My question is very simple, what is the best approach to work with Parse using the local store at the time I want to query the saved objects.
Is it better to trigger several queries to the local store directly on the main thread and avoid nesting a lot of anonymous classes or using a background thread?
It's important thing to notice is that this method is going to be called very frequently and the pattern will be repeated in several places with different queries. I'm evaluating both efficiency and code quality in readability. These methods will be called synchronously so we can assume the data will be consistent at any time.
As the objects are being saved locally I would expect the queries to be very fast in response. Here's a rough sample of how the code would look like in both cases.
Option one:
public void processBatches() {
ParseQuery<Batch> batchQuery = Batch.getQuery();
int batchCount = batchQuery.fromLocalDatastore().count();
List<Batch> batches = batchQuery.fromLocalDatastore().find();
for(Batch b : batches) {
// do whatever I need to do
}
}
Option two:
public void processBatches() {
ParseQuery<Batch> batchQuery = Batch.getQuery();
int batchCount = batchQuery.fromLocalDatastore().countInBackground(new CountCallback() {
#Override
public void done(int i, ParseException e) {
if (i > 0) {
batchQuery.findInBackground(new FindCallback<Batch>() {
#Override
public void done(List<Batch> list, ParseException e) {
for (Batch batch : list) {
// do whatever I need to do
}
}
});
}
}
});
}
Well since in option one you are blocking the UI thread, there could be a delay in the user's ability to interact with your application. This is not a very good option since even if it is for just a moment, users don't want to be waiting unless they know operations are happening. But, if you know that at any time there will be little to no delay, go ahead and do it.
Nevertheless, I argue that option two is going to be the best option. This is because, in general, all network operations should be performed in the background. Although in your case you are performing local datastore queries, suppose a user has gone to their application task manager and cleared the data (very rare this will happen) what happens now when you perform the find from local data store and processing of Batch objects? Well, the app crashes. Again, this is not a very good option for the usability for your application.
Choose the second option, and allow an AsyncThread to run the find() and count() query operations to the network if there is nothing found from local data store queries. Also, from the Parse documentation for find:
public Task<List<T>> findInBackground()
Retrieves a list of ParseObjects that satisfy this query from the source in a background thread.
This is preferable to using ParseQuery.find(), unless your code is already running in a background thread.
Returns:
A Task that will be resolved when the find has completed.
Parse's creators prefers that the users of their API use a background thread to perform operations.
It really depends.
Is the user triggering the update? If so then do it on the main thread because you don't want them waiting
If not, then is the data access a result of fetching data from the web (and hence you should already be on a background thread) so could probably just remain on the background thread
Also what happens in "// do whatever I need to do"? Is it an update to the UI or more background processing?
I'm currently developing an android app that communicates with some other device, that acts like a server. Basically to build the application's views, I first have to send a query via a TCP connection to the server to get the info. I (successfully) execute these queries with the help of an async task:
private class TCPQuery extends AsyncTask<String, String, String> {
#Override
protected String doInBackground(String... params) {
//connect the socket send the query and receive feedback
}
#Override
protected void onPostExecute(String result) {
//parse server feedback and build the view
}
}
This approach works fine when it comes to single queries that are made only a couple of times during the application's lifetime. What I have trouble implementing is the following:
a certain view in the application, contains seekbars. So basically, every change of the seekbar value (every time the onProgressChange method fires) must be sent to the server(no feedback this time), so it can keep track of the actual values.
How would you go about implementing this? Of course, no networking in android may be done on the main thread. But here establishing a connection, sending a message and closing the connection every time the value changes is not acceptable. Sliding the bar only a little already results in a dozen such calls in a split second.
I've tried approaching this problem by implementing a service. The service had its own socket to communicate with the server. I would connect the socket to the server and keep it open, so that I would be able to call the service's send method any time a seekbar change has been made. But that seemed to interfere with the other queries I mentioned before (the ones executed with async tasks). I couldn't connect one while the other was active. Now I'm not sure whether my service implementation was just bad, or if I am misunderstanding a crucial networking concept here.
I have thought of only sending the data onStopTrackingTouch, but that is not really what I am after. Any help would be very much appreciated!
Use the system clock to check when the last query has been sent, and don't send another until a certain time has elapsed.
You can change seekbar's value as you want, but the query will be sent only every X milliseconds.
static long sendInterval = 600; //milliseconds
#Override
public void onStartTrackingTouch(SeekBar seekBar) {
long nextSend = 0;
}
#Override
public void onProgressChanged(......) {
if (nextSend < uptimeMillis()) {
...send the query and parse feedback...
nextSend = uptimeMillis() + sendInterval ;
}
Start with nextSend = 0, so the first time the query will be sent immediatly.
Choose sendInterval value according to server's response time. Start with a high value and decrease until you see that all is working well.
If the query itself and the response are small (a few bytes) consider using UDP instead of TCP, it's faster and you can use lower values of sendInterval.
Other way to do it, different and maybe better:
since the response time may vary much depending on network traffic, query complexity and server load, you can use a boolean flag. Set it to False before sending the query, set it to True after parsing the response. Use it in an If statement:
#Override
public void onStartTrackingTouch(SeekBar seekBar) {
boolean readyForQuery = true;
}
#Override
public void onProgressChanged(......) {
if (readyForQuery) {
readyForQuery = false;
<...asyncronous send the query, parse feedback and set readyForQuery=true;...>
}
Consider also the worst case: when the server is down and will not respond at all to the query.
Take care to find a way to set the flag True after a reasonable amount of time and/or when the query code generates an exception, otherwise you won't get further responses even when the server goes up again.
I have problem when implementing AsyncTask. I have to rotate my phone in order to get a recent information. Below is my class:
GamerObject gamer;
….
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ip = "134.188.204.155";
// Set the name of the gamer
gamername = (TextView) findViewById(R.id.gamer_name);
// Set the gamerstatus:
gamerstatus = (TextView) findViewById(R.id.tvgamer_status_msg);
// set the job status
jobstatus = (TextView) findViewById(R.id.tvJob_status_msg);
new Operation().execute();
}
private class Operation extends AsyncTask<Void, Void, Void> {
#Override
protected Void doInBackground(Void... params) {
gamer= new GamerObject(ip);
gamer.UpdateAllData();
}
#Override
protected void onPostExecute(Void result) {
updateUI();
}
}
private void updateUI() {
gamer.updateAllData();
// Set the name of the gamer
TextView gamername = (TextView) findViewById(R.id.gamer_name );
gamername.setText(gamer.gamername);
gamername = gamer.gamername ;
// Set the gamer status:
…
// set the job status
…
}
Before I was using a Intent for Refresh the Interface, but now I want to try using AsyncTask so that it can compatible with Android version 4. Anybody knows how to get the information updated without rotating the phone ?
EDIT:
If I'm not wrong, Why my UI didn't refresh if there is new value, it is because new Operation().execute(); only use once in onCreate. AsyncTask will be executed if onCreate has been called, which means every time I rotate my phone, it will go to onCreate . Anybody knows how to keep the AsyncTask executed?
AsyncTask will only be executed once, so whenever system calls onCreate on your activity dueto some lifecycle event, asyncTask will be executed.
One, simple but naive approach, would be to create a new Thread and use Handler to update your UI. Some more information can be found eg. here and of course in Android doc.
Better approach, but more complicated would be to use Loader and LoaderCallback along with ContentProvider as #metter mentioned. This will require implementing you own ContentProvider and force you to add more "abstraction layers" to your app but will allow to separate network base code and ui code.
So this is as always tough decision to make either use "simple" but ugle solution with threads or "harder" but elegant solution with ContentProvier
I want to get the inofrmation updated automatically without have to rotating the phone.
Doing that requires some more work. Unfortunately, we can't see what your Async task actually does. If it is reading data from a database and you wan't your UI to be informed about any changes to the database, then your content resolver could call it's notifyChange and your Activity would listen to these changes and then call the async task again. For that, you would use a Content Observer. However, if your task is downloading data from the web, then there are two methods to get informed if the data online changed. One is called polling and means that you periodically connect and check the server. You should never do that on a mobile device due to limitations in battery, performance and data traffic. The other is called pushing and requires you to set up some infrastructure.
I hope that helps you.
I have severals URLs I need to get data from, this should happen in order, one by one. The amount of data returned by requesting those URLs is relatively big. I need to be able to reschedule particular downloads which failed.
What is the best way to go? Shall I use IntentService, Loaders or something else?
Additional note: I would need not only to download, but also post process the data (create tables in db, fill it with data, etc). So DownloadManger can't be of help here.
I would use an IntentService.
It has a number of advantages that are suitable for your needs, including being able to download the data without your application running and supporting automatic restart of the service using setIntentRedelivery().
You can set a number of identifiers for the particular job, you need to perform using Intent extras, and you can keep track of the progress using SharedPreferences - that way you can also resume the work if it's been cancelled previously.
The easiest way is probably to use the system DownloadManager http://developer.android.com/reference/android/app/DownloadManager.html
(answering from my phone, so please excuse the lack of formatting)
I would suggest a service for this. Having service resolves many problems
It would allow reporting of progress asynchronously to the application so you can enable or disable a specific gui in application based on the download status of data
It will allow you to continue the download even if the user switches to other application or closes the application.
Will allow you to establish independent communication with server to prioritize downloads without user interaction.
Try a WakefulIntentService for creating a long-running job that uses wakelocks to keep your task alive and running https://github.com/commonsguy/cwac-wakeful .
Also, if your whole app process is getting killed, you may want to look into persisting the task queue to disk, using something like Tape, from Square
I think the way to go is loading urls in an array, then starting an AsyncTask, returning a boolean to onPostExecute indicating if the operation has success or not. then, keeping a global int index, you can run the AsyncTask with the next index if success, or the same index otherwise. Here is a pseudocode
private int index=0;
//this array must be loaded with urls
private ArrayList<String> urlsArray;
new MyDownloaderAsyncTask().execute(urlsArray.get(index));
class MyDownloaderAsyncTask extends AsyncTask<String,String,Boolean>{
#Override
doInBackground(String... input){
//downlaod my data is the function which download data and return a boolean
return downloadMyData();
}
#Override
onPostExecute(Boolean result){
if(result)
new MyDownloaderAsyncTask().execute(urlsArray.get(++index));
else
new MyDownloaderAsyncTask().execute(urlsArray.get(index));
}
}
hope this help
I have just completed an open source library that can do exactly what you need. Using droidQuery, you can do something like this:
$.ajax(new AjaxOptions().url("http://www.example.com")
.type("GET")
.dataType("JSON")
.context(this)
.success(new Function() {
#Override
public void invoke($ droidQuery, Object... params) {
//since dataType is JSON, params[0] is a JSONObject
JSONObject obj = (JSONObject) params[0];
//TODO handle data
//TODO start the next ajax task
}
})
.error(new Function() {
#Override
public void invoke($ droidQuery, Object... params) {
AjaxError error = params[0];
//TODO adjust error.options before retry:
$.ajax(error.request, error.options);
}
}));
You can specify other data types, which will return different object types, such as JSONObject, String, Document, etc.
Similar to #Murtuza Kabul I'd say use a service, but it's a little complicated than that. We have a similar situation related to constant internet access and updates, although ours places greater focus on keeping the service running. I'll try to highlight the main features without drowning you in too much detail (and code is owned by the company ;) )
android.permission.RECEIVE_BOOT_COMPLETED permission and a BroadcastReceiver listening for android.intent.action.BOOT_COMPLETED to poke the service awake.
Don't link the service to the Activity, you want it running all the time. eg we call context.startService(new Intent(context.getApplicationContext(), OurService.class))
The service class is just a simple class which registers and calls an OurServiceHandler (as in our case we fire off repeated checks and the Handler manages the 'ticks')
We have an OurServiceRunnable which is a singleton which is checked and called by the Handler for each test. It protects against overlapping updates. It delegates to an OurServiceWorker to do the actual lifting.
Sounds heavy handed, but you want to ensure that the service is always running, always ticking (via the Handler) but only running a single check at a time. You're also going to run into database issue if you use the standard SqlLite DbHelper paradigm, as you can't open the DB on multiple threads and you definitely want the internet access off the main thread. Our hack was a java.util.concurrent.locks.ReentrantLock protecting access to the DB, but you could probably keep DB access on the UI thread and pass DB operations via the Handler.
Beyond this it's just a matter of keeping the downloads atomic in terms of "get task, download task, complete task" or enabling it to pick up from a failed state eg downloaded OK, attempt to complete.
You should take a look at the volley library :
http://www.javacodegeeks.com/2013/06/android-volley-library-example.html
There is also an interesting video of the author that took place at google io 2013 :
http://www.youtube.com/watch?v=yhv8l9F44qo
Mainly because it eases the process of managing a lot of these fastidious tasks that are connection checking, connection interruption, queue management, retry, resume, etc.
Quoting from the javacodegeeks "Advantages of using Volley :
Volley automatically schedule all network requests. It means that Volley will be taking care of all the network requests your app executes for fetching response or image from web.
Volley provides transparent disk and memory caching.
Volley provides powerful cancellation request API. It means that you can cancel a single request or you can set blocks or scopes of requests to cancel.
Volley provides powerful customization abilities.
Volley provides Debugging and tracing tools"
Update from dennisdrew :
For large file, better use a variant of volley which authorize using another http client implementation. This link gives more details :
The volley article about this modification :
http://ogrelab.ikratko.com/android-volley-examples-samples-and-demos/
The github file detail :
https://github.com/ogrebgr/android_volley_examples/blob/master/src/com/github/volley_examples/toolbox/ExtHttpClientStack.java
public class FetchDataFromDBThread implements Runnable {
/*
* Defines the code to run for this task.
*/
#Override
public void run() {
// Moves the current Thread into the background
android.os.Process
.setThreadPriority(android.os.Process.THREAD_PRIORITY_BACKGROUND);
FetchDataFromDB();
}
}