Android GridView and threading wait - android

I have a method playSoE() that performs an operation on each view and updates the ArrayAdapter and thus GridView after each operation. I want to do is have the code wait for 1 second after the update is done, and then perform the operation on the next view. For example:
public class MainActivity extends Activity{
public void playSoE(){
for(int i = 0; i < primesLE; i++) //iterates to each view
mAdapter.setPositionColor(index, 0xffff0000 + 0x100 * index); //operation performed on view
mAdapter.notifyDataSetChanged(); //updates adapter/GridView
//Code that waits for one second here
}
}
I have tried many threading APIs but none of them seem to work, they've either froze up the application for primesLE seconds and shown all the operations at the end, skip the second wait and just performed all the operations at once, or gotten an Exception related to concurrency.

Assuming the update itself is quick, you should not be creating additional threads at all. Create a Handler and use postDelayed(...) to time your updates in the UI thread.

You may have to take a look at AsyncTask.
Put the wait code at the doInBackground() and then the following that affects the visual on the onPostExecute()
All you do on doInBackground() will not freeze your application.

Related

Why setText() after sleep doesn't work? Inside android how message queue works?

I know the 2 rules that UI thread shouldn't blocked and udating ui in only UI thread.
So I know roughly why using Thread.sleep() with setText() doesn't work. (Because call Thread.sleep() blocks the UI thread !!)
But...why??
Imagine below code, I clicked a button to start timer to represent number every 50 milli seconds.
public void onTimerClicked(View v){
TextView tv = (TextView) findViewById( .. )
for( int i = 1; i <= 10; i++ ){
Thread.sleep(50) // in milil seconds
tv.setText(String.valueOf(i));
}
}
I already know when I click a button, after I get some freezing time only displays '10' on a text view. But why?? If UI thread encounter tv.setText(..) , it doesn't work for updating UI immediately?? or queueing the task( updating text view UI ) to message queue on main thread?? Then what is the criteria for queueing to message queue instead running the code immediately when it faces.
also, if setting text view with numbers are all queued, then after all sleeping is end. Does UI update all the task very fast?? is it reason that I can only last update ??
I am really curious about how UI thread works for dealing with UI datating internally.. but there are rarely information or explanation about this.
please let me understand !!
Thanks for reading

Android: Output of thread on UI after it finishes its execution

Basically I have 2 tabs.
In which first tab contains the data, and the second one contains some summary kind of data based on first tab's data.
In the first tab there is list of events. Whenever the there is an add/update/delete operation on events the summary needs to recalculated. The recalculated summary creates the list of rows in the database. And from those rows the summary tab is displayed.
So the problem is summary calculation takes long amount of time. So it is blocking the UI thread. So I moved the calculations in the thread for smooth UI operations.
Now as summary calculation is running in thread, I don't know when the calculation is complete and now I need to update the summary tab data and showing something while calculation is going on.
The current setup which I have is something like this:
My DatabaseHelper Class:
add() {
...
// Do Add operation
...
reCalculateSummary();
}
remove() {
...
// Do remove operation
...
reCalculateSummary();
}
reCalculateSummary() {
new Thread(new Runnable() {
#Override
public void run() {
...
// calculate summary (may call another method)
...
// Add calculated summary to DB
}
}).start();
}
While the reCalculateSummary() is running, the summary to tab should show loading message.
So How can I achieve this whole situation working?
How can I know if thread has completed its execution? So that I can update summary tab data by retrieving new db values of summary data.
So here is some confusing situation. Please feel free to ask if you have any doubts in understanding the situation.
Thanks in advance!
Use an event bus, so you can update the UI, and avoid the limitation on AsyncTasks.
You can create the event "recalculateSummary", and just before job start (think of it as onPreExecute), update the UI with something that shows the user that the information on screen is not up-to-date, and update results when job is done.
Examples on event buses are Otto, EventBus, AutoBus ...
You can use an AsyncTask.
Do the processing in the doInBackground(..) method, and update the UI inside onPostExcecute(..) method

AsyncTask queue and its entire progress

