I've written a AsyncTask:
public class AudioTransition extends AsyncTask <Void, Void, MediaPlayer>
{
private int goalID;
private int milliseconds;
private MediaPlayer tempPlayer;
AudioTransition(int ID, int Milliseconds)
{
goalID = ID;
milliseconds = (int)(((float)Milliseconds)/100);
}
#Override
protected void onPreExecute()
{
tempPlayer = MediaPlayer.create(context, goalID);
tempPlayer.setVolume(0, 0);
tempPlayer.setLooping(true);
tempPlayer.start();
}
#Override
protected MediaPlayer doInBackground(Void... arg0) {
for (int i = 0; i < 100; i++)
{
value = i;
publishProgress();
try {
Thread.sleep(milliseconds);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
if (!player.isPlaying())
tempPlayer.pause();
}
return tempPlayer;
}
#Override
protected void onProgressUpdate(Void... v) {
super.onProgressUpdate(v);
player.setVolume(100-value, 100-value);
tempPlayer.setVolume(value, value);
}
#Override
protected void onPostExecute( MediaPlayer result ) {
super.onPostExecute(result);
player.reset();
player = tempPlayer;
player.setVolume(100,100);
transitioning = false;
}
}
But the volume doesn't fade out. It just starts both tracks, then stops. The MediaPlayers are not updated until doInBackground completes. How can I make the MediaPlayers get updated within this type of background worker? It seems like the publishProgress() thing should work.
Oh lord. Dont be sleeping threads inside of AsyncTask! You are completely misusing AsyncTask. You couldn't think of another way to do a timer type thing, so you're latching onto the idea of publishprogress from AsyncTask (which doesn't even work how I think you think it works) even though AsyncTask should be used for one thing and one thing only: doing heavy work off of the main (UI) thread.
If you just wanted to fade the volume out then use something like this: (this goes somewhere outside of any method).
private Runnable VolumeFadeRunnable = new Runnable() {
#Override
public void run() {
volume--;
player.setVolume(volume, volume);
if(volume>0)
handler.postDelayed(this, 1000);
else
handler.removeCallbacks(this);
}
};
just initialize your handler as a field inside of onCreate or whatever and make sure that and the counter variable are visible inside of that runnable.
Related
I have ConcurrentLinkedQueue and Queue is updated in main thread and AsyncTask display the data inside the ConcurrentLinkedQueue. It works ok in the first launch.
If the application is stopped by back key or home key, then the application is relaunch, the problem is there. In main thread, the Queue has data, but in AsyncTask, Queue length is zero.
However, the application is forced stopped and relaunched, then it is fine.
What could be the issue with this Queue?
I use Fragment.
public class OneFragment extends Fragment implements SensorEventListener {
Queue<Float> queuex = new ConcurrentLinkedQueue<Float>();
Queue<Float> queuey = new ConcurrentLinkedQueue<Float>();
Queue<Float> queuez = new ConcurrentLinkedQueue<Float>();
public void onCreate(Bundle savedInstanceState) {
}
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
}
#Override
public void onSensorChanged(SensorEvent event) {
queuex.add(Float.valueOf(deltaX));
queuey.add(Float.valueOf(deltaY));
queuez.add(Float.valueOf(deltaZ));
}
protected class Update extends AsyncTask<Context, Integer, String> {
float x_,y,z;
#Override
protected String doInBackground(Context... params) {
int i = 0;
while (true) {
try {
if(!queuez.isEmpty()) { //Always zero after relaunch if the app is stopped by pressing home or back buttons.
Float g_x = queuex.poll();
x_ = g_x.floatValue();
Float g_y = queuey.poll();
y = g_y.floatValue();
Float g_z = queuez.poll();
z = g_z.floatValue();
publishProgress(i);
i++;
if(systemON == true)
mVibrator.vibrate(1000);
}
} catch (Exception e) {
}
}
//return "COMPLETE!";
}
// -- gets called just before thread begins
#Override
protected void onPreExecute() {
super.onPreExecute();
}
#Override
protected void onProgressUpdate(Integer... values) {
super.onProgressUpdate(values);
mSeriesX.add(x, x_);
mSeriesY.add(x, y);
mSeriesZ.add(x++, z);
if(x%50 == 0) {
mRenderer.setXAxisMin(x);
mRenderer.setXAxisMax(x+50);
}
if (mChartView != null) {
mChartView.repaint();
}
}
// -- called if the cancel button is pressed
#Override
protected void onCancelled() {
super.onCancelled();
}
#Override
protected void onPostExecute(String result) {
super.onPostExecute(result);
}
}
}
First off, you should never have an infinite loop in an AsyncTask. Use a Thread instead. In fact if your task will take longer than a few seconds you should use a Thread, because multiple tasks can't execute concurrently (default implementation is to share a single task thread).
Secondly- your problem is due to the fact the task runs forever. That means the task for the previous activity (remember back finishes the activity) is still running when your new Activity launches. So your old task is still running, and it has access to the old instance of Activity- thus the old instance of the queue. The new instance of the activity is writing to the new instance of the queue, but the old task can't see it.
You should move from an AsyncTask to a Thread so that you don't have the single task problem. You should also interrupt the thread when your activity is destroyed, and have the loop in the thread check isInterrupted to see if the thread is interrupted, and if so exit the loop to kill the thread as well.
I trying to do some long time operation in service which is started from the controller.
public void startDashboardBgService() {
Boolean isAppInForeground = Container.getInstance().isAppInForeground();
if (isAppInForeground) {
stopDashboardBgService();
context.startService(new Intent(context, DashboardService.class));
}
}
Then i calling long time operation method from the onStartCommand event.
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Logger.d("onStartCommand");
updateDashboardValues();
handler = new Handler();
context = this;
return mStartMode;
}
And here is the method which should to do work on the separated thread.
private Runnable runnableCode = new Runnable() {
#Override
public void run() {
List<Reminder> reminders = Reminder.getAllRemindersFromDB(context);
if (reminders.size() > 0) {
for (Reminder r : reminders) {
Logger.d(r.getMessage());
}
} else {
Logger.d("No reminders in table, dashboard cannot be updated");
}
}
};
private void updateDashboardValues() {
try {
Thread t = new Thread(runnableCode);
t.start();
} catch (Exception e) {
TrackingEventLogHelper.logException(e, Constants.Global.EXCEPTION,
Constants.ExceptionMessage.EXC_CANNOT_CANCEL_UNIQUE_ALARM_FOR_DASHBOARD_CHECK, true);
}
}
But when is the method called from activity (controller) UI is lagging and i getting following message.
Skipped 604 frames! The application may be doing too much work on its main thread.
How can i solve it please?
Many thanks for any advice.
Try running the code inside an AsyncTask, see an example below. Note: you can parameterize it with any types you need.
Edit: example of how to pass parameters (Context in this case) to the task.
new AsyncTask<Context, Void, Void>() {
//local fields
#Override
protected void onPreExecute() {
//runs on UI Thread
}
#Override
protected void onCancelled() {
/* runs on UI Thread instead of onPostExecute()
if cancel(boolean) was called */
}
#Override
protected void onProgressUpdate(Progress... values) {
//runs on UI Thread
}
#Override
protected Void doInBackground(Context... params) {
//background Thread - do heavy work here
Context c = params[0];
return null;
}
#Override
protected void onPostExecute(Void result) {
//runs on UI Thread
}
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, mContext);
You can create a new Thread & a Runnable and wrap your method in it.
Or you can use an IntentService which takes care of running your methods in a worker thread automatically
Android Services by default, run on UI thread. If you have to do some heavy work you need to perform it on worker thread.
It would be better to use IntentService for long running operations, because using AsyncTask for long running operations is not recommended.
I am opening a progressdialog with AsyncTask in doInBackground method the question is loading from database and after question successfully loaded the progress dialog box will be closed
but my problem is some time I am getting following error
android.view.WindowManager$BadTokenException: Unable to add window -- token android.view.ViewRoot$W#44757528 is not valid; is your activity running?
by doing some googling I have found that there may be i am holding on to a reference to a Context (either explicitly, or by creating a Dialog or Toast or some other dependent item) that has been destroyed (typically because you are using the onCreateDialog or you passed the Activity to some other process that didn't get destroyed when the Activity was destroyed).
So I have put below code that dismiss progressdialog in-case if activity is destroyed before dialog box is dismissed
protected void onDestroy() {
if (pdForNewQuestion != null)
pdForNewQuestion.dismiss();
super.onDestroy();
}
but I still face the issue. I am not destroying any activity but still the error suddenly comes sometimes and sometimes it works properly
the async code is below
// Start new question in every 60 seconds :)
new Thread(new Runnable() {
public void run() {
while (true) {
try {
Thread.sleep(1000);
mProgressStatus++;
} catch (Exception e) {
e.printStackTrace();
}
runOnUiThread(new Runnable() {
public void run() {
mProgress.setProgress(mProgressStatus);
txtCountingNum.setText((timer--) + "\nSec.");
if (timer < 0) {
questionLoadWithAsyncTask();
}
}
});
}
}
}).start();
public void questionLoadWithAsyncTask() {
new AsyncTask<Void, Void, Void>() {
#Override
protected void onPreExecute() {
pdForNewQuestion = new ProgressDialog(QuizActivity.this);
pdForNewQuestion.setTitle("Please wait...");
pdForNewQuestion.setMessage("Question is loading...");
pdForNewQuestion.setCancelable(false);
pdForNewQuestion.setIndeterminate(true);
pdForNewQuestion.show();
}
#Override
protected Void doInBackground(Void... arg0) {
wordsCursor = dbHelper.getRandomWords();
return null;
}
#Override
protected void onPostExecute(Void result) {
if (pdForNewQuestion != null) {
pdForNewQuestion.dismiss();
}
}
}.execute();
}
Check whether the dialog is showing or not if dialog is showing then only dismiss like this..
#Override
protected void onDestroy() {
if (pdForNewQuestion != null) {
if (pdForNewQuestion.isShowing()) {
pdForNewQuestion.dismiss();
pdForNewQuestion = null;
}
}
super.onDestroy();
}
you are running infinite loop inside new Thread but not breaking the loop and stopping that Thread. It runs infinitely in the background even when activity goes background. try stopping the Thread once work is finished.
First of all why are you starting your AsyncTask inside a Thread? As i understand you are trying to start an AsyncTask every 60 seconds and populate a new question. There is a much better way to do this using only a Handler and AsyncTask.
Create a Handler and post Runnable which runs every seconds and depending on the result start your AsyncTask :
int mSeconds = 0;
Handler mHandler = new Handler();
mHandler.postDelayed(new Runnable() {
#Override
public void run() {
if(mSeconds == 60){
new QuestionLoader().execute();
mSeconds = 0;
}
mHandler.postDelayed(this, 1000);
mSeconds++;
}
}, 1000);
and you can create your AsyncTask like this :
private class QuestionLoader extends AsyncTask<Void, Void, Void>{
#Override
protected void onPreExecute(){
super.onPreExecute();
pdForNewQuestion = new ProgressDialog(MainActivity.this);
pdForNewQuestion.setTitle("Please wait...");
pdForNewQuestion.setMessage("Question is loading...");
pdForNewQuestion.setCancelable(false);
pdForNewQuestion.setIndeterminate(true);
pdForNewQuestion.show();
}
#Override
protected Void doInBackground(Void... params) {
wordsCursor = dbHelper.getRandomWords();
return null;
}
#Override
protected void onPostExecute(Void result){
super.onPostExecute(result);
if(pdForNewQuestion != null && pdForNewQuestion.isShowing()){
pdForNewQuestion.dismiss();
}
}
}
This is usually caused by your app trying to display a dialog using a previously-finished Activity as a context. Then check that the activity is not closed by some other apps or other triggers before showing the dialog
if (!isFinishing()) {
//showdialog here
}
I am very confused, i am trying for two days to make animation on an image while my engine is thinking of a move (this is a game app). I execute the chooseMove() method inside AsyncTask object because it is a little heavy recursive function that may take some time,and also i want to rotate an hourglass image while the engine is thinking. and i tried to do it every way i know: another AsyncTask, Android's own animation class,handlers etc'. but no matter what i do it seems that i can't make it work. if i try to execute two threads at the same time,it only executes one of them, and the same thing happens with android own animation class. so i tried to use a progress dialog just to see that i am not getting crazy,and guess what.. same problem! I show the progress dialog in the onPreExecute() ,but the doInBackgroun() never gets done! the progress dialog take control over the whole app for some reason. how should i do it? i though that the progress dialog is meant for this kind of things. thx!
EDIT: this is the code of my async task class. as you can see, i am showing a progress dialog in the onPreExecute() ,and dismissing it in the onPostExecute. but the onPost never gets called because the doInBackground() never gets called either.
protected void onPreExecute() {
super.onPreExecute();
activity.setPlaying(true);
if (android.os.Build.VERSION.SDK_INT >android.os.Build.VERSION_CODES.GINGERBREAD) {
// only for gingerbread and newer versions
activity.invalidateOptionsMenu();
}
progressDialog = new ProgressDialog(activity);
progressDialog.setMessage("Thinking...");
progressDialog.setIndeterminate(true);
progressDialog.setCanceledOnTouchOutside(false);
progressDialog.show();
}
#Override
protected Integer doInBackground(Void... arg0) {
int engineWin = board.takeWin(board.getBoard(), engine.getNumberOfEngine());
int opponentWin = board.takeWin(board.getBoard(), engine.getNumberOfOpponent());
if(engineWin!=-1){
try{
Thread.sleep(500);
}
catch(Exception e){}
return engineWin;
}
else if(opponentWin!=-1){
try{
Thread.sleep(500);
}
catch(Exception e){}
return opponentWin;
}
else{
if(engine.isEnginesTurn()&&!Board.checkGameOver(board.getBoard())){
int[] newBoard = new int[42];
System.arraycopy(board.getBoard(), 0, newBoard, 0, 42);
return engine.chooseMove(engine.isEnginesTurn(),newBoard,-500001,500001,0,4).getMove();
}
else{
return -1;
}
}
}
#Override
protected void onProgressUpdate(Void... values) {
super.onProgressUpdate(values);
}
#Override
protected void onPostExecute(Integer result) {
super.onPostExecute(result);
progressDialog.dismiss();
if(result!=-1){
ballAnimTask = new BallAnimTask(activity,boardView,board,engine);
ballAnimTask.execute(findSlotNumberByIndex(result));
}
}
this is the recursive chooseMove() i am calling in doInBackground(). before i tried to show the progress dialog,everything worked just fine. there is no problem with this function or any other function for that matter. only when i tried to do animations or dialogs at the same time,i got issues. the chooseMove() is physically on another class and i am only calling it from the AsyncTask. maybe this is the problem??
public Best chooseMove(boolean side,int[]board,int alpha,int beta,int depth,int maxDepth){
Best myBest = new Best();
Best reply;
int num;
if(Board.checkGameOver(board)||depth==maxDepth){
myBest.setScore(returnPositionScore(board));
return myBest;
}
if(side){
myBest.setScore(alpha);
num = numberOfEngine;
}
else{
myBest.setScore(beta);
num = numberOfOpponent;
}
ArrayList<Integer> availableMoves = new ArrayList<Integer>();
availableMoves = searchAvailableMoves(board);
for(int move:availableMoves){
board[move] = num;
reply = chooseMove(!side,board,alpha,beta,depth+1,maxDepth);
board[move] = 0;
if(side&&reply.getScore()>myBest.getScore()){
myBest.setMove(move);
myBest.setScore(reply.getScore());
alpha = reply.getScore();
}
else if(!side&&reply.getScore()<myBest.getScore()){
myBest.setMove(move);
myBest.setScore(reply.getScore());
beta = reply.getScore();
}
if(alpha>=beta){
return myBest;
}
}
return myBest;
}
If you want to perform any UI related operation from within an AsyncTask you need to call the AsyncTask publishProgress method. This will invoke an onProgressUpdate callback in the main UI thread and so you can then safely perform your UI related operations there.
Obviously any code in the onProgressUpdate can't block, though, since you are now back in the main UI thread. But you can update a status bar, for example, and then wait for the next onProgressUpdate callback before you update it again.
Just for some inspiration, here is the general code format that I have used successfully to show a horizontal progress bar which updates while a background task is running. I know it doesn't answer your exact question, but I hope it helps you to find your solution.
ProgressDialog myProgressDialog;
private void someFunctionThatStartsTheAsyncTask()
{
new myBackgroundTask.execute(someIntegerArgument);
}
private class myBackgroundTask extends AsyncTask<Integer, Integer, Boolean>
{
protected void onPreExecute()
{
myProgressDialog = new ProgressDialog(myContext);
myProgressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
myProgressDialog.setTitle("Doing something in background...");
myProgressDialog.setMessage("Please wait...");
myProgressDialog.setCancelable(false);
myProgressDialog.setIndeterminate(false);
myProgressDialog.setMax(100);
myProgressDialog.setProgress(0);
myProgressDialog.show();
}
protected Boolean doInBackground(Integer... params)
{
int someVariable = params[0];
boolean success;
for (whatever loop conditions)
{
// some long-running stuff, setting 'success' at some point
...
publishProgress(newPositionForProgressBar);
}
return success;
}
protected void onProgressUpdate(Integer... progressPosition)
{
myProgressDialog.setProgress(progressPosition[0]);
}
protected void onPostExecute(Boolean result)
{
myProgressDialog.dismiss();
if (result)
{
//doInBackground returned true
}
else
{
//doInBackground returned false
}
}
}
I try to use this code to prevent multi-click in ImageView but it doesn't help.
Boolean isClicked = false;
#Override
public void onClick(View v)
{
if (v == imgClick && !isClicked)
{
//lock the image
isClicked = true;
Log.d(TAG, "button click");
try
{
//I try to do some thing and then release the image view
Thread.sleep(2000);
} catch (InterruptedException e)
{
e.printStackTrace();
}
isClicked = false;
}
}
In the log cat, I can see 5 lines "button click" when I click on ImageView for 5 times as quickly as possible. I can see the log cat print the first line, wait for a while (2 seconds) and then print the next line. I think when I click the ImageView, the fired event is moved to queue in order, isn't it?. So how can I stop that?
I also try to use setEnable() or setClickable() instead of isClicked variable but it doesn't work too.
Just try this working code
Boolean canClick = true; //make global variable
Handler myHandler = new Handler();
#Override
public void onClick(View v)
{
if (canClick)
{
canClick= false; //lock the image
myHandler.postDelayed(mMyRunnable, 2000);
//perform your action here
}
}
/* give some delay..*/
private Runnable mMyRunnable = new Runnable()
{
#Override
public void run()
{
canClick = true;
myHandler.removeMessages(0);
}
};
Instead of sleeping in 2 seconds, I use some task like doSomeThing() method (has accessed UI thread), and I don't know when it completed. So how can I try your way?
//I referred this android link. You can handle thread more efficiently but i hope below code will work for you..
//you try this and
Boolean canClick = true; //make global variable
public void onClick(View v) {
if(canClick){
new DownloadImageTask().execute();
}
}
private class DownloadImageTask extends AsyncTask<String, Void, Bitmap> {
protected Bitmap doInBackground(String... urls) {
Log.d("MSG","Clicked");
canClick =false;
//perform your long operation here
return null;
}
protected void onPostExecute(Bitmap result) {
canClick =true;
}
}
You could keep track of the last consumed click upon your View, and based on it either perform the necessary actions, or simply return:
private long calcTime;
private boolean isClickedLately(final long millisToWait)
{
if (System.currentTimeMillis() - calcTime < millisToWait)
return true;
return false;
}
#Override
public void onClick(View v)
{
if (isClickedLately(2000))
return;
calcTime = System.currentTimeMillis();
Log.d(TAG, "consuming button click");
// perform the necessary actions
}
With the millisToWait parameter you can adjust the threshold of "waiting", but if you know that you want to wait exactly 2 seconds between two consecutive clicks, you can eliminate it.
This way you don't have to deal with Threads, which is good, since it's not a great idea to make the gui thread wait.