I have a HashMap of Sound objects
private HashMap<Integer, Sound> sounds;
over which I'm trying to iterate to turn off all the sounds. I used
this answer to create an Iterator, but I'm still getting ConcurrentModificationException, though I'm sure there's no other code calling this at the same time.
public synchronized final void stopAll() {
Iterator<Entry<Integer, Sound>> soundEntries = sounds.entrySet().iterator();
while(soundEntries.hasNext())
{
Entry<Integer, Sound> s = soundEntries.next();
s.getValue().myOnCompletionListener = null;
s.getValue().fadeYourself();
}
sounds.clear();
}
In what way should I rewrite this to keep the ConcurrentModificationException from happening?
This is inside my Sound class:
private class soundFader extends AsyncTask<Sound, Void, Void>
{
#Override
protected Void doInBackground(Sound... arg0) {
arg0[0].fadeOut();
return null;
}
}
private void fadeOut()
{
float STEP_DOWN = (float) 0.10;
float currentVol = myVolume;
float targetVol = 0;
if(isSoundEnabled())
{
while(currentVol > targetVol)
{
currentVol -= STEP_DOWN;
mp.setVolume(currentVol, currentVol);
try {
Thread.sleep(70);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
mp.setVolume(0, 0);
onCompletion(mp);
sounds.remove(resource); // THIS LINE WAS MY ERROR
mp.seekTo(0);
nowPlaying = false;
}
public void fadeYourself()
{
soundFader fader = new soundFader();
fader.execute(this);
}
It is not permissible for one thread to modify a Collection while another thread is iterating over it.
If you want to modify only values (not keys) there is no need to use iterators here.
public synchronized final void stopAll() {
for(Sound s: sounds.values())
{
s.myOnCompletionListener = null;
s.fadeYourself();
}
sounds.clear();
}
Ninja edit:
You are removing items from the Collection while iterating. Hence the CoMo exception.
Since you are doing sounds.clear(); towards the end, you can remove the sounds.remove(resource); line.
Related
In my program a number of values are stored on the server.
I read these data using of the AsyncTask class from the server.
This is my code:
public class TestActivity extends AppCompatActivity {
private static List<String> mPackName;
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mPackName = new ArrayList<>();
try {
boolean check = new GetDataAsyncTask().execute("1234567").get();
} catch (InterruptedException e) {
} catch (ExecutionException e) {
e.printStackTrace();
e.printStackTrace();
}
}
private class GetDataAsyncTask extends AsyncTask<String, Void, Boolean> {
#Override
protected Boolean doInBackground(String... params) {
final String mCode = params[0];
APIGettingPosts apiGettingPosts = new APIGettingPosts(TestActivity.this, "get_off_code.php");
apiGettingPosts.getOffCode(new APIGettingPosts.OnOffCodeReceived() {
#Override
public void onReceived(List<Posts> posts) {
if (!(posts == null || posts.isEmpty()))
for (int i = 0; i < posts.size(); ++i) {
mPackName.add(posts.get(i).getTitle());
Log.e("mPackName["+String.valueOf(i)+"]",mPackName.get(i));
}
}
});
Log.e("Size of mPackName: ", String.valueOf(mPackName.size()));
for (int i = 0; i < mPackName.size(); ++i)
if (mCode.equals(mPackName.get(i))) {
Log.e("Is Equal: ", mPackName.get(i));
return true;
}
return false;
}
}
}
The program correctly receives the data from the server and stores it in the mPackName list. At the end of the doInBackground function, the program checks if the input value in the GetDataAsyncTask().execute("1234567") function exists in the mPackName list, returns the true value.
Although the input value of the GetDataAsyncTask().execute("1234567") function is in the mPackNamelist, the program returns the false value.
The Log cat output is as follows:
08-28/com.green E/Size of mPackName:: 0
08-28/com.green E/mPackName[0]: 1234567
08-28/com.green E/mPackName[1]: QAZXSWE
08-28/com.green E/mPackName[2]: QWERTYU
The size of the mPackName list is also zero in Log cat , although it has three values {'1234567','QAZXSWE','QWERTYU'}.
The question is: How do I search '1234567' value in the mPackName list to return the true value in check = new GetDataAsyncTask().execute("1234567").get();
code?
Edited Answer
Looks like you even don't need AsyncTask as getOffCode method already runs in background thread.
Remove GetDataAsyncTask class and create a method like below.
private void search(final SearchCallback callback) {
APIGettingPosts apiGettingPosts = new APIGettingPosts(TestActivity.this, "get_off_code.php");
apiGettingPosts.getOffCode(new APIGettingPosts.OnOffCodeReceived() {
#Override
public void onReceived(List<Posts> posts) {
if (!(posts == null || posts.isEmpty())) {
for (int i = 0; i < posts.size(); ++i) {
mPackName.add(posts.get(i).getTitle());
Log.e("mPackName[" + String.valueOf(i) + "]", mPackName.get(i));
if (mCode.equals(mPackName.get(i))) {
callback.onSearchFound(true);
break;
}
}
}
callback.onSearchFound(false);
}
});
}
public interface SearchCallback{
void onSearchFound(boolean success);
}
Then call from onCreate method like below
search(new SearchCallback(){
#Override
public void onSearchFound(boolean success) {
}
});
Try placing a switch in the onPostExecute() method.
EG.
...
private class GetDataAsyncTask extends AsyncTask<String, Void, Boolean> {
#Override
void onPostExecute(Object o){
handleResults()
}
...
void handleResults(){
// Insert your check here
}
I'm currently creating an Android app for school but still want to give my best.
I'm pretty new to Android development and coding in general. The app is supposed to be a stock market game. (Btw, I'm German, so there might be some German variables)
So I want to sort my RecyclerView containing shares. It works alphabetically but not by worth.
I can guarantee that the name "worth" of the double in the JSONObject is correct. What am I doing wrong?
public class CompanyAdapter extends RecyclerView.Adapter<CompanyAdapter.viewHolder> implements Filterable {
private CustomFilter filter;
private ArrayList<JSONObject> jObjList;
private final String keyName;
private final String keyWorth;
private final String keyChange;
public final static int SORT_ALPHABETICALLY = 0;
public final static int SORT_ALPHABETICALLY_REVERSE = 1;
public final static int SORT_BY_WORTH = 2;
public final static int SORT_BY_WORTH_REVERSE = 3;
public CompanyAdapter(Context context, ArrayList<JSONObject> jObjList) {
this.jObjList = jObjList;
Context c = context;
keyName = c.getResources().getString(R.string.nameCompany);
keyWorth = c.getResources().getString(R.string.worthCompany);
keyChange = c.getResources().getString(R.string.changeCompany);
sort(SORT_ALPHABETICALLY);
}
//left out some unnecessary code
public void sort (int sorting) {
if (jObjList.size()>1) {
switch (sorting) {
case SORT_ALPHABETICALLY:
sortAlphabetically();
break;
case SORT_ALPHABETICALLY_REVERSE:
sortAlphabeticallyReverse();
break;
case SORT_BY_WORTH:
sortByWorth();
break;
case SORT_BY_WORTH_REVERSE:
sortByWorthReverse();
break;
}
}
}
private void sortAlphabetically () {
Collections.sort(jObjList, new Comparator<JSONObject>() {
#Override
public int compare(JSONObject j1, JSONObject j2) {
try {
return j1.getString(keyName).compareToIgnoreCase(j2.getString(keyName));
} catch (JSONException e) {
return 0;
}
}
});
}
private void sortAlphabeticallyReverse () {
sortAlphabetically();
Collections.reverse(jObjList);
}
private void sortByWorth () {
Collections.sort(jObjList, new Comparator<JSONObject>() {
#Override
public int compare(JSONObject j1, JSONObject j2) {
try {
return Double.compare(j1.getDouble(keyWorth), j2.getDouble(keyWorth));
} catch (JSONException e) {
Log.e("JSONException", e.getMessage());
return 0;
}
}
});
}
private void sortByWorthReverse () {
sortByWorth();
Collections.reverse(jObjList);
}
}
try to replace
return Double.compare(j1.getDouble(keyWorth), j2.getDouble(keyWorth));
with
System.out.print("VALUE1: "+j1.getDouble(keyWorth));
System.out.print("VALUE2: "+j2.getDouble(keyWorth));
return (int)(j1.getDouble(keyWorth)-j2.getDouble(keyWorth));
as well to make sure and debug the value print it.
and after sortByWorth();
add notifyDataSetChanged();
Have you checked the values of the objects you are comparing within the console?
Since you are reading in the values as a string, perhaps they will not be giving the result you expect.
Furthermore, what operation is the compare function performing?
Replace:
return Double.compare(j1.getDouble(keyWorth), j2.getDouble(keyWorth));
In sortByWorth method, to:
return j1.getDouble(keyWorth).compareTo(j2.getDouble(keyWorth))
Try it..
I forgot the notifyDataSetChanged(). Sorry, that's a stupid error.
I am replacing my Sqlite database with an online database (Firestore). For that each answer of the database comes back to me by callback.
The problem is that I have several calls to the database in a loop that filled a table and that the table is not accesible unless I declare it in the end and therefore I can not change it.
So I'm looking for a way to fill this table without completely modifying the code that already exists. I saw the ArrayBlockingQueue but I wonder if a simpler solution does not exist.
If possible I would like to keep all the variables inside the function but I have not yet found a solution for that.
I know that for this example we do not necessarily need a table but I want to keep it because it's just an example ;)
Before (SQLite)
public int player_in_x_game(int id_player) {
int gamesWherePlayerIsHere = 0;
ArrayList<Games> gamesArray = Database.getGamesArray();
for (Game game: gamesArray)
if(Utils.isPlayerPresentInGame(game.getId(), idPlayer))
gamesWherePlayerIsHere++;
return gamesWherePlayerIsHere;
}
After (with callbacks)
private static int counter= 0;
private static int resultNP = 0;
private static ArrayBlockingQueue<Integer> results;
public static void numberGamesWherePlayerIsPresent(final long idPlayer, final Callbacks.IntCallback callback){
Thread thread = new Thread() {
#Override
public void run() {
games(new Callbacks.ListGameCallback() {
#Override
public void onCallback(ArrayList<Game> gameArrayList) {
counterNumberGamesWherePlayerIsPresent= gameArrayList.size();
results = new ArrayBlockingQueue<>(gameArrayList.size());
for (Game game: gameArrayList){
Utils.isPlayerPresentInGame(game.getId(), idPlayer, new Callbacks.BooleanCallback() {
#Override
public void onCallback(boolean bool) {
if (bool)
results.add(1);
else
results.add(0);
}
});
}
int result;
try {
while (counter > 0) {
result = results.take();
counter--;
resultNP += result;
}
}catch (InterruptedException ie){
ie.fillInStackTrace();
Log.e(TAG,"results.take() failed");
}
callback.onCallback(resultNP);
}
});
}
};
thread.setName("Firestore - numberGamesWherePlayerIsPresent()");
thread.start();
}
I'm using the AsyncTask to render a graph from a list. Sometimes it works alright and the graph is rendered. However in some cases the graph is not being rendered, and the reason for this is that the doInBackground() method is not being triggered. Here's the code of the AsyncTask.
private class HistoryPlotAsync extends AsyncTask<Void, Void, Void> {
boolean isAnalysisMode = false;
List<Byte> listECG;
List<Byte> listHS;
HistoryPlotAsync(List<Byte> listECG, List<Byte> listHS, List<Byte> listMur, boolean isAnalysisMode) {
this.listECG = listECG;
this.listHS = listHS;
this.isAnalysisMode = isAnalysisMode;
HistoryPlot.this.multiHsRenderer.setPanEnabled(false, false);
HistoryPlot.this.multiEcgRenderer.setPanEnabled(false, false);
}
protected Void doInBackground(Void... params) {
if (HistoryPlot.this.pcgPlayer != null) {
HistoryPlot.this.pcgPlayer.start();
} else {
try {
HistoryPlot.this.startSound();
} catch (Throwable throwable) {
throwable.printStackTrace();
}
}
int i = 0;
int loopCounter = 0;
if (this.listHS != null && this.listHS.size() > 0) {
loopCounter = this.listHS.size();
}
double xValue = 0.0d;
double xValueEcg = 0.0d;
do {
xValueEcg += 0.0015625d;
if (this.listHS != null && i % 2 == 0) {
xValue += 0.0032012d;
hsSeries.add(xValue,listHS.get((i/2)));
}
try {
if (this.listECG != null && i < this.listECG.size()) {
ecgSeries.add(xValueEcg, listECG.get(i));
}
} catch (Exception e) {
try {
Log.d("HistoryPlot -> ", "doInBackground: Exception " + e.getMessage());
} catch (Exception e2) {
Log.e("HistoryPlot -> ", "Error in Analysis mode point conversion");
}
}
if (i > HistoryPlot.this.refRange && i % 94 == 0) {
HistoryPlot.this.xMin = HistoryPlot.this.xMin + 0.15d;
HistoryPlot.this.xMax = HistoryPlot.this.xMax + 0.15d;
HistoryPlot.this.multiHsRenderer.setXAxisMin(HistoryPlot.this.xMin);
HistoryPlot.this.multiHsRenderer.setXAxisMax(HistoryPlot.this.xMax);
HistoryPlot.this.multiEcgRenderer.setXAxisMin(HistoryPlot.this.xMin);
HistoryPlot.this.multiEcgRenderer.setXAxisMax(HistoryPlot.this.xMax);
}
if (i % 16 == 0) {
publishProgress(new Void[0]);
try {
Thread.sleep(20);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
i++;
if (i >= loopCounter) {
break;
}
} while (!HistoryPlot.this.taskHistoryPlot.isCancelled());
return null;
}
protected void onProgressUpdate(Void... values) {
HistoryPlot.this.mHsChart.repaint();
HistoryPlot.this.mEcgChart.repaint();
}
protected void onPostExecute(Void result) {
HistoryPlot.this.stopSound();
HistoryPlot.this.enableReplay();
HistoryPlot.this.multiHsRenderer.setPanEnabled(true, true);
HistoryPlot.this.multiEcgRenderer.setPanEnabled(true, true);
}
}
The AsyncTask is executed via the following code in the onCreate() method of the Activity.
protected void onCreate(Bundle savedInstance) {
...
this.taskHistoryPlot = new HistoryPlotAsync((List) mapData.get("fileEcg"), (List) mapData.get("fileHs"), (List) mapData.get("fileMur"), isAnalysisMode);
this.taskHistoryPlot.execute();
}
Instead of using the execute() method, I have also tried using the executeOnExecutor(THREAD_POOL_EXECUTOR) method with the same results.
I got the issue resolved using the following code. The problem was that I had simply tried using the execute() method and the executeOnExecutor() method as is. You need to define an Executor and a Blocking Queue variables and define pool sizes for this to work.
static int mCorePoolSize = 60;
static int mMaximumPoolSize = 80;
static int mKeepAliveTime = 10;
static BlockingQueue<Runnable> workQueue = new LinkedBlockingQueue<Runnable>(mMaximumPoolSize);
static Executor mCustomThreadPoolExecutor = new ThreadPoolExecutor(mCorePoolSize, mMaximumPoolSize, mKeepAliveTime, TimeUnit.SECONDS, workQueue);
this.taskHistoryPlot = new HistoryPlotAsync((List) mapData.get("fileEcg"), (List) mapData.get("fileHs"), (List) mapData.get("fileMur"), isAnalysisMode);
this.taskHistoryPlot.executeOnExecutor(mCustomThreadPoolExecutor);
Asynctask methods will surely run, if you have have call to this class.
Please debug that, the conditions inside doInBackground(), for example
"if (HistoryPlot.this.pcgPlayer != null) "
because it may be possible that condition is not working well, or throws xception and led the function not being called.
If you want that code to execute multiple times, do not use the onCreate function. Create one that you can call everytime you want, onCreate() only runs once in the Activity lifecycle. That can be the cause.
You can call it from the onCreate as well, if you need ;)
Replace these two lines:
this.taskHistoryPlot = new HistoryPlotAsync((List) mapData.get("fileEcg"), (List) mapData.get("fileHs"), (List) mapData.get("fileMur"), isAnalysisMode);
this.taskHistoryPlot.execute();
with:
new HistoryPlotAsync((List) mapData.get("fileEcg"), (List) mapData.get("fileHs"), (List) mapData.get("fileMur"), isAnalysisMode).execute();
You dont need to pass context of activity here "this." will give the context of activity which you dont need it here so apply above mentioned changes.
OnCreate will run it one time only create another method in which you need to run your async task and call that new method wherever you want.You can also call that method in onCreate too.
My goal is to have an AsyncTask that
can execute multiple times (one task at a time of course)
its current task can be cancelled
can be used by any activity
can execute many different tasks
does not have any problem with screen rotation (or phonecalls etc)
To achieve that i have created the classes shown below. But my experience with (and understanding of) threads is very limited. And since i don't know of any way to debug multiple threads, there is no way (for me) of knowing if this is going to work or not. So what i'm really asking is: Is this code ok?
And since there is no code that it is currently using this, here's an example use for it:
Data2Get d2g = new Data2Get(this, Data2Get.OpCountNumbers);
d2g.setParam("up2Num", String.valueOf(800));
LongOpsRunner.getLongOpsRunner().runOp(d2g);
So, here we go. This is the interface that every activity that wants to execute a long task (operation - op) should implement:
public interface LongOpsActivity {
public void onTaskCompleted(OpResult result);
}
This is a class to enclose any result of any task:
public class OpResult {
public LongOpsActivity forActivity;
public int opType;
public Object result;
public OpResult(LongOpsActivity forActivity, int opType, Object result){
this.forActivity = forActivity;
this.opType = opType;
this.result = result;
}
}
And finally the big part, the singleton async task class:
import java.util.HashMap;
import java.util.Map.Entry;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
import android.os.AsyncTask;
public class LongOpsRunner extends AsyncTask<Void, OpResult, Void> {
public class Data2Get implements Cloneable {
// one id for each operation
public static final int OpCountNumbers = 1;
public static final int OpCountLetters = 2;
public LongOpsActivity forActivity;
public int opType;
private HashMap<String, String> params = new HashMap<String, String>();
public Data2Get(LongOpsActivity forActivity, int opType) {
this.forActivity = forActivity;
this.opType = opType;
}
public void setParam(String key, String value) {
params.put(key, value);
}
public String getParam(String key) {
return params.get(key);
}
public void clearParams() {
params.clear();
}
#Override
protected Object clone() throws CloneNotSupportedException {
// deep clone
Data2Get myClone = (Data2Get) super.clone();
myClone.clearParams();
for (Entry<String, String> entry : params.entrySet()) {
myClone.setParam(new String(entry.getKey()), new String(entry.getValue()));
}
return myClone;
}
}
private class IntermediateResult extends OpResult {
public IntermediateResult(LongOpsActivity forActivity, int opType, Object result) {
super(forActivity, opType, result);
}
}
// not really needed
private class FinalResult extends OpResult {
public FinalResult(LongOpsActivity forActivity, int opType, Object result) {
super(forActivity, opType, result);
}
}
private final ReentrantLock lock = new ReentrantLock();
private final Condition executeOp = lock.newCondition();
private volatile boolean finished = false;
private volatile boolean waiting = true;
private volatile boolean shouldCancel = false;
private volatile boolean activityHasBeenNotified = true;
private Data2Get startingOpParams = null;
private Data2Get currentOpParams = null;
private FinalResult currentOpResult;
protected Void doInBackground(Void... nothing) {
try {
lock.lockInterruptibly();
do {
waiting = true;
while (waiting) {
executeOp.await();
}
shouldCancel = false;
activityHasBeenNotified = false;
boolean opCancelled = false;
try {
currentOpParams = (Data2Get) startingOpParams.clone();
} catch (CloneNotSupportedException cns) {
// do nothing
}
switch (currentOpParams.opType) {
case Data2Get.OpCountNumbers:
int numberCounter = 0;
int numLoopCount = 0;
while ((!opCancelled) & (numLoopCount <= 5000000)) {
if (!shouldCancel) {
numberCounter = (numberCounter + 1)
% Integer.parseInt(currentOpParams.getParam("up2Num"));
if (numberCounter == 0) {
numLoopCount++;
publishProgress(new IntermediateResult(
currentOpParams.forActivity,
currentOpParams.opType,
"Numbers loop count:" + numLoopCount));
}
} else {
opCancelled = true;
activityHasBeenNotified = true;
}
if (!opCancelled) {
currentOpResult = new FinalResult(
currentOpParams.forActivity,
currentOpParams.opType,
"Numbers loop completed.");
publishProgress(currentOpResult);
}
}
break;
case Data2Get.OpCountLetters:
int letterLoopCount = 0;
char ch = 'a';
while (!opCancelled & (letterLoopCount <= 5000000)) {
if (!shouldCancel) {
ch++;
if (Character.toString(ch).equals(currentOpParams.getParam("up2Letter"))) {
ch = 'a';
letterLoopCount++;
publishProgress(new IntermediateResult(
currentOpParams.forActivity,
currentOpParams.opType,
"Letters loop count:" + letterLoopCount));
}
} else {
opCancelled = true;
activityHasBeenNotified = true;
}
if (!opCancelled) {
currentOpResult = new FinalResult(
currentOpParams.forActivity,
currentOpParams.opType,
"Letters loop completed.");
publishProgress(currentOpResult);
}
}
break;
default:
}
} while (!finished);
lock.unlock();
} catch (InterruptedException e) {
// do nothing
}
return null;
}
public void cancelCurrentOp() {
shouldCancel = true;
}
#Override
protected void onProgressUpdate(OpResult... res) {
OpResult result = res[0];
if (result instanceof IntermediateResult) {
// normal progress update
// use result.forActivity to show something in the activity
} else {
notifyActivityOpCompleted(result);
}
}
public boolean currentOpIsFinished() {
return waiting;
}
public void runOp(Data2Get d2g) {
// Call this to run an operation
// Should check first currentOpIsFinished() most of the times
startingOpParams = d2g;
waiting = false;
executeOp.signal();
}
public void terminateAsyncTask() {
// The task will only finish when we call this method
finished = true;
lock.unlock(); // won't this throw an exception?
}
protected void onCancelled() {
// Make sure we clean up if the task is killed
terminateAsyncTask();
}
// if phone is rotated, use setActivity(null) inside
// onRetainNonConfigurationInstance()
// and setActivity(this) inside the constructor
// and all that only if there is an operation still running
public void setActivity(LongOpsActivity activity) {
currentOpParams.forActivity = activity;
if (currentOpIsFinished() & (!activityHasBeenNotified)) {
notifyActivityOpCompleted(currentOpResult);
}
}
private void notifyActivityOpCompleted(OpResult result) {
if (currentOpParams.forActivity != null) {
currentOpParams.forActivity.onTaskCompleted(result);
activityHasBeenNotified = true;
}
}
private static LongOpsRunner ref;
private LongOpsRunner() {
this.execute();
}
public static synchronized LongOpsRunner getLongOpsRunner() {
if (ref == null)
ref = new LongOpsRunner();
return ref;
}
public Object clone() throws CloneNotSupportedException {
throw new CloneNotSupportedException();
}
}
I hope someone helps with making this work, as it would be very useful not only for me, but many other people out there. Thank you.
Try Loaders. I switched from simple AsyncTasks to AsyncTaskLoaders and they solve lots of problems. If you implement a Loader as a standalone class, it would meet all of your requirements, especially when it comes to rotation which is the biggest issue with old AsyncTask.