I haven't started coding yet. I'm trying to figure out which libraries that is out there and what the API can offer first.
I need to download a lot of resources before starting my app. Basically I'm going to have a "SplashSreen" and a progressbar that shows which file being downloaded and the progressbar for the entire progress of downloading the x-number of resources.
After looking through some discussions and tutorials on the subject, it seems to me that AsyncTask will do the job. But it's only possible to show the current progress for the current "object" being downloaded, and not for the entire queue.
Could anyone help me out here, give me some tutorials or something to dig into?
TBH; I'm not even sure if AsyncTask is what I'm looking for in this scenario. Basically I need something that can download resources, put in a queue, and let me know the progress of the entire queue, and which resource (name of it) being download at a certain time.
Thanks!
Of course an AsyncTask can do the job. Just put all your files in a asyncTask which display the progress.
class InitTask extends AsyncTask<Void, Integer, Boolean> {
private List<String> mNbFiles;
public InitTask(List<String> filesURL)
{
super();
mFilesURL = filesURL;
}
#Override
protected void onPreExecute() {
//show and init your progress bar here...
super.onPreExecute();
}
#Override
protected Boolean doInBackground(Void... params) {
for(int i = 0 ; i < mNbFiles.size() ; ++i){
//download one file ...
publishProgress(i);
}
}
}
I hope this will help you. Comment if you have any question.
AsyncTask isn't what you should be using for long running downloads. AsyncTasks are really about the UI, and it's important to know that the user navigating away or even rotating their screen will kill the download.
You should use a service to perform the download, so put all of your download code into an IntentService and update some kind of persistant storage to indicate progress (a couple variables in shared preferences would do fine (on to indicate current state, the other to indicate progress)).
You can then use your activity to fire up the service, and then monitor the progress of the downloads via the persistant storage or some callback passed via the intent (persistant storage is probably easiest).
AsyncTask is exactly what you are looking for, you can queue depending on the API an amount of tasks, im going to check the Asynctask implementation if there is a getter for the queue size, but if not you can make a list of asyncTask and update when its finished.
If you queue 15 tasks you can set your progress bar to 15 max and on each onPostExecute increment the progressBar :)
I will check the queue later and edit my answer.
Regards.
AsyncTask is very much what you're looking for. That's not really true that you can only update for the current Object. What that means is that you can update for the current AsyncTask.
Below is the onProgressUpdate method.
protected void onProgressUpdate(Integer... values) {
super.onProgressUpdate(values);
Log.i(logTag, "onProgressUpdate: " + String.valueOf(values[0]));
View view = getView();
if(view != null)
((ProgressBar)view.findViewById(R.id.progressBar)).setProgress(values[0]);
}
You see it takes any number of integers as the arguments. In your publishProgress method, you could call onProgressUpdate(itemProgress, totalProgress) and then update 2 different progress bars accordingly in onProgressUpdate.
You do all the downloading in doInBackground and each time you want to update the progress bar/bars, call publishProgress which passes your progress ints to onProgressUpdate.
In your AsyncTask doInBackground() method put your code that downloads all your objects and post updates of the progress bar using a handler. Take a look at this example:
http://developer.android.com/reference/android/widget/ProgressBar.html
The following code goes inside doInBackground():
while (mProgressStatus < 100) {
mProgressStatus = doWork();
// Update the progress bar
mHandler.post(new Runnable() {
public void run() {
mProgress.setProgress(mProgressStatus);
}
});
}

notifyDataSetChanged only refreshes GridView once, despite being called multiple times

