ConcurrentLinkedQueue usage between main thread and AsyncTask in Android - java

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.

Related

Stop the onPostExecute of an Async Task in java

I have the following code. My problem is, that I can't get the JSON.execute() to stop/cancel. I spend quite some time looking up possible answers but I wasn't able to find anything that really worked (e.g. JSON.cancel(true)). As soon as I turn the trackerswitch on, the AsnycTask starts running every 3 seconds just like it's supposed to. Is there a way to easily stop the AsyncTask from executing as soon as the trackerswitch is turned off?
private boolean tracking = false;
private Switch trackerswitch;
private final Timer timer= new Timer();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.table_layout);
final Handler handler=new Handler();
final int delay = 4000;
trackerswitch=findViewById(R.id.trackerswitch);
trackerswitch.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
NetworkAccess JSON = new NetworkAccess();
if(trackerswitch.isChecked()){
trackerswitch.setText("Tracking...");
tracking=true;
handler.postDelayed(new Runnable() {
#Override
public void run() {
NetworkAccess JSON = new NetworkAccess();
JSON.execute();
handler.postDelayed(this, delay);
}
},delay);
}
else{
tracking=false;
trackerswitch.setText("Start Tracking");
}
}
});
}
}
This is what's called in the network class:
public class NetworkAccess extends AsyncTask<Void, Void, Void> {
public ArrayList<String> alldata = new ArrayList<>();
public ArrayList<String> locationlist = new ArrayList<>();
int stride;
String data;
#Override
protected Void doInBackground(Void... voids) {//4B4ADC
SOME CODE WHICH ISN'T IMPORTANT FOR MY PROBLEM
alldata.addAll(elementlist);
locationlist.addAll(loctrack);
}
}
catch(IOException | JSONException e){
MainActivity.field1.setText(e.getClass().getCanonicalName());
}
return null;
}
#Override
protected void onPostExecute(Void aVoid) {
super.onPostExecute(aVoid);
MainActivity.field1.setText(String.format("%20s %20s", alldata.get(0), alldata.get(1)));
COUPLE MORE OF THESE SETTEXT COMMANDS TO FILL A TABLE WITH DATA
}
}
Thanks for your help!
handler.postDelayed() adds objects of the Runnable you provide to the message queue, to be run at the specified interval. You need to remove all the queued objects from the message queue in order to cancel the execution. Calling JSON.cancel(true) does not affect other objects that are already added to the queue.
You'll have to retain a reference to your Runnable implementation and then call handler.removeCallbacks(r) to prevent further executions. Instead of using an anonymous class in handler.postDelayed().
This documentation page sheds more light on the matter.
Also refer this page for what happens when you call cancel(true) on an AsyncTask.

Android : How to Run method in service on different thread?

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.

Android: AsyncTask: Long execution time after calling onPostExecute

I have a simple Android AsyncTask with a long background action:
HANDLER.post(new Runnable() {
#Override
public void run() {
new AsyncTask<Context, Integer, GameTypeAdapter>() {
#Override
protected GameTypeAdapter doInBackground(Context... contexts) {
return new GameTypeAdapter(contexts[0]);//Long database action
}
#Override
protected void onPreExecute() {
pbWaiter.setVisible(View.VISIBLE);
}
#Override
protected void onPostExecute(GameTypeAdapter gameTypeAdapter) {
txtGameName.setText(settings.getGameName());
cmbType.setAdapter(gameTypeAdapter);
if (settings.getGamePlugin() != null) {
cmbType.setSelection(((GameTypeAdapter) cmbType.getAdapter()).getItemPosition(settings.getGamePlugin()));
}
sldJoker.setProgress(settings.getJoker());
pbWaiter.setVisible(View.INVISIBLE);
}
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, getActivity());
}
});
If the method onPostExecute is called by thread and the last line (hideWaiter) was called the execution of the UI calls are visible after 1/10 seconds (a half second in debug mode). In this time the UI hangs and the waiter animation stops.
Why the UI thread stops?
Thanks.

Async Task with Media Player isn't Firing PublishProgress

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.

Show an AlertDialog from a background Thread with the appcontext

Is possible to raise an AlertDialog from a background thread using a reference to getApplicationContext()?
I'm trying with that code but it doesn't work
new Thread(){
public void run(){
new AlertDialog.Builder(appcontext)
.setMessage("Test")
.setPositiveButton("Ok", null)
.show();
}
}.start();
Thanks in advance
No, you do not want to do this. Android does not permit UI work on any thread but the UI-thread because UI code is not thread safe. See "Painless Threading"1.
You can call Activity.runOnUiThread(Runnable) (on the specific activity) from another thread to force the code to run on the UI thread. You can also call View.post(Runnable) (on the specific view) to cause the action to be queued onto the UI thread. For more details on those options and others, see the above mentioned article.
However, Android also provides something called AsyncTask which is specifically designed for running some stuff on a separate thread, and some on the UI thread. This automatically uses Android's threadpool, and if you do not have any reason to use an explicit separate thread, is an easy, clean way to go:
From the Android docs:
private class DownloadFilesTask extends AsyncTask<URL, Integer, Long> {
// Runs on a ThreadPool thread
protected Long doInBackground(URL... urls) {
int count = urls.length;
long totalSize = 0;
for (int i = 0; i < count; i++) {
totalSize += Downloader.downloadFile(urls[i]);
// Sends data to onProgressUpdate to run on the UI thread
publishProgress((int) ((i / (float) count) * 100));
}
return totalSize;
}
// Runs on the UI thread!
protected void onProgressUpdate(Integer... progress) {
setProgressPercent(progress[0]);
}
// Runs on the UI thread!
protected void onPostExecute(Long result) {
showDialog("Downloaded " + result + " bytes");
}
}
you cannot access UI elements from a thread, you must create a handler and call it from your thread.
1- handler: to handle UI from other thread
private Handler handler= new Handler() {
public void handleMessage(Message msg){
/*put your code here to update on UI*/
}
};
2- in you thread call this:
Thread t = new Thread(new Runnable() {
#Override
public void run() {
handler.sendEmptyMessage(0);
}
}); //thread
t.start();
Simplest Solution is to use an AsyncTask.
try the following code
private class LaunchDialog extends AsyncTask<Void,Void,Void>{
Context context;
public LaunchDialog(Context ctx){
context = ctx;
}
#Override
protected ArrayList<CategoryObj> doInBackground(Void... params) {
//do the task to be done on NON-UI thread , or NON-Blocking thread
// publishProgress(null);
}
#Override
protected void onProgressUpdate(Void... v){
//stuff done on UI thread , can be invoked from doInBackground
}
#Override
protected void onPostExecute(Void x){
//stuff to be done after task executes(done on UI thread)
new AlertDialog.Builder(context)
.setMessage("Test")
.setPositiveButton("Ok", null)
.show();
}
#Override
protected void onPreExecute(){
//stuff to be done before task executes (done on UI thread)
}
}
to start the thread just do
new LaunchDialog(this).execute();
read article about painless threading here -- http://developer.android.com/resources/articles/painless-threading.html
You'll have to get access to the UI thread, any of these: View.post(), Activity.runOnUiThread(), or obtaining and sending a Message.
You cannot change UI in background thread. There is another thread to perform UI activities
runOnUiThread(new Runnable() {
#Override
public void run() {
//perform UI operations
}
});

Categories