Running threads in order so that they don't conflict - java

So, general set up is I have a ListView that gets different values depending on the tab, using fragments.
If I move through the tabs slowely, it works great. But if I quickly flip through the tabs, they "conflict". (Items from one tab will appear on the second).
So, my solution was to make Threads that have the runnable parts, then have a queue and add to the queue, then run them off the queue. I didn't think this would work when I did it, and it didn't.
So, the general code looks like such:
final Thread clearThread = new Thread(new Runnable() {
public void run() {
MyFragment.adapter.clear();
}
});
this.threadQueue.add(clearThread);
if (tab == MenuActivity.TITLES.indexOf("My"))
{
// My Puzzles
final Activity a = this;
final Thread myThread = new Thread(new Runnable() {
public void run() {
MyFragment.getUserPuzzles(a);
}
});
this.threadQueue.add(myThread);
}
else if (tab == MenuActivity.TITLES.indexOf("Top"))
{
// Top Puzzles
final Activity a = this;
final Thread topThread = new Thread(new Runnable() {
public void run() {
MyFragment.getTopPuzzles(a);
}
});
this.threadQueue.add(topThread);
}
//.... More adding the Thread Queue.
while (this.threadQueue.size() != 0)
{
final Thread temp = this.threadQueue.poll();
this.runOnUiThread(temp);
}
This is in my FragmentActivity class, where as the methods, adapters, etc. are all in the fragment class (MyFrag).
So, the general question becomes, how can I alter the ListView in a way that it won't conflict with other values being populated while it's being populated. Some of the threads do get values online, so depending on the connection, they can be fast or slow, but it loads so that it adds while it loads.
Any help would be appreciated.
Thanks!

Put a "current request id" incrementing field in. When you spawn the thread set the request id in the thread. When the thread completes check the field and only update the view if it matches.
i.e.
AtomicInteger currentId = new AtomicInteger(0);
new Processor(currentId.incrementAndGet()).start();
In the processor
if (currentId.get() == ourId) {
// only here do stuff
}
For total safety you could use a synchronized block rather than an AtomicInteger, you probably don't need that in this case but that version would look like:
int currentId = 0;
Object lock = new Object();
synchronized(lock) {
new Processor(++currentId).start(); // Must add then use, not use then add!
}
In the processor
synchronized(lock) {
if (currentId == ourId) {
// only here do stuff
}
}

To sync threads you lock the object, and when two threads must always sync with each other it should probably be just one thread. Why not use separate ListView for each tab.

Related

How to pass a value from a runnable thread once it has finished in Android Studio?