I will attempt to summarize my code as follows:
I have a TileAdapter which extends from BaseAdapter to populate a GridView
My TileAdapter contains an array of Tiles that is used to maintain state
My TileUpdate class performs the following operations:
Changes Tile A's colour to green
Calls TileAdapter.notifyDataSetChanged()
Changes Tile B's colour to yellow
Calls TileAdapter.notifyDataSetChanged()
Changes Tile B's colour to red
Calls TileAdapter.notifyDataSetChanged()
I would have expected to see my GridView refresh 3 times, once for each call to notifyDataSetChanged().
However, I only see it refresh after step 6, when and never see Tile B turn yellow.
What is happening here? I presume there is some part of the API I'm not aware of.
Thanks
Update
I still have not been successful in implementing this, even after studying the Handler approaches described below.
So in my UI thread I've created a new GridOperationQueue class which extends from Thread:
public class GridOperationQueue extends Thread {
private Handler handler;
#Override
public void run() {
Looper.prepare();
handler = new Handler();
Looper.loop();
}
public void addTaskToQueue(final GridUpdateTask task)
{
Log.d(this.getClass().toString(), "Current thread is ID " + Thread.currentThread().getId());
handler.post(new Runnable()
{
#Override
public void run() {
Log.d(this.getClass().toString(), " Handler task's current thread is ID " + Thread.currentThread().getId());
task.run();
}
});
}
public void clearQueue(){
handler.removeCallbacksAndMessages(null);
}
}
So my UI thread invokes addTaskToQueue providing a new Task object. The idea is that the task's processing will be performed on a separate thread and after it completes invokes a notifyDataSetChanged() on the UI thread.
However, I've added some logging and it seems that when my tasks run they are still running on the main thread....how would this be? See the following logging:
GridOperationQueue(4393): Current thread is ID 1
GridOperationQueue(4393): Current thread is ID 1
GridOperationQueue(4393): Current thread is ID 1
GridOperationQueue$1(4393): Handler task's current thread is ID 9
TileOperationService$1(4393): Current thread is ID 9
MyActivity(4393): Current thread is ID 9
GridOperationQueue$1(4393): Handler task's current thread is ID 9
TileOperationService$2(4393): Current thread is ID 9
MyActivity(4393): Current thread is ID 9
GridOperationQueue$1(4393): Handler task's current thread is ID 9
TileOperationService$3(4393): Current thread is ID 9
MyActivity(4393): Current thread is ID 9
How come the tasks are still running on the main thread?
I assume you're making all of those changes in your onClick method. The onClick method runs in the UI thread... so, while the code in onClick is running, the UI thread cannot change and tile colors since it's busy. in fact, notifyDataSetChanged simply sets a flag that tells Android to update the changes to the view whenever it can; notifyDataSetChanged does not force an update, but simple tells android one is needed. Thus, you are simply telling android it needs to update the view three times... but, by the time android can actually make the update, which is after your onClick method is done, it can only see the most recent change to the tile color.
How do you get around this? Well, it depends on what you really want to do. for instance, if you want tile color A to change to tile color B when you click the view, and then change to tile color C 500 ms later, do something like this
Handler handler; // instance var
public onCreate(Bundle savedInstanceState) {
...
handler = new Handler();
}
// in your onClick method, wherever it may be (pseudocode)
public void onClick(View v) {
1) set tile color to color B
2) call notifyDataSetChanged
3) schedule a color change in 500 ms:
handler.postDelayed(new Runnable() {
public void run() {
1) set tile color to color C
2) call notifyDataSetChanged
}
}), 500);
You should take a look at Updating the UI from a Timer. The trouble is that when you call notifyDataSetChanged() you only trigger a flag to the GUI. When the adapter gets a repaint it checks if the flag is set and takes action. It does not take any action directly when you call notify.
If you want to update the GUI directly you should learn about the class Handler (see the previous link) so you can post updates to the interface.
Maybe I misunderstood your question, but it seems that you are updating twice the color of Tile B (identical steps 3 and 5 on your question). If that's the case, you will only see the last color (red) and yellow will never be displayed, of course.
Or, you meant "Tile C" on step 5. If this is the case, then the following comments might help:
Even if the UI is changing 3 times, you are probably overriding each color on each call.
1) So your three tiles are 'default' color
2) You update Tile A to green
3) notifyDataSetChanged() is called
(your grid view is updated correctly)
4) You update Tile B to yellow
5) notifyDataSetChanged() is called
(your grid view updates Title B to yellow, but overrides Title A to 'default' color.
6) You update Tile C to red
7) notifyDataSetChanged() is called
(your grid view updates Title C to red, but overrides Title A and B to 'default' color.
That could be the issue.
Also, remember as other guys have mentioned, notifyDataSetChanged() does nothing directly. It only signals the UI to be refreshed when the ideal conditions are met.

"realtime" search using AsyncTask?

I am writing an application that searches a database in "realtime".
i.e. as the user presses letters it updates a search results list.
Since the search can take a while, I need to do the search in background and allow new key presses to re-start a search. So that is a user presses 'a' (and the code starts searching for "a"), then presses 'b' - the code will NOT wait for "a" search to end, then start searching for "ab", but rather STOP the "a" search, and start a new "ab" search.
To do that I decided to do the search in an AsyncTask. Is this a wise decision ?
Now - whenever a keypress is detected, I test to see if I have an AsyncTask running. If I do - I signal it (using a boolean within the AsyncTask) it should stop. Then set a timer to re-test the AsyncTask within 10 mSec, to see if it terminated, and start the new search.
Is this a smart method ? Or is there another approach you would take ?
TIA
First yes, AsyncTask is a good way to do this. The problem I see with your approach is the timer waiting to watch something die. When you invoke the asyncTask hold onto a reference of it. Let it keep state for you so you know if it's out searching or it's has returned. When the user clicks another letter you can tell that asyncTask to cancel. Something like this:
public void onClick() {
if( searchTask != null ) {
searchTask.cancel();
}
searchTask = new SearchTask( MyActivity.this ).execute( textInput.getText() );
}
public class SearchTask extends AsyncTask<String,Integer,List<SearchResult>> {
private boolean canceled = false;
protected onPostExecute( List<SearchResult> results ) {
if( !canceled ) {
activity.handleResults( results );
}
}
public void cancel() {
canceled = true;
}
}
This is safe because onPostExecute() is on the UI thread. And cancel() is only called from the UI thread so there is no thread safety issues, and no need to synchronize. You don't have to watch a thread die. Just let the GC handle cleaning up. Once you drop the reference to the AsyncTask it will just get cleaned up. If your AsyncTask blocks that's ok because it only hangs up the background thread, and when the timeout hits it will resume by calling onPostExecute(). This also keeps your resources to a minimum without using a Timer.
Things to consider about this approach. Sending a new request everytime a new letter is typed can overload your servers because the first few letters are going to produce the largest search results. Either limit the number of results you'll return from the server (say 10-50 results max), or wait until they've entered enough characters to keep results down (say 3). The cons of making the user type more characters is the feedback doesn't kick in until 3 chars. However, the pro is it will dramatically reduce the hits on your server.

Categories

Resources