#Override
protected void onCreate (Bundle savedInstanceState) {
progressDialog.show();
if (/* task that returns a boolean value */) {
// Do stuff
}
else {
// Do other stuff
}
progressDialog.dismiss();
}
This code should be showing the progress dialog, wait for the task to produce its result, then evaluate the if statement and dismiss the dialog. But this doesn't happen: the UI thread is blocked, the task is executed and only then is the progress dialog shown, only to be dismissed immediately.
What would be the correct way to solve this problem ?
A simple worker thread.
public void onClick(View v) {
new Thread(new Runnable() {
public void run() {
// a potentially time consuming task
}
}).start();
}
There are other alternatives that can be considered depending on your requirement as mentioned in this answer.
You can use AsyncTask for this purpose, however, it has been deprecated in Sdk 30 and recommended to use the java.concurrent.* utilities directly docs. Following is an workaround using ExecutorService, although it is not perfect, it definitely meets your functionality:
In your Activity (say, MyActivity), create a member of ExecutorService and initialize it. Add method and callback like following, when you want to perform some background task, just call it:
public class MyActivity extends AppCompatActivity {
// You can use your preferred executor
private final ExecutorService executor = new ThreadPoolExecutor(0, 1,
3L, TimeUnit.SECONDS,
new LinkedBlockingQueue<>());
#Override
protected void onCreate (Bundle savedInstanceState) {
// Initiate the task
executeParallel(new Callable<Boolean> {
#Override
public Boolean call() {
// Perform your task and return boolean
return trueOrFalse;
}
}, new Callback<Boolean>() {
#Override
public void onStart() {
// Show progress dialog
progressDialog.show();
}
#Override
public void onComplete(Boolean result) {
if (result) {
// Do some tasks
} else {
// Do other tasks
}
// Remove dialog
progressDialog.dismiss();
}
}, new Handler(Looper.getMainLooper()));
}
public <R> void executeParallel(#NonNull Callable<R> callable, #Nullable Callback<R> callback, Handler handler) {
executor.execute(() -> {
handler.post(() -> {
if (callback != null) {
callback.onStart();
}
});
R r = null;
try {
r = callable.call();
} catch (Exception e) {
// Ignore
} finally {
R result = r;
handler.post(() -> {
if (callback != null) {
callback.onComplete(result);
}
});
}
});
}
public interface Callback<R> {
void onStart();
void onComplete(R result);
}
}
When you are done, just shut down the ExecutorService. This Executor and related methods can be moved into ViewModel if you use them for better design. Remember, you should not use Thread directly to avoid potential memory leak.
Related
I followed this post to set up an Http Async Request: HttpRequest
So, now, I call: new DownloadTask().execute("http://www.google.com/");
to make this request.
How can I manage different calls? For example:
new DownloadTask().execute("http://www.google.com/");
new DownloadTask().execute("http://www.facebook.com/");
new DownloadTask().execute("http://www.twitter.com/");
And have different results?
Pass one more argument to the AsyncTask. Make some constants corresponding to your tasks.
new DownloadTask().execute("http://www.google.com/", DownloadTask.ID_ASYNC1);
new DownloadTask().execute("http://www.facebook.com/", DownloadTask.ID_ASYNC2);
new DownloadTask().execute("http://www.twitter.com/", DownloadTask.ID_ASYNC3);
Inside AsyncTask, use this id to identify which is the request being called.
private class DownloadTask extends AsyncTask<String, Void, String> {
//Variable for storing the req id
private int id;
//Constants corresponding to your tasks
public static int ID_ASYNC1 = 0;
static static int ID_ASYNC1 = 0;
static static int ID_ASYNC1 = 0;
#Override
protected String doInBackground(String... params) {
id = params[1]);
//your code
}
#Override
protected void onPostExecute(String result) {
if(id == ID_ASYNC1){
//Do your task #1
} else if(id == ID_ASYNC2){
//Do your task #2
}
}
}
You have to use looper for smooth downloading for multiple file it will download them one by one. In this way your application run smoothly huge huge amount of downloads.
How to use Looper
public class MainActivity extends Activity implements DownloadThreadListener,
OnClickListener {
private DownloadThread downloadThread;
private Handler handler;
private ProgressBar progressBar;
private TextView statusText;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Create and launch the download thread
downloadThread = new DownloadThread(this);
downloadThread.start();
// Create the Handler. It will implicitly bind to the Looper
// that is internally created for this thread (since it is the UI
// thread)
handler = new Handler();
progressBar = (ProgressBar) findViewById(R.id.progress_bar);
statusText = (TextView) findViewById(R.id.status_text);
Button scheduleButton = (Button) findViewById(R.id.schedule_button);
scheduleButton.setOnClickListener(this);
}
#Override
protected void onDestroy() {
super.onDestroy();
// request the thread to stop
downloadThread.requestStop();
}
// note! this might be called from another thread
#Override
public void handleDownloadThreadUpdate() {
// we want to modify the progress bar so we need to do it from the UI
// thread
// how can we make sure the code runs in the UI thread? use the handler!
handler.post(new Runnable() {
#Override
public void run() {
int total = downloadThread.getTotalQueued();
int completed = downloadThread.getTotalCompleted();
progressBar.setMax(total);
progressBar.setProgress(0); // need to do it due to a
// ProgressBar bug
progressBar.setProgress(completed);
statusText.setText(String.format("Downloaded %d/%d", completed,
total));
// vibrate for fun
if (completed == total) {
((Vibrator) getSystemService(VIBRATOR_SERVICE))
.vibrate(100);
}
}
});
}
#Override
public void onClick(View source) {
if (source.getId() == R.id.schedule_button) {
int totalTasks = new Random().nextInt(3) + 1;
for (int i = 0; i < totalTasks; ++i) {
downloadThread.enqueueDownload(new DownloadTask());
}
}
}
}
DownloadThread.Class
public final class DownloadThread extends Thread {
private static final String TAG = DownloadThread.class.getSimpleName();
private Handler handler;
private int totalQueued;
private int totalCompleted;
private DownloadThreadListener listener;
public DownloadThread(DownloadThreadListener listener) {
this.listener = listener;
}
#Override
public void run() {
try {
// preparing a looper on current thread
// the current thread is being detected implicitly
Looper.prepare();
Log.i(TAG, "DownloadThread entering the loop");
// now, the handler will automatically bind to the
// Looper that is attached to the current thread
// You don't need to specify the Looper explicitly
handler = new Handler();
// After the following line the thread will start
// running the message loop and will not normally
// exit the loop unless a problem happens or you
// quit() the looper (see below)
Looper.loop();
Log.i(TAG, "DownloadThread exiting gracefully");
} catch (Throwable t) {
Log.e(TAG, "DownloadThread halted due to an error", t);
}
}
// This method is allowed to be called from any thread
public synchronized void requestStop() {
// using the handler, post a Runnable that will quit()
// the Looper attached to our DownloadThread
// obviously, all previously queued tasks will be executed
// before the loop gets the quit Runnable
handler.post(new Runnable() {
#Override
public void run() {
// This is guaranteed to run on the DownloadThread
// so we can use myLooper() to get its looper
Log.i(TAG, "DownloadThread loop quitting by request");
Looper.myLooper().quit();
}
});
}
public synchronized void enqueueDownload(final DownloadTask task) {
// Wrap DownloadTask into another Runnable to track the statistics
handler.post(new Runnable() {
#Override
public void run() {
try {
task.run();
} finally {
// register task completion
synchronized (DownloadThread.this) {
totalCompleted++;
}
// tell the listener something has happened
signalUpdate();
}
}
});
totalQueued++;
// tell the listeners the queue is now longer
signalUpdate();
}
public synchronized int getTotalQueued() {
return totalQueued;
}
public synchronized int getTotalCompleted() {
return totalCompleted;
}
// Please note! This method will normally be called from the download
// thread.
// Thus, it is up for the listener to deal with that (in case it is a UI
// component,
// it has to execute the signal handling code in the UI thread using Handler
// - see
// DownloadQueueActivity for example).
private void signalUpdate() {
if (listener != null) {
listener.handleDownloadThreadUpdate();
}
}
}
DownloadTask.Class
public class DownloadTask implements Runnable {
private static final String TAG = DownloadTask.class.getSimpleName();
private static final Random random = new Random();
private int lengthSec;
public DownloadTask() {
lengthSec = random.nextInt(3) + 1;
}
#Override
public void run() {
try {
Thread.sleep(lengthSec * 1000);
// it's a good idea to always catch Throwable
// in isolated "codelets" like Runnable or Thread
// otherwise the exception might be sunk by some
// agent that actually runs your Runnable - you
// never know what it might be.
} catch (Throwable t) {
Log.e(TAG, "Error in DownloadTask", t);
}
}
}
DownloadThreadListener.class (interface)
public interface DownloadThreadListener {
void handleDownloadThreadUpdate();
}
Using this you can add huge amount of downloads it will add them queue.
Complete tutorial
I am trying to do this simple task. I have two buttons called START and STOP, I want to execute some task in loop by clicking on START and want to exit from this loop by clicking STOP.
Code-
public class DrawPath extends Activity implements View.OnClickListener {
ArrayList<LatLng> positions = new ArrayList<LatLng>() ;
static int c=1;
Location location;
GoogleMap mMap;
Button btStart,btStop;
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.drawpath);
initializeVar();
btStart.setOnClickListener(this);
btStop.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
c = 0;
System.out.println("tested2");
}
});
}
private void initializeVar()
{
btStart=(Button)findViewById(R.id.btStart);
btStop=(Button)findViewById(R.id.btStop);
}
#Override
public void onClick(View v) {
getupdate(1);
}
private void getupdate(int d) {
c = d;
CurrentPosition currentPosition = new CurrentPosition(this);
if (c == 0) {
System.out.println("Done");
} else {
location = currentPosition.getCurrentLocation();
LatLng pos = new LatLng(location.getLatitude(), location.getLongitude());
positions.add(pos);
System.out.println("running");
try {
Thread.sleep(5000);
getupdate(c);
} catch (Exception e) {
}
}
}
}
Somebody please share any idea how to achieve it.
You can use Handler with Runnable to stop your thread after STOP button click.
I am giving you hint use following code according to your requirement
Handler handler = new Handler();
Runnable runable = new Runnable({
#Override
public void run(){
// count
handler.postDelayed(this, 1000);
}
});
Now you can call following line from your btnStop.onClick().
handler.removeCallbacks(runable);
Check this for more details on Handler and Runnable
What I suggest it create a inner class which extends Thread and according to user's action start and stop the thread. here is an example
class DrawPath extends Activity implements View.OnClickListener {
MyThread thread;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.drawpath);
initializeVar(); //not in my code so you add it
btStart.setOnClickListener(this);
btStop.setOnClickListener(this);
}
#Override
public void onClick(View view) {
switch (view.getId()) {
case R.id.btStart:
if (thread == null) {
thread = new MyThread();
thread.start();
}
break;
case R.id.btStop:
if (thread != null) {
thread.interrupt();
}
break;
}
}
class MyThread extends Thread {
#Override
public void run() {
while (true) {
try {
Thread.sleep(3000);
//your stuff goes here or before sleep
} catch (InterruptedException e) {
e.printStackTrace();
thread = null;
break;
}
}
}
}
//whey interrupt here bcz infinite loop will be running until and unless you stop it.
#Override
protected void onDestroy() {
super.onDestroy();
if (thread != null)
thread.interrupt();
}
}
I saw your code which needs little more improvements that's why I wrote this big file :)
Suggestions :
Checkout the implementation of onClickListener.
Stopping thread at onDestroy() because thread will be running even after you close your application, so you need to stop when you
come out (destroyed) of your main activity.
I'm programming a small android app in Java/eclipse.
In one part of my app i need a thread, as i build in the following way:
protected void onResume() {
super.onResume();
// we're going to simulate real time with thread that append data to the graph
new Thread(new Runnable() {
#Override
public void run() {
// we add 100 new entries
for (int i = 0; i < 100; i++) {
runOnUiThread(new Runnable() {
#Override
public void run() {
addEntry();
}
});
// sleep to slow down the add of entries
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// manage error ...
}
}
}
}).start();
}
Evertything works fine so far. But now i want to start that thread not automatically. I want to handle ".start()" with a button.
How can i realize it?
I'm very new to Java and Android.
Thanks in Advance!
You can use Handler with Runnable instead of your Thread idea, Check out the following code, it server your purpose,
private Handler broadcastHandler;
private Runnable broadcastRunnable;
protected void onResume() {
super.onResume();
broadcastRunnable = new Runnable() {
#Override
public void run() {
// Your UI related operations
runOnUiThread(new Runnable() {
#Override
public void run() {
addEntry();
}
});
// Add some delay
broadcastHandler.postDelayed(broadcastRunnable, 1000);
}
}
public void onButtonClick(View view) {
broadcastHandler.postDelayed(broadcastRunnable, 1000);
}
I am performing two class which is extending ASyncTask and both have different functions but because of the second class my first class is lagging. So what i want to know is, is there any better solution to code in such a way that both of the operation will perform the task without making other operation to wait?
Updated with code
For the first call in the onCreate()
new connection().execute(); //
Some task performed by the same class called
public class connection extends AsyncTask {
#Override
protected Object doInBackground(Object... arg0) {
//some operation
return value;
}
#Override
protected void onPostExecute(Object o) {
super.onPostExecute(o);
String m = String.valueOf(o);
if (o != null) {
someoperation
} else {
edittxt.setTextColor(Color.RED);
edittxt.setText("No Internet Connection");
}
}
}
similarly i am performing the other class that i have.
You can use AsyncTask.executeOnExecutor with THREAD_POOL_EXECUTOR, the default executor is SERIAL_EXECUTOR.
You can create two separate threads and perform your operations. It will quarantine, that all operations will be performed async.
final Handler handler = new Handler();
Thread operation1 = new Thread(new Runnable() {
#Override
public void run() {
doOperation1();
handler.run(new Runnable(){
#Override
public void run() {
onPostExecute1();
}
});
}
});
Thread operation2 = new Thread(new Runnable() {
#Override
public void run() {
doOperation2();
handler.run(new Runnable(){
#Override
public void run() {
onPostExecute2();
}
});
}
});
operation1.start();
operation2.start();
I've got a problem in Android with runOnUiThread. I start an AsyncTask (inside an Activity) that waits for a while, and after calls the method showDialog(id). After this call, it sleeps for another while, and finishes calling the method dismissDialog(id).
This is the code:
public class MyActivity extends Activity {
...
protected class StartWaiting extends AsyncTask<Void, Void, Void> {
#Override
protected Void doInBackground(Void... arg0) {
try {
Thread.sleep(2500);
} catch (InterruptedException e) {
e.printStackTrace();
}
// Show dialog
runOnUiThread(new Runnable() {
#Override
public void run() {
showDialog(PROGRESS_DIALOG_LOADING);
}
});
try {
Thread.sleep(2500);
} catch (InterruptedException e) {
e.printStackTrace();
}
// Dismiss dialog
runOnUiThread(new Runnable() {
#Override
public void run() {
dismissDialog(PROGRESS_DIALOG_LOADING);
}
});
return null;
}
#Override
protected void onPostExecute(Void arg) {
new StartServices().execute();
}
}
}
Unfortunately, the behaviour is different from what I want to do: showDialog() and dismissDialog() are called one immediately after the other.
I think that the reason why this happens is that the UI Thread execute both actions "when it can", that is at the end of sleeping actions.
Well, does it exist some "flushUI()" method that force a thread (in this case, the UI Thread) to execute every action before continue?
You could just add a synchronization point in your thread, using a CountDownLatch for instance:
final CountDownLatch latch = new CountDownLatch(1);
view.post(new Runnable() {
public void run() {
try {
// Do stuff on the UI thread
} finally {
latch.countDown();
}
}
});
try {
if (!latch.await(CAPTURE_TIMEOUT, TimeUnit.MILLISECONDS)) {
return;
}
} catch (InterruptedException e) {
// Handle exception
}