I am using a thread to get an id from my database. Once the thread has finished running I need to use that id in another thread to get a list of items. Once that thread has finished I use the list of items to populate a spinner. The problem I am encountering is that it is populating the spinner before the thread has returned the list of items. How can I make it so the spinner doesn't get populated until the thread has finished running?
spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
#Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
new Thread(new Runnable() {
#Override
public void run() {
String bookName = existingBooks.get((int) spinner.getSelectedItemId());
Book myBook = AppDatabase.getInstance(getContext()).bookDAO().getBookByTitle(bookName);
long book_Id = myBook.getBook_id();
existingCountDates = AppDatabase.getInstance(getContext()).countDAO().getCountDatesInBook(book_Id);
List<Count> Counts = AppDatabase.getInstance(getContext()).countDAO().getAllCounts();
Log.e("Dates", existingCountDates.toString());
Log.e("All Counts", Counts.toString());
}
}).start();
//TODO: FIX THIS ISSUE
Log.e("Problem:", "This gets called before the thread finishes. Not sure how to fix it");
str_adapter.clear();
for (int i = 0; i < existingCountDates.size(); i++) {
str_adapter.add(existingCountDates.get(i));
}
str_adapter.notifyDataSetChanged();
}
#Override
public void onNothingSelected(AdapterView<?> parent) {
//TODO: DISABLE OTHER DROPDOWN MENUS?
}
});
You can use another thread and wait previous thread till finish using thread.join(). bu better version is use ExecutorServices such as for instance the ThreadPoolExecutor.
CODELAB
I am not sure whether this will help you or not. But you can use a bool variable to check whether the thread has finished its work.
bool flag = false;
new Thread(new Runnable() {
#Override
public void run() {
//statements
flag = true;
}
}).start();
while(flag != true);
//statements to be executed after completion of thread.
NOTE: Make sure execution never goes to infinite loop.
How can I make it so the spinner doesn't get populated until the thread has finished running?
There are a couple of ways to do that. The easiest might be to get an ExecutorService which is usually recommended as opposed to creating a Thread "by hand". This also allows you to return a value from your job in which case you submit a Callable<?> instead of a Runnable.
ExecutorService threadPool = Executors.newFixedThreadPool(1);
...
Future<Void> future = threadPool.submit(new Runnable() {
public void run() {
...
}
});
// wait for this job to finish
future.get();
...
// when you are done with the pool you need to shut it down
threadPool.shutdown();
You can also start a thread and then join() with it. That waits for the thread to finish.
Thread thread = new Thread(new Runnable() { ... });
thread.start();
// now we can do stuff while the thread runs in the background
...
// wait for the thread to finish before returning
thread.join();
...
The get() method or the join() method also means that the existingCountDates or anything else that was modified inside of the job will be updated in the caller's memory appropriately.

Is it possible to use a non-final in an enclosing scope like a runnable (no.)

