I am dealing with the android UI and I am facing what looks a very common problem here. By using an AsyncTask, I want to:
1- Show a ProgressDialog meanwhile some stuff gets ready
2- Show a countdown to let the user get ready for the real action
3- Play a sound afterwards the countdown to notify the sample will be taken
4- Show another ProgressDialog representing the 10s sample taking
5- Play a sound afterwards the sample is taken
Well, this is my outcome:
1- Works fine
2- MISSING, THE UI IS NOT UPDATED BUT THE BACKGROUND PROCESS IS RUNNING
3- Works fine
4- Works fine
5- Works fine
The funniest is, when I remove the code to handle the first part that handles the first progress dialog, the other parts are executed/displayed as expected. I understand there is something blocking the UI at some point but I am quite newbie with android to realize what's blocking it.
Thanks in advance for your help.
public class SnapshotActivity extends AppCompatActivity {
private int COUNTDOWN_TIME = 5;
private Button startStop;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_snapshot);
startStop = (Button) findViewById(R.id.btnStartStop);
startStop.setText("START");
}
public void startSnapshot(View view) {
startStop.setClickable(false);
new Async(SnapshotActivity.this).execute();
}
class Async extends AsyncTask<Void, Void, Void>{
TextView tv = (TextView) findViewById(R.id.tvCountDown);
ProgressDialog progressDialog ;
ProgressDialog preparing;
Context context;
int flag = 0;
int counter = COUNTDOWN_TIME;
public Async(Context context) {
this.context = context;
progressDialog = new ProgressDialog(context);
preparing = new ProgressDialog(context);
}
#Override
protected void onPreExecute(){
super.onPreExecute();
}
#Override
protected Void doInBackground(Void... params) {
//PROGRESS DIALOG
flag = 4;
publishProgress();
try {
//SIMULATE SOME WORKLOAD
Thread.sleep(2000);
} catch (InterruptedException ex) {
Thread.currentThread().interrupt();
}
flag = 5;
publishProgress();
//HANDLE THE COUNTDOWN
for(counter = COUNTDOWN_TIME; counter>=1; counter--){
publishProgress();
try {
Thread.sleep(1000);
} catch (InterruptedException ex) {
Thread.currentThread().interrupt();
}
}
//PLAY THE SOUND
new Thread(new Runnable() {
#Override
public void run() {
Uri notification = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
Ringtone r = RingtoneManager.getRingtone(getApplicationContext(), notification);
r.play();
}
}).start();
//PROGRESS DIALOG
flag = 1;
publishProgress();
//10s SAMPLE
flag = 2;
for(int j = 0; j <= 10; j++ ){
try {
Thread.sleep(1000);
} catch (InterruptedException ex) {
Thread.currentThread().interrupt();
}
publishProgress();
}
//PLAY THE SOUND
new Thread(new Runnable() {
#Override
public void run() {
Uri notification = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
Ringtone r = RingtoneManager.getRingtone(getApplicationContext(), notification);
r.play();
}
}).start();
flag = 3;
publishProgress();
return null;
}
#Override
protected void onProgressUpdate(Void... values) {
super.onProgressUpdate(values);
switch (flag) {
case 0:
tv.setText(String.valueOf(counter));
break;
case 1:
tv.setText("");
progressDialog.setTitle("TAIKING SAMPLE");
progressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
progressDialog.setMax(10);
progressDialog.setCanceledOnTouchOutside(false);
progressDialog.show();
break;
case 2:
progressDialog.incrementProgressBy(1);
break;
case 3:
progressDialog.dismiss();
break;
case 4:
preparing.setMessage("Starting the device...");
preparing.setCanceledOnTouchOutside(false);
preparing.show();
break;
case 5:
preparing.dismiss();
break;
default:
break;
}
}
#Override
protected void onPostExecute(Void result) {
super.onPostExecute(result);
startStop.setClickable(true);
}
}
}
There are two issues:
You are not informing onProgressUpdate() of the countdown value
There is a race condition
doInBackground() and onProgressUpdate() methods are executed in two different threads and both methods/threads are accessing the flag field in a unsafe way. Instead of setting a value in flag in doInBackground() to be read in onProgressUpdate(), you can inform this value directly in the publishProgress() call. So I advise you to do the following:
public class SnapshotActivity extends AppCompatActivity {
// ...
// Change the Progress generic type to Integer.
class Async extends AsyncTask<Void, Integer, Void> {
// Remove the flag field.
// int flag = 0;
#Override
protected Void doInBackground(Void... params) {
//PROGRESS DIALOG
// Pass the flag argument in publishProgress() instead.
// flag = 4;
publishProgress(4);
// ...
// flag = 5;
publishProgress(5);
//HANDLE THE COUNTDOWN
for (counter = COUNTDOWN_TIME; counter>=1; counter--){
// The countdown progress has two parameters:
// a flag indicating countdown and
// the countdown value.
publishProgress(6, counter);
// ...
}
//PLAY THE SOUND
// ...
//PROGRESS DIALOG
// flag = 1;
publishProgress(1);
//10s SAMPLE
// flag = 2;
for(int j = 0; j <= 10; j++ ){
// ...
publishProgress(2);
}
//PLAY THE SOUND
// ...
// flag = 3;
publishProgress(3);
return null;
}
// This signature changes.
#Override
protected void onProgressUpdate(Integer... values) {
super.onProgressUpdate(values);
// Redeclare flag here.
int flag = values[0];
switch (flag) {
// ...
case 6:
tv.setText(Integer.toString(values[1]));
break;
// ...
}
}
}
}
Related
im building an android application that recive images from arduino uno in order to show them continously as a video , i write an asyncTask that reads image and show it in image view , how can i invoke this method every seconed automatically .
here is my asyncTask
I made a button that invoke the async task , but how to make it invoked continously
class myAsyncTask extends AsyncTask<Void, Void, Void>
{
#Override
protected Void doInBackground(Void... params) {
// TODO Auto-generated method stub
mmInStream = tmpIn;
int byteNo;
try {
byteNo = mmInStream.read(buffer);
if (byteNo != -1) {
//ensure DATAMAXSIZE Byte is read.
int byteNo2 = byteNo;
int bufferSize = 7340;
int i = 0;
while(byteNo2 != bufferSize){
i++;
bufferSize = bufferSize - byteNo2;
byteNo2 = mmInStream.read(buffer,byteNo,bufferSize);
if(byteNo2 == -1){
break;
}
byteNo = byteNo+byteNo2;
}
}
}
catch (Exception e) {
// TODO: handle exception
}
return null;
}
protected void onPostExecute(Void result) {
super.onPostExecute(result);
bm1 = BitmapFactory.decodeByteArray(buffer, 0, buffer.length);
image.setImageBitmap(bm1);
}
}
If it's from a background thread, one possibility is to use an unbounded for loop. For example, suppose the AsyncTask currently does:
private class MyTask extends AsyncTask<T1, Void, T3>
{
protected T3 doInBackground(T1... value)
{
return longThing(value);
}
protected void onPostExecute(T3 result)
{
updateUI(result);
}
}
then rewrite it as something like:
private class MyTask extends AsyncTask<T1, T3, T3>
{
protected T3 doInBackground(T1... value)
{
for (;;)
{
T3 result = longThing(value);
publishProgress(result);
Thread.sleep(1000);
}
return null;
}
protected void onProgressUpdate(T3... progress)
{
updateUI(progress[0]);
}
}
Of course, you should have a check to break the loop (for example when the Activity is paused or destroyed).
Another option is to create a Handler instance and call postDelayed() repeatedly.
Handler h = new Handler();
h.postDelayed(r, DELAY_IN_MS);
Runnable r = new new Runnable() {
public void run() {
// Do your stuff here
h.postDelayed(this, DELAY_IN_MS);
}
}
I the code blow.
And I need to call execute within a loop.
Problem is I need the loop to pause while execute is completing and then continue.
The loop runs but it calls execute immediately, which causes the files not be to downloaded.
private class DownloadVerses extends AsyncTask<String, Void, String> {
#Override
protected String doInBackground(String... params) {
StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build();
StrictMode.setThreadPolicy(policy);
try {
//Thread.sleep(5000);
if(Utils.downloadFile(params[0])){
int progressPercentage = Integer.parseInt(params[2]);
downloadProgress.setProgress(progressPercentage);
return "Downloading: "+params[1];
}
else{
return "ERROR Downloading: "+params[1];
}
} catch (Exception e) {
Thread.interrupted();
return "ERROR Downloading: "+params[1];
}
}
#Override
protected void onPostExecute(String result) {
if(result.contains("ERROR")){
downloading.setTextColor(Color.parseColor("#f05036"));
}
else{
downloading.setTextColor(Color.parseColor("#79a1ad"));
}
downloading.setText(result);
}
#Override
protected void onPreExecute() {}
#Override
protected void onProgressUpdate(Void... values) {}
}
And I am currently trying:
DownloadVerses download = new DownloadVerses();
int i = 0;
while(i < verseTitles.size()){
String progressString = String.valueOf(progress);
if(download.getStatus() != AsyncTask.Status.RUNNING){
download.execute(Mp3s.get(i), Titles.get(i),progressString);
if(progress < 100){
if((progress + increment) > 100){
progress = 100;
}
else{
progress += increment;
}
}
i++;
}
}
Remove this line...
DownloadVerses download = new DownloadVerses();
And every time create a new object of DownloadVerses to execute a new AsyncTask...This procedure will not stop any AsyncTask from execution and it will allow every AsyncTask to execute independently.
new DownloadVerses().execute(Mp3s.get(i), Titles.get(i),progressString);
Update your code as follows...
int i = 0;
while(i < verseTitles.size()){
String progressString = String.valueOf(progress);
if(download.getStatus() != AsyncTask.Status.RUNNING){
new DownloadVerses().execute(Mp3s.get(i), Titles.get(i),progressString);
if(progress < 100){
if((progress + increment) > 100){
progress = 100;
}
else{
progress += increment;
}
}
i++;
}
}
Update:
You can use onProgressUpdate() method to update progress update. For more details, you can see AsyncTask document from Android Developer site.
I am using this code to play a sound
final MediaPlayer mp = MediaPlayer.create(this, R.raw.sound);
mp.setOnCompletionListener(new OnCompletionListener() {
#Override
public void onCompletion(MediaPlayer mp) {
mp.release();
}
});
It works fine on its own, however there was a problem after I added an animation that extends ImageView, which refreshes(by calling handler.postDelayed) the image resource at an interval about 30ms to create animation. The problem is that when the animation starts, it terminates the the playing of the sound. Here is the code for the Runnable that refreshes the ImageView.
private Runnable runnable = new Runnable () {
public void run() {
String name = "frame_" + frameCount;
frameCount ++;
int resId = mContext.getResources().getIdentifier(name, "drawable", mContext.getPackageName());
imageView.setImageResource(resId);
if(frameCount < totalFrameCount) {
mHandler.postDelayed(runnable, interval);
}
}
};
I also tried to use a thread that calls the anmiationView.postInvalidate to do the animation, however it has the same problem. Please help. Thanks
Edit:
It looks like the problem is due to WHEN the animation is called. Previously I called it in the onActivityResult of the activity. Looks like this is not the right place to call. Now I put the animation view in a popupWindow and play it there, it works properly. Not sure exactly why.
in handler's comments :
"A Handler allows you to send and process {#link Message} and Runnable
objects associated with a thread's {#link MessageQueue}. Each Handler
instance is associated with a single thread and that thread's message
queue. When you create a new Handler, it is bound to the thread /
message queue of the thread that is creating it -- from that point on,
it will deliver messages and runnables to that message queue and execute
them as they come out of the message queue."
so, the problem may be caused by both of animation and media playing operations are in
the same message queue own by which thread create the handler (let's say the main thread).
if the animation loops for ever, then the media player will hardly get any chance to run.
you could take it a try with HandlerThread, the thread will contain a new looper for the
handler created from it, all the runnables added to that handler will be running in another
individual thread.
the animation thread and the media play thread should be running in the different threads not
scheduling in the same one.
hope, it helps.
the HandlerThread usage and some discuss looks like this :
How to create a Looper thread, then send it a message immediately?
maybe it is caused by your miss arranged codes, i take it a try on my nexus 4 with android version 4.4.2, even no any cache tech, the animation and music works like a charm...
here is the major codes :
public class MainActivity extends Activity implements View.OnClickListener {
protected static final String TAG = "test002" ;
protected static final int UPDATE_ANI = 0x0701;
protected static final int UPDATE_END = 0x0702;
protected static final int[] ANI_IMG_IDS = {R.raw.img1, R.raw.img2, R.raw.img3, R.raw.img4,
R.raw.img5, R.raw.img6, R.raw.img7};
protected static final int[] BTN_IDS = {R.id.btnStart, R.id.btnStop};
protected android.os.Handler aniHandler = null; // async update
protected boolean isAniRunning = false ;
protected int aniImgIndex = 0 ;
protected ImageView aniImgView = null ;
protected MediaPlayer mediaPly = null ;
// animation timer
class AniUpdateRunnable implements Runnable {
public void run() {
Message msg = null ;
while (!Thread.currentThread().isInterrupted() && isAniRunning) {
msg = new Message();
msg.what = UPDATE_ANI;
aniHandler.sendMessage(msg);
try {
Thread.sleep(500);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
break ;
}
}
msg = new Message() ;
msg.what = UPDATE_END ;
aniHandler.sendMessage(msg) ;
}
}
protected void prepareMediaPlayer(MediaPlayer mp, int resource_id) {
AssetFileDescriptor afd = getResources().openRawResourceFd(resource_id);
try {
mp.reset();
mp.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getDeclaredLength());
afd.close();
mp.prepare();
} catch (IllegalArgumentException e) {
Log.d(TAG, "IlleagalArgumentException happened - " + e.toString()) ;
} catch(IllegalStateException e) {
Log.d(TAG, "IllegalStateException happened - " + e.toString()) ;
} catch(IOException e) {
Log.d(TAG, "IOException happened - " + e.toString()) ;
}
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// init : buttons onclick callback
{
Button btn;
int i;
for (i = 0; i < BTN_IDS.length; i++) {
btn = (Button) findViewById(BTN_IDS[i]);
btn.setOnClickListener(this);
}
}
// init : update animation handler callback
{
aniHandler = new Handler() {
public void handleMessage(Message msg) {
switch (msg.what) {
case UPDATE_ANI:
updateAniImages();
break ;
case UPDATE_END:
updateAniEnd();
break ;
default:
break;
}
}
};
}
// init : prepare image view
{
aniImgView = (ImageView)findViewById(R.id.imgAni) ;
mediaPly = MediaPlayer.create(this, R.raw.happyny) ;
mediaPly.setLooping(true);
}
}
protected void updateAniImages() {
if(aniImgIndex >= ANI_IMG_IDS.length) {
aniImgIndex = 0 ;
}
InputStream is = getResources().openRawResource(ANI_IMG_IDS[aniImgIndex]) ;
Bitmap bmp = (Bitmap) BitmapFactory.decodeStream(is) ;
aniImgView.setImageBitmap(bmp);
aniImgIndex++ ;
}
protected void updateAniEnd() {
aniImgIndex = 0 ;
aniImgView.setImageBitmap(null);
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
#Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.btnStart:
isAniRunning = true ;
// no re-enter protectiion, should not be used in real project
new Thread(new AniUpdateRunnable()).start();
mediaPly.start();
break;
case R.id.btnStop:
isAniRunning = false ;
mediaPly.stop();
prepareMediaPlayer(mediaPly, R.raw.happyny);
break;
default:
break;
}
}
}
the major project codes and test apk should be find here :
apk installer
source code
I have created a program in android for multithreading.
When I hit one of the button its thread starts and print value to EditText now I want to determine that thread is running or not so that I can stop the thread on click if it is running and start a new thread if it is not running here is mu code:
public void startProgress(View view) {
final String v;
if(view == b1)
{
v = "b1";
}
else
{
v = "b2";
}
// Do something long
Runnable runnable = new Runnable() {
#Override
public void run() {
//for (int i = 0; i <= 10; i++) {
while(true){
if(v.equals("b1"))
{
i++;
}
else if(v.equals("b2"))
{
j++;
}
try {
if(v.equals("b1"))
{
Thread.sleep(3000);
}
else if(v.equals("b2"))
{
Thread.sleep(1000);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
handler.post(new Runnable() {
#Override
public void run() {
// progress.setProgress(value);
if(v.equals("b1"))
{
String strValue = ""+i;
t1.setText(strValue);
}
else
{
String strValue = ""+j;
t2.setText(strValue);
}
//t1.setText(value);
}
});
}
}
};
new Thread(runnable).start();
}
#Override
public void onClick(View v) {
if(v == b1)
{
startProgress(b1);
}
else if(v == b2)
{
startProgress(b2);
}
}
Instead of that messy code, an AsyncTask would do the job you need with added readability ...
It even has a getStatus() function to tell you if it is still running.
You'll find tons of examples by looking around a bit (not gonna write one more here). I'll simply copy the one from the documentation linked above:
Usage
AsyncTask must be subclassed to be used. The subclass will override at least one method (doInBackground(Params...)), and most often will override a second one (onPostExecute(Result).)
Here is an example of subclassing:
private class DownloadFilesTask extends AsyncTask<URL, Integer, Long> {
protected Long doInBackground(URL... urls) {
int count = urls.length;
long totalSize = 0;
for (int i = 0; i < count; i++) {
totalSize += Downloader.downloadFile(urls[i]);
publishProgress((int) ((i / (float) count) * 100));
// Escape early if cancel() is called
if (isCancelled()) break;
}
return totalSize;
}
protected void onProgressUpdate(Integer... progress) {
setProgressPercent(progress[0]);
}
protected void onPostExecute(Long result) {
showDialog("Downloaded " + result + " bytes");
}
}
Once created, a task is executed very simply:
new DownloadFilesTask().execute(url1, url2, url3);
Use a static AtomicBoolean in your thread and flip its value accordingly. If the value of the boolean is true, your thread is already running. Exit the thread if it is true. Before exiting the thread set the value back to false.
There are some way can check the Thread properties
You able to check Thread is Alive() by
Thread.isAlive() method it return boolean.
You able to found runing thread run by
Thread.currentThread().getName()
Thanks
I have a Async Task that does not add the percentage while it is going through the task. It always stays at 0% 0/100
Here is my code
private class getAppInfo extends AsyncTask<String, String, String> {
/** The system calls this to perform work in a worker thread and
* delivers it the parameters given to AsyncTask.execute() */
ProgressDialog dialog;
#Override
protected void onPreExecute() {
if(showLoading == true){
dialog = new ProgressDialog(SelfHelp.this);
dialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
dialog.setMessage("Loading");
dialog.setIndeterminate(true);
dialog.setCancelable(false);
dialog.setMax(100);
dialog.setProgress(100);
dialog.show();
}
}
#Override
protected String doInBackground(String... urls) {
String xml = null;
int count = 0;
try {
// defaultHttpClient
DefaultHttpClient httpClient = new DefaultHttpClient();
HttpGet httpGet = new HttpGet(urls[0]);
HttpResponse httpResponse = httpClient.execute(httpGet);
HttpEntity httpEntity = httpResponse.getEntity();
xml = EntityUtils.toString(httpEntity);
while(count != 100){
publishProgress(""+count);
count += 5;
}
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
} catch (ClientProtocolException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
Document doc = parser.GetDomElement(xml);
NodeList nl = doc.getElementsByTagName("topic");
getChildElements(nl);
return xml;
}
#Override
protected void onProgressUpdate(String... progress) {
Log.v("count",progress[0]);
dialog.setProgress(Integer.parseInt(progress[0]));
}
/** The system calls this to perform work in the UI thread and delivers
* the result from doInBackground() */
#Override
protected void onPostExecute(String result) {
//dialog.setProgress(100);
menuList.setAdapter(setListItems(menuItems));
menuList.setTextFilterEnabled(true);
if(showLoading == true){
dialog.dismiss();
showLoading = false;
}
}
It does go into onProgressUpdate and the count goes up by 5 but the progress bar does not change. How can I have it increment by 5 and show the progress properly?
Your issue is related to setIndeterminate(true): You should set it to false if you want to have progress update. if you setIndeterminate(true) then the ProgressDialog will work as the classic Windows Hourglass
You can try following code, It is showing progress in % ratio, here is the code,
public class ProgressBarExampleActivity extends Activity
{
ProgressThread progThread;
ProgressDialog progDialog;
Button button1, button2;
int typeBar; // Determines type progress bar: 0 = spinner, 1 = horizontal
int delay = 1000; // Milliseconds of delay in the update loop
int maxBarValue = 30; // Maximum value of horizontal progress bar
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
// // Process button to start spinner progress dialog with anonymous inner class
// button1 = (Button) findViewById(R.id.Button01);
// button1.setOnClickListener(new OnClickListener()
// {
// public void onClick(View v)
// {
// typeBar = 0;
// showDialog(typeBar);
// }
// });
// Process button to start horizontal progress bar dialog with anonymous inner class
button2 = (Button) findViewById(R.id.Button02);
button2.setOnClickListener(new OnClickListener()
{
public void onClick(View v)
{
typeBar = 1;
showDialog(typeBar);
}
});
}
// Method to create a progress bar dialog of either spinner or horizontal type
#Override
protected Dialog onCreateDialog(int id)
{
switch(id)
{
// case 0: // Spinner
// progDialog = new ProgressDialog(this);
// progDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
// progDialog.setMessage("Loading...");
// progThread = new ProgressThread(handler);
// progThread.start();
// return progDialog;
case 1: // Horizontal
progDialog = new ProgressDialog(this);
progDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
progDialog.setMax(maxBarValue);
progDialog.setMessage("Dollars in checking account:");
progThread = new ProgressThread(handler);
progThread.start();
return progDialog;
default:
return null;
}
}
// Handler on the main (UI) thread that will receive messages from the
// second thread and update the progress.
final Handler handler = new Handler()
{
public void handleMessage(Message msg)
{
// Get the current value of the variable total from the message data
// and update the progress bar.
int total = msg.getData().getInt("total");
progDialog.setProgress(total);
// if (total >= maxBarValue)
if (total <= 0 )
{
dismissDialog(typeBar);
progThread.setState(ProgressThread.DONE);
}
}
};
// Inner class that performs progress calculations on a second thread. Implement
// the thread by subclassing Thread and overriding its run() method. Also provide
// a setState(state) method to stop the thread gracefully.
private class ProgressThread extends Thread
{
// Class constants defining state of the thread
final static int DONE = 0;
final static int RUNNING = 1;
Handler mHandler;
int mState;
int total;
// Constructor with an argument that specifies Handler on main thread
// to which messages will be sent by this thread.
ProgressThread(Handler h)
{
mHandler = h;
}
// Override the run() method that will be invoked automatically when
// the Thread starts. Do the work required to update the progress bar on this
// thread but send a message to the Handler on the main UI thread to actually
// change the visual representation of the progress. In this example we count
// the index total down to zero, so the horizontal progress bar will start full and
// count down.
#Override
public void run()
{
mState = RUNNING;
total = maxBarValue;
while (mState == RUNNING)
{
// The method Thread.sleep throws an InterruptedException if Thread.interrupt()
// were to be issued while thread is sleeping; the exception must be caught.
try
{
// Control speed of update (but precision of delay not guaranteed)
Thread.sleep(delay);
} catch (InterruptedException e) {
Log.e("ERROR", "Thread was Interrupted");
}
// Send message (with current value of total as data) to Handler on UI thread
// so that it can update the progress bar.
Message msg = mHandler.obtainMessage();
Bundle b = new Bundle();
b.putInt("total", total);
msg.setData(b);
mHandler.sendMessage(msg);
total--; // Count down
}
}
// Set current state of thread (use state=ProgressThread.DONE to stop thread)
public void setState(int state)
{
mState = state;
}
}
}
See the output,
I will mention another aproach, because I came across this solution when I was looking for some practical way how to communicate from my Service running AsyncTask back to main UI. Lucifer's solution is not modular for Services, if you need to use your Service in more then 1 class (that was my case), you won't be able to access variable handler and as far as I know you can't even send Handler as Intent to Service (you can send it to AsyncTask tho). Solution is broadcasting.
sendBroadcast(new Intent(WORK_DONE));
in AsyncTask and
private BroadcastReceiver receiver = new BroadcastReceiver() {
public void onReceive(Context c, Intent i) { //update your UI here }
}
registerReceiver(receiver, new IntentFilter(WORK_DONE));
in your activity.
I don't like all those inner classes android developers use. I understand it's easier to create inner class and access outer class variables, but once you need to use the class again your doomed and you have to edit the code! I am realy new to Android, maybe I am wrong and you actually don't need to reuse those classes. Never did a bigger project so I have no idea but it just doesn't feel right, since on college, they tried hard to teach us how to programm reusable code.