I'm new to android and have been doing some reading about worker threads and not blocking the UI thread. I'm playing around with a simple timer app that starts a thread that updates a textview every second when the activity is created. So my question is, these days what is the best way to do this. Both of the two examples below work but is there a better (more efficient/ more Android) way?
final Handler handler = new Handler();
handler.postDelayed(new Runnable() {
#Override
public void run() {
seconds++;
runOnUiThread(new Runnable() {
#Override
public void run() {
secondsTextView.setText(seconds);
}
});
handler.postDelayed(this, 1000);
}
}, 1000);
or
new Thread(){
#Override
public void run(){
try{
while(!isInterrupted()){
Thread.sleep(1000);
runOnUiThread(new Runnable() {
#Override
public void run() {
seconds++;
secondsTextView.setText(seconds);
}
});
}
}catch(Exception e){
Log.e("Activity1", e.toString());
}
}
}.start();
The more efficient way is:
timeOnTextView.postDelayed(new Runnable() {
#Override
public void run() {
seconds++;
timeOnTextView.postDelayed(this, 1000);
}
}, 1000);
The run() of the Runnable passed to postDelayed() is invoked on the main application thread, so you do not need to use runOnUiThread().
Since postDelayed() is implemented on View, you do not need a Handler.
Related
I have an activity with 3 buttons: create, start and cancel. Button create creates a new thread, button start runs it and button cancel stops this thread. And my problem is that thread isn't interrupted after calling interrupt method (this action is performed after clicking on cancel button). I know, that in my thread I should check if thread is interrupted or no. I added it, but interrupting still doesn't work. Here's my code:
private View.OnClickListener listener = new View.OnClickListener() {
#SuppressLint("HandlerLeak")
#Override
public void onClick(View v) {
switch (v.getId()){
case R.id.create_button:
thread = new Thread(new Runnable() {
#Override
public void run() {
int i;
for (i = 0; i<10;i++){
final int finalI = i;
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
textCounter.post(new Runnable() {
#Override
public void run() {
textCounter.setText(String.valueOf(finalI));
}
});
}
}, 500*i);
if (thread.isInterrupted()){
Log.i(getClass().getSimpleName(), "Thread is interrupted");
return;
}
}
new Handler().postDelayed(new Runnable() {
#SuppressLint("SetTextI18n")
#Override
public void run() {
textCounter.setText("Done!");
}
},(i+1)*500);
}
});
break;
case R.id.start_button:
thread.run();
break;
case R.id.cancel_button:
thread.interrupt();
Log.i(getClass().getSimpleName(), "Cancel Button clicked");
break;
}
}
};
So, why thread isn't interrupted and how can I solve this problem?
Your thread is quickly adding 11 tasks (10 + the last one) to this handler you're creating and then dying. These tasks have a delay, and then the message queue will take care of running the 10 + 1 runnables. To do what you're trying to do you should make the thread to wait 500ms between each loop.
Something similar to this:
thread = new Thread(new Runnable() {
#Override
public void run() {
int i;
for (i = 0; i<10;i++){
final int finalI = i;
textCounter.post(new Runnable() {
#Override
public void run() {
textCounter.setText(String.valueOf(finalI));
}
});
try {
Thread.sleep(500);
} catch (InterruptedException ignored) {
Log.i(getClass().getSimpleName(), "Thread is interrupted");
return;
}
if (thread.isInterrupted()){
Log.i(getClass().getSimpleName(), "Thread is interrupted");
return;
}
}
textCounter.post(new Runnable() {
#Override
public void run() {
textCounter.setText("Done!");
}
});
}
});
There are many problems with your code
Problem 1: When users click on Create button, you create a new thread, it is not expected behavior as you want, so just create a new thread if it is not created yet or terminated.
Problem 2: When users click on Start button
thread.run();
This line does not start the thread, it just executes the code in run method on calling thread, in this case main/UI thread. To start a thread you must use start method. Make sure you start the thread if it is has been created.
Problem 3: When users click on Cancel button
thread.interrupt();
Because there is no thread started so this line will do nothing.
Problem 4: From your description, you want to use a thread to increase counter on a TextView from 0 to 9 each 0.5 second, then display "Done". Your code is incorrect and contains many redundant.
Solution: You can following this code.
private View.OnClickListener listener = new View.OnClickListener() {
#Override
public void onClick(View view) {
switch (view.getId()) {
case R.id.create_button:
// Only create a new thread if it is not created or it is terminated.
if (thread == null || thread.getState() == Thread.State.TERMINATED) {
thread = new Thread(new Runnable() {
#Override
public void run() {
try {
for (int i = 0; i < 10; i++) {
final int finalI = i;
// This will post a message to main/UI thread.
textCounter.post(new Runnable() {
#Override
public void run() {
textCounter.setText(String.valueOf(finalI));
}
});
// Sleep current thread in 0.5 second before running next step.
Thread.sleep(500);
}
// Display Done after finishing counter.
textCounter.post(new Runnable() {
#SuppressLint("SetTextI18n")
#Override
public void run() {
textCounter.setText("Done!");
}
});
} catch (InterruptedException e) {
// Display Cancelled if the current thread is cancelled.
textCounter.post(new Runnable() {
#SuppressLint("SetTextI18n")
#Override
public void run() {
textCounter.setText("Cancelled!");
}
});
}
}
});
} else {
Toast.makeText(MainActivity.this,
"Thread is already created. No need to create anymore.",
Toast.LENGTH_SHORT)
.show();
}
break;
case R.id.start_button:
// Start thread if it is created.
if (thread != null && thread.getState() == Thread.State.NEW) {
thread.start();
} else {
Toast.makeText(MainActivity.this,
"Thread is not created yet or it is running.",
Toast.LENGTH_SHORT)
.show();
}
break;
case R.id.cancel_button:
// Cancel the thread if it is running.
if (thread != null && thread.isAlive()) {
thread.interrupt();
} else {
Toast.makeText(MainActivity.this,
"Thread is not running yet.",
Toast.LENGTH_SHORT)
.show();
}
break;
}
}
};
I want complete code to disable a Button for some time for example 2 minutes in Android Studio. Thank you for help.
protected void onclick(View v){
bwasta = (Button) findViewById(R.id.btDes);
new CountDownTimer(10000, 10) { //Set Timer for 10 seconds
public void onTick(long millisUntilFinished) {
}
#Override
public void onFinish() {
bwasta.setEnabled(true);
bwasta.setEnabled(false);
}
}.start();
This might help you out.
Button bwasta = (Button) findViewById(R.id.btDes);
bwasta.setEnabled(false);
new Thread(new Runnable() {
#Override
public void run() {
try {
Thread.sleep(2*
60*
1000);//min secs millisecs
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
YourActivityName.this.runOnUiThread(new Runnable() {
#Override
public void run() {
bwasta.setEnabled(true);
}
});
}
}).start();
DO NOT RELY ON Thread.sleep()
Actually, there is already a Question and an Answer on SO regarding the inaccuracy of Thread.sleep()(as per the OP's experience) here.
The answer then favors the accuracy of a ScheduledThreadPoolExecutor using the schedule() method.
Do the following:
Button bwasta = (Button) findViewById(R.id.btDes);
bwasta.setEnabled(false);
ScheduledThreadPoolExecutor exec = new ScheduledThreadPoolExecutor(1);
exec.schedule(new new Runnable() {
#Override
public void run() {
YourActivityName.this.runOnUiThread(new Runnable() {
#Override
public void run() {
bwasta.setEnabled(true);
}
});
}
}, 2, TimeUnit.MINUTES);
You can use mHandler.postDelayed(mRunnable, mTime) function to achieve this
Button bwasta = (Button) findViewById(R.id.btDes);
bwasta.setEnabled(false);
bwasta.postDelayed(new Runnable() {
public void run() {
bwasta.setEnabled(true);
}
}, 2*60*1000);
I have to update the list after optimizing the running apps ....
m_optimizeBtn.setOnClickListener(new View.OnClickListener()
{
#Override
public void onClick(View v)
{
launchProgressRing(OptimizationActivity.this);
listAdaptor.notifyDataSetChanged();
}
});
}
Killing the running process in a seprate thread....
public void launchProgressRing(Context ctx){
final ProgressDialog opt_proDialog=new ProgressDialog(ctx);
opt_proDialog.setTitle("Please wait...");
opt_proDialog.setMessage("Optimizing power draining apps...");
opt_proDialog.setIndeterminate(true);
opt_proDialog.show();
opt_proDialog.setCancelable(false);
new Thread(new Runnable()
{
#Override
public void run()
{
//TODO: optimize apps
m_cPowerDrainingApps.killBgRunningProcesses(runningAppsList);
try
{
Thread.sleep(1500);
} catch (InterruptedException e)
{
e.printStackTrace();
}
runOnUiThread(new Runnable()
{
#Override
public void run()
{
opt_proDialog.dismiss();
}
});
}
}).start();
}
listAdaptor.notifyDataSetChanged() is not working ,don't know why ???
What I suggest is to use AsyncTask to do the job. AsyncTask has two good methods for you:
doInBackground: which you can put most of the background tasks in there
onPostExecute : which you can put the logic of what needs to be done when the background task has finished its job.
So your code should look like this:
public class BackgroundTask extends AsyncTask<Void,Void,Void>{
private ListAdapter mAdapter;
public BackgroundTask(ListAdapter adapter)
{
mAdapter = adapter
}
public Void doInBackground (Void... params)
{
//define m_cPowerDrainingApps somewhere
m_cPowerDrainingApps.killBgRunningProcesses(runningAppsList);
try
{
Thread.sleep(1500);
} catch (InterruptedException e)
{
e.printStackTrace();
}
}
public Void onPostExecute (Void... params)
{
//do your UI things
mAdapter.notifyDataSetChanged();
}
}
and then run this with:
new BackgroundTask(listAdapter).execute()
Use a Handler and its postDelayed method to invalidate the list's adapter as follows:
final Handler handler = new Handler()
handler.postDelayed( new Runnable() {
#Override
public void run() {
adapter.notifyDataSetChanged();
handler.postDelayed( this, 60 * 1000 );
}
}, 60 * 1000 );
You must only update UI in the main (UI) thread.
right now i'm doing
public void someStuff(){
new Thread(new Runnable() {
#Override
public void run() {
//doing long task
doOtherStuff();
}
}).start();
}
public void doOtherStuff(){
doEvenMoreStuff();
}
but the problem is that it executes doOtherStuff in the same thread and It needs to be executed in the UI Thread. how can I accomplish this?
I am only using the thread because otherwise the app freezes. I just need doOtherStuff to wait for the thread to finish.
Try this:
this.runOnUiThread(new Runnable() {
#Override
public void run() {
//do something
}
});
this is your activity.
Use handler :
public void doOtherStuff(){
new Handler(context.getMainLooper()).post(new Runnable() {
#Override
public void run() {
// Executes on UI thread
doEvenMoreStuff();
}
});
}
where context might be your Activity
Not sure if best practice but you can Try this:
public void someStuff(){
new Thread(new Runnable() {
#Override
public void run() {
YourActivityClassName.this.runOnUiThread(new Runnable() {
#Override
public void run() {
//doing long task
doOtherStuff();
}
});
}
}).start();
An alternative way of using Handler which other answers suggested is AsyncTask:
It has two methods which can be useful in your case:
doInBackground: which runs in the background thread so your UI won't freeze
onPostExecute: which runs on UI thread after doInBackground finishes. A generic class may look like:
private class MyTask extends AsyncTask<String, Void, String> {
#Override
protected String doInBackground(String... input) {
//do background processes on input and send response to onPostExecute
return response;
}
#Override
protected void onPostExecute(String result) {
//update UIs based on the result from doInBackground
}
}
and you can execute the task by:
new MyTask(inputs).execute()
I want to update the UI during an intensive task. I've got a separate thread where the intensive method runs but I can't update the UI...
This is the structure of my code:
btn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View arg0) {
updateUI();
Thread t = new Thread(){
#Override
public void run(){
runIntensiveMethod();
}
}
t.start();
}
});
If I comment runIntensiveMethod(); the animation runs perfectly fine. However, it seems as though the UI queue is delayed until the method is run.
How can I post an animation and run the method while the animation runs?
Everything I've tried halts the UI until the method has been executed.
You should set a priority for that thread:
android.os.Process.setThreadPriority(android.os.Process.THREAD_PRIORITY_BACKGROUND);
Also check here
Just call runOnUiThread when you want to refresh from your runIntensiveMethod()
runOnUiThread(new Runnable()
{
#Override
public void run()
{
updateUI();
}
});
Change your code to this, this should work.
btn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View arg0) {
Thread t = new Thread(){
#Override
public void run(){
runIntensiveMethod();
runOnUiThread(new Runnable() {
public void run() {
updateUI();
}
}
}
t.start();
}
});