The plan:
I created a little PreferenceActivity (don't hate me, I'm supporting API 10 and up) and need to display the current usage of local storage data by my app. I did this using a specialized class (a pretty big one, as of the moment) that handles all file operations (it's called FileOperations.java for a reason). Inside this class file there is a method getSize(File file) {...} which does just that. It gets the size of a file (or folder) with this little piece of code:
public long getSize(File file) {
long size = 0;
if(file.isDirectory()) {
for(File child : file.listFiles()) {
size += getSize(child);
}
}
size = file.length();
return size;
}
The general idea was to use this in a background Thread so it doesn't clog the UI even the slightest bit. (I am really annoyed by lagging apps and suffer from them daily)
The problem:
This works just fine. However, as soon as I purge the folder the app stores it's data in using this beauty:
private void purgeLocalStorage() {
new Thread(new Runnable() {
#Override
public void run() {
Looper.prepare();
Log.i("ActivityPrefsLocalStorage.purgeLocalStorage.Thread.Runnable.run", "Started to run");
final String directory = context.getResources().getString(R.string.app_name);
final String usedData = context.getResources().getString(R.string.ActivityPrefsLocalStorage_usedData);
final File file = new File(Environment.getExternalStorageDirectory()+"/"+directory);
final FileOperations FO = new FileOperations(context);
Log.i("ActivityPrefsLocalStorage.purgeLocalStorage.Thread.Runnable.run", "deleting folder: "+file);
if(FO.delete(file)) {
Log.i("ActivityPrefsLocalStorage.purgeLocalStorage.Thread.Runnable.run", file+" deleted");
runOnUiThread(new Runnable() {
#Override
public void run() {
Toast.makeText(context, R.string.ActivityPrefsLocalStorage_deleteSucces, Toast.LENGTH_SHORT).show();
setTotalLocalDataTexts(usedData+" "+context.getResources().getString(R.string.pref_totalData_default), "");
getUsedStorage();
}
});
} else {
Log.e("ActivityPrefsLocalStorage.purgeLocalStorage.Thread.Runnable.run", "could not delete "+file);
runOnUiThread(new Runnable() {
#Override
public void run() {
Toast.makeText(context, R.string.ActivityPrefsLocalStorage_deleteError, Toast.LENGTH_SHORT).show();
}
});
}
}
}).start();
}
Things hit the fan...
See, the problem is that my method for reading the size of the folder does not want to function properly when called by the previous method.
Here's a snippet:
private void getUsedStorage() {
new Thread(new Runnable() {
#Override
public void run() {
Log.i("ActivityPrefsLocalStorage.getUsedStorage.Thread.Runnable.run", "Started to run");
final String directory = context.getResources().getString(R.string.app_name);
final String usedData = context.getResources().getString(R.string.ActivityPrefsLocalStorage_usedData);
final File file = new File(Environment.getExternalStorageDirectory()+"/"+directory);
final FileOperations FO = new FileOperations(context);
final DataUsage DU = new DataUsage(context);
Log.i("ActivityPrefsLocalStorage.getUsedStorage.Thread.Runnable.run", "Checking filesize of folder: "+file);
long fileSize = FO.getSize(file);
String usedUnits = DU.getUnit(fileSize, true, false, false);
String usedBytes = DU.getUnit(fileSize, true, true, true);
Log.i("ActivityPrefsLocalStorage.getUsedStorage.Thread.Runnable.run", "filesize of "+file+": "+usedUnits);
setTotalLocalDataTexts(usedData+" "+usedUnits, usedBytes);
}
}).start();
}
However, a quick and easy workaround would be to place it on the UI thread like so:
...blabla code you already read above.
long fileSize = FO.getSize(file);
String usedUnits = DU.getUnit(fileSize, true, false, false);
String usedBytes = DU.getUnit(fileSize, true, true, true);
Log.i("ActivityPrefsLocalStorage.getUsedStorage.Thread.Runnable.run", "filesize of "+file+": "+usedUnits);
runOnUiThread(new Runnable() {
#Override
public void run() {
setTotalLocalDataTexts(usedData+" "+usedUnits, usedBytes);
}
});
}
}).start();
}
And that's where it starts getting interesting. I cannot use non-finals inside the new Runnable(), and I cannot make them final since I want the value to update and not remain stuck at eg. 32MiB (while it has just been purged).
Possible fixes:
I should man up and just use a final. The user will understand they need to refresh the page manually. (oh no...)
Hire... erm. Extend an AsyncTask<Void, Void, Void> to do the work.
My ideal fix:
Someone giving me an awesome snippet of code for free that does all the magic. no, seriously though, I would really appreciate anything apart from my list of possible fixes. There has to be some way to pass the new Runnable() a variable without creating classes and implementing the entire universe? Or is that what I am trying to achieve really a new thing?
TL;DR:
Things go wrong as soon as I call getUsedStorage() from within a new Runnable(). This function is also a background task inside a Runnable, but updates the UI using a private void function that sets it. It only passes variables to this function. and then things fly off the handle(r).
Edit: grammar.
Edit2: Also a pretty interesting thing to note here, I used something similar in another PreferenceActivity, and that one works. (but that one does not update at the press of a button that calls another private something functionName() {new Thread(new Runnable() {public void run() {...});})
There are a couple of ways to use non-finals inside of a Runnable or other enclosed classes.
The first is to change your variables to be members of an enclosing class. This will allow you to use the variables inside the Runnable. An example follows:
public class Foo {
private long time;
public void run() {
time = System.currentTimeMillis();
new Thread(new Runnable() {
#Override
public void run() {
time += 1;
System.out.println("Our current time is: " + time);
}
});
}
}
The other option, and it is quite hacky, is to use a final array with a length of 1. An example of that follows:
public class Foo {
public void run() {
final long[] time = { System.currentTimeMillis() };
new Thread(new Runnable() {
#Override
public void run() {
time[0] += 1;
System.out.println("Our current time is: " + time[0]);
}
});
}
}
As gonemad16 on reddit.com/r/androiddev pointed out, my issue had nothing to do with final vs non final. That was not the reason I was getting an old value. All my variables are given a value, sent to setTotalLocalDataTexts and then go out of scope... nothing is updating their values.. so there is no harm in them being final and no benefit to them being non final...
It was an an issue in getSize()
I thought I had a correct loop there using if file.isDirectory() {...}. It created a directory tree and executed itself using the children it has found. When all items have been scanned the value returns to the function calling it.
This was working just fine for me while I was still running all of my code on the ui thread. Everything was slow. But it worked.
However, I forgot that I removed a very crucial ...} else {...
I believe I removed that one because it caused a stack overflow at some point, so I removed it and I guess forgot to put it back...
And here I was thinking my first SO question wouldn't be a noobish question...

How to make this search in a new thread

I have search as you type functionality although it searches very fast and I can't notice it even with no multithreading I still want to know how would I use multithreading on this
search.textProperty().addListener(new ChangeListener<String>() {
#Override
public void changed(ObservableValue<? extends String> observableValue, String s, String s2) {
manager.searchString(s2);
listView.getItems().setAll(manager.getList());
}
});
So basically there is a TextField that when its text is changed I go and call a search method in object manager which puts its search result in an array when it finishes.
Then the ListView should update its data to this new array when it finishes.
How can I make the search on one thread and when it finishes it update the list data?!
I believe I can't just call the list function from other thread because GUI stuff should be called from only one thread.
To do this efficiently in a different thread is not as simple as it sounds.
You don't want to create and execute a new thread every time a key is pressed because:
There is system overhead for thread creation which would make that an extremely intensive process
There's no guarantee that the threads will execute and complete in the order they are created, so you may get an earlier thread finishing after a subsequent one and consequentially updating the list with invalid entries.
You could use a single-thread executor service (which keeps one thread alive and uses it to execute Runnables passed into it in order), which would be more efficient, but you'd need to remember to shut it down when your text field is destroyed (if you do ever destroy your text field). Something along these lines:
// first of all, at the class level (assuming listView and manager are both class-level variables, preferably final ones, too):
// An INNER class implementing Runnable which will carry out the searching
private class Searcher implements Runnable {
private volatile boolean cancelled = false;
private final String searchTerm;
Searcher(String searchTerm) {
this.searchTerm = searchTerm;
}
public void cancel() {
cancelled = true;
}
#Override
public void run() {
// remember that there's no guarantee that this will execute before the NEXT keypress, so we add a check to ensure that we still want to perform the search when it gets executed:
if (!cancelled) {
manager.searchString(searchTerm);
Platform.runLater(listViewUpdater); // listViewUpdater is defined below
}
}
}
// a Runnable to actually update the GUI after a seach has been performed
private Runnable listViewUpdater = new Runnable(){
#Override
public void run() {
listView.getItems().setAll(manager.getList());
}
}
private ExecutorService executor = Executors.newSingleThreadExecutor();
private Searcher lastSearcher = null;
// ... then, in the method which sets up the GUI
search.textProperty().addListener(new ChangeListener<String>() {
#Override
public void changed(ObservableValue<? extends String> observableValue, String s, String s2) {
if (lastSearcher != null) {
lastSearcher.cancel(); // prevents lastSearcher from running if it hasn't done so already
}
lastSearcher = new Searcher(s2);
executor.submit(lastSearcher);
}
});
The downside is you are creating a new object every time the value changes, but that isn't nearly as bad as creating a new Thread every time.

How can I add items to a list from a thread and keep the adding order?

I'm trying to add some items in a list and keep the adding order. The problem is that
the adding part is done in a thread because I need to get some details from DB, put them in a
item and add it to list.
public void addNewItem(Step item){
Thread t = new Thread(new Runnable(){
public void run() {
item.setDetails(db.getStepDetails(step.getId()));
add(item);
}
});
t.start();
}
The add method looks like:
private void add(Step step){
mitems.add(step);
}
If I do something like
addNewItem(stepA);
addNewItem(stepB);
If the first call take too much time, the second item is added first in my list.
Do you know how can I wait for an action to finish before continue adding any other item in my list?
Thank you.
You could add a synchronized block or use a ReentrantLock.
Thread t = new Thread(new Runnable(){
public void run() {
synchronized(someSafeVar) {
item.setDetails(db.getStepDetails(step.getId()));
add(item);
}
}
});
Also, if it's only the "add" method that takes time, this method only could be synchronized.
You could create a single worker thread, and feed it from a queue, and thus you would have a single thread adding to mitems.
You would need to creat two lists in that case, and syncrhonize them.
EG.,
List<Step> processingList = new ArrayList<Step>();
List<Step> mitems = new ArrayList<Step>();
public void addNewItem(Step item)
{
syncrhonized(processingList)
{
processingList.add(item);
}
}
The worker thread takes items from the front of processingList, works on them, and adds them to mitems when they are ready.
You could use a Dictionary instead:
Dictionary mitems = new Hashtable();
and
mitems.put("1", item);
The Key would be the Order then.
you could keep your item order if you added the items to your list before you set the details.
then you could wait with all following actions until a initialized flag of your items is set:
mitems.add(stepA);
mitems.add(stepB)
[...]
ThreadGroup group = new ThreadGroup("GetStepDetailsGroup");
for(Step step : mitems)
{
Thread thread = new Thread(group, new Runnable(){
public void run() {
item.setDetails(db.getStepDetails(step.getId()));
}
});
thread.start();
}
while(group.activeCount() > 0) {
try {
Thread.sleep(50);
} catch(InteruptedExpection e) {
// ignored
}
}
In your example, it looks like adding the Steps to a List is totally independent of retrieving information from the DB and filling in the Steps.
Therefore, you could just put all your Steps in a List using only one Thread. Then, you would fetch the DB information using Runnables, one for each Step and execute these Runnables in a Executor with as many threads as you like (while still getting a speedup from multithreading).
Thread#join
Waits for this thread to die.
Hope this helps,
Yaron

synchronized lock and separate threads: Android

I have a callback function in which i receive a string.
This string is to be passed to a separate thread for processing since the processing takes time.
Also, since multiple callbacks can come simultaneously, I would like to have a synchronized lock till i pass the string into the new thread. But I do not wish to have the new thread (where processing is going on) to be locked also.
Could someone please help me figure out the design for this?
I have written the following code but in this I think in this no callbacks can be received till the whole processing of the separate thread is also done, thereby defeating the whole purpose of this new thread.
String sLine;
onClick(String line){
synchronized (lock) {
sLine = line;
new Thread(new Runnable() {
#Override
public void run() {
doProcessing(Sline);
}).start();
}
}
Also, since multiple callbacks can come simultaneously, I would like to have a synchronized lock till i pass the string into the new thread.
A: I don't think you need to put a lock here. This string is not accessed by multi-thread.
But I do not wish to have the new thread (where processing is going on) to be locked also.
A: As I see nothing was locked here :) I think it could be better if you do something like that:
Create an class Runner implement Runnable, this class will do processing
Everytime you got callback, use ThreadPoolExecutor to execute this Runner. This help you reuse Thread instance.
Note that: These line code doesn't need to synchronized, put synchronized inside processing method if you need.
// Implement class Runner
public class Runner implements Runnable {
private String mLine;
public Runner(String line) {
mLine = line;
}
#Override
public void run() {
process();
}
public void process() {
// Do processing with mLine
// Put synchronized if you need, it bases on your context
}
}
// Initialize thread pool
private ThreadPoolExecutor executor = new ThreadPoolExecutor(10, 100, 1000, TimeUnit.SECONDS, new LinkedBlockingDeque<Runnable>());
// Execute runner when receiving callback
onClick(String s) {
Runner runner = new Runner(s);
executor.execute(runner);
}
Try changing like below
String sLine;
onClick(final String line){
sLine = line;
new Thread(new Runnable() {
#Override
public void run() {
doProcessing(line);
}).start();
}

Categories