I have this button that when clicked calls a thead to update user ui:
averageButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
new Testing().execute();
}
});
The method gets executed but it freezes the UI for 10 seconds. I want to update the UI everytime I call the averageMiles.append(mile) method.
class Testing extends AsyncTask<Void, Void, Void> {
#Override
protected void onPostExecute(Void unused) {
int x = 0;
while (true) {
if (x == 10) {
break;
}
ArrayList<Double> milesList = new ArrayList<>();
if (x == 0) {
averageMiles.setText("mph: ");
}
String mile = milesValue.getText().toString();
if (!isNum(mile)) {
continue;
}
averageMiles.append(mile);
milesList.add(Double.valueOf(mile.trim()));
x++;
if (x == 10) {
averageMiles.append("\nAverage: " + getAverage(milesList));
return ;
} else {
averageMiles.append(", ");
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
Use ThreadPool executor from official oracle and android developer documentations
Oracle Docs - java.util.concurrent.ThreadPoolExecutor
Android Docs - java.util.concurrent.ThreadPoolExecutor
as you have specified you are using java the above two documentation is recommended by google as show below for AsyncTask
This class was deprecated in API level 30.
Use the standard java.util.concurrent or Kotlin concurrency utilities instead.
The ultimate objective is the android is saying that, if you want to update UI, simply use runOnUiThread with a new runnable you update the UI, which means for each UI update you may be creating fresh short term thread which only updates the UI and thread finishes and garbage collected
Sample Code
public class MainActivity extends AppCompatActivity {
int count = 0;
Executor ex;
MyThread th;
class MyThread extends Thread implements Runnable {
private boolean running=false;
public void setRunning(boolean running) {
this.running = running;
}
#Override
public void run() {
while(running) {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
runOnUiThread(new Runnable() {
#Override
public void run() {
count++;
TextView tv = findViewById(R.id.textView);
tv.setText(String.valueOf(count));
}
});
}
}
}
public void onStartClick(View view) {
th = new MyThread();
ex = Executors.newSingleThreadExecutor();
th.setRunning(true);
ex.execute(th);
}
public void onStopClick(View view) {
if(th!=null) {
th.setRunning(false);
}
}
}
all the member variables of the class should be only accessed inside runOnUiThread, for example count++ count is a variable of the MainActivity if you want any value specific to the thread you put only inside the MyThread class
Never try to access any of the MainActivity variable inside the run() method
You can also write MyThread as separate class, and set values similar to th.setRunning before starting the thread.
If you want a callback after the thread is completed use an interface which will give you a callback method in your MainActivity
So it is simply core java
I have created an example with interface
Concurrent Executor Interface Example
Related
I have a .lrc file and I need to go over every line with a CountDownTimer. I have tried using AsyncTask to do so but I get the error:
Caused by: java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()
On line new CountDownTimer... I tried doing it with runnable but I still get the same error. My goal is to get it to go over every line in .lrc file which looks like this:
[00:04.15]Help me, it's like the walls are caving in
[00:10.46]Sometimes I feel like giving up
[00:13.63]But I just can't
...
I am not sure how efficient it is to do it the way I am trying to do. I am thinking of going through every line in the doInBackground(). If there is a better way to do it then let me know. But to begin with, why am I getting the EXCEPTION ?
Just to note.. I have simplified the code as much as I could so it would be easier to understand what I am trying to do.
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
MyView myView = new
myView.play();
}
}
public class MyView{
public void play() {
new CustomAsync().execute();
}
}
class CustomAsync extends AsyncTask<Lyric, Void, Void> {
protected Void doInBackground(Lyric... param) {
startLyricCountDownTimer(param);
return null;
}
protected void onPostExecute(Void param) {
//Print Toast or open dialog
}
private void startLyricCountDownTimer(Lyric lyric){
new CountDownTimer(30000, 10) { //This is where it throws the error
public void onTick(long millisUntilFinished) {
//Do the thing
}
public void onFinish() {
}
}.start();
}
}
EDIT
Is it better to go with the AsyncTask and do like Son Truong suggested or to use the following code for each and every lrc line?
new Handler(Looper.getMainLooper()).post(new Runnable() {
#Override
public void run() {
new CountDownTimer(millisInFuture,countDownInterval) {
#Override
public void onTick(
CountDownTimer uses a Handler to post messages to a Message Queue of a Thread which has a Looper. onTick and onFinish will be called on which thread based on where you create CountDownTimer instance.
In your case because you create CountDownTimer instance in doInBackground method of AsyncTask so these two methods will be call on AsyncTask thread.
In constructor of CountDownTimer, it will create Handler instance as well. The Handler will check whether or not current thread has a Looper, if not it will throw a RuntimeException with message.
Can't create handler inside thread that has not called
Looper.prepare()
Because AsyncTask uses a thread which has no Looper, that why your app crashes.
My suggestion is in doInBackground method you open a connection to .lrc file and read each line, for each line read, use runOnUIThread to send the line to UI thread (then you can process the line read there by display a Toast on screen, etc).
Update: I will demo how to read line by line from a file then display it on a text view each 3 seconds.
First write a class which read from an inputstream line by line
static class ReadLyricTask extends AsyncTask<InputStream, String, Void> {
WeakReference<MainActivity> mMainActivity;
ReadLyricTask(MainActivity activity) {
mMainActivity = new WeakReference<>(activity);
}
#Override
protected Void doInBackground(InputStream... inputStreams) {
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStreams[0]));
String line;
try {
while ((line = reader.readLine()) != null) {
publishProgress(line);
}
} catch (IOException e) {
// Do nothing.
} finally {
try {
inputStreams[0].close();
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return null;
}
#Override
protected void onProgressUpdate(String... values) {
MainActivity activity = mMainActivity.get();
if (activity != null) {
activity.displayLyricLineOnTextView(values[0]);
}
}
}
Then just use it in MainActivity
public class MainActivity extends AppCompatActivity {
private static final int UPDATE_LYRIC_TEXT_INTERVAL = 3000; // Change lyric text each 3 seconds.
private int mCurrentInterval = 0;
private TextView mLyricTextView;
private Handler mHandler = new Handler();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mLyricTextView = findViewById(R.id.lyricText);
// I put a file named lyric.lrc in raw folder, for your case just open an input stream from a file.
InputStream inputStream = getResources().openRawResource(R.raw.lyric);
new ReadLyricTask(this).execute(inputStream);
}
private void displayLyricLineOnTextView(final String lyricLine) {
mHandler.postDelayed(new Runnable() {
#Override
public void run() {
mLyricTextView.setText(lyricLine);
}
}, mCurrentInterval);
mCurrentInterval += UPDATE_LYRIC_TEXT_INTERVAL;
}
}
CountDownTimer runs in separate thread and no need of asynctask to run a timer.Best solution would be create a service and make service to trigger a timer.
As timer run on non ui thread, while updating ui make sure you update from UI thread.You could use UI handler or runOnUithread method to update view.
I am using Android Studio. I am trying to fix a clock in which I pressed the button, it will return the time to 12:00. I got it working but I want it to be executed by waiting 250 miliseconds. The problem is that they only wait for 250 miliseconds (but there are two executions so that's 5 seconds) and then it goes to 12:00 right away without showing it to the Text View. Is there something I am doing wrong? Also if I applied handler function here, I'm afraid I don't have much knowledge to use that.
Edit: This is from my MainActivity method. Example, change from 1 to 2 then wait for 250 miliseconds, change from 2 to 3 then wait for 250 miliseconds
synchronized (this){
try {
while(minuteHand != 0 || hourHand != 12){
if (hourHand != 12){
hourHand++;
hourText.setText(Integer.toString(hourHand));
wait(250);
}
if (minuteHand != 0) {
minuteHand += 5;
minuteHand %= 60;
minuteText.setText(Integer.toString(minuteHand));
wait(250);
}
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
Below is the proper way to update UI elements from an asynchronous thread:
public class SampleActivity extends Activity {
private TextView hourText;
private TextView minuteText;
private void updateHourText(final String text) {
runOnUiThread(new Runnable() {
#Override
public void run() {
hourText.setText(text);
}
});
}
private void updateMinuteText(final String text) {
runOnUiThread(new Runnable() {
#Override
public void run() {
minuteText.setText(text);
}
});
}
private class ClockTask extends AsyncTask<Void, Void, Void>
{
private int hourHand;
private int minuteHand;
public ClockTask(int hourHand, int minuteHand) {
this.hourHand = hourHand;
this.minuteHand = minuteHand;
}
#Override
protected Void doInBackground(Void... voids) {
try {
while(minuteHand != 0 || hourHand != 12){
if (hourHand != 12){
hourHand++;
updateHourText(Integer.toString(hourHand));
Thread.sleep(250);
}
if (minuteHand != 0) {
minuteHand += 5;
minuteHand %= 60;
updateMinuteText(Integer.toString(minuteHand));
Thread.sleep(250);
}
}
} catch (InterruptedException e) {
e.printStackTrace();
}
return null;
}
}
}
And then call this from your button's onClickListener():
new ClockTask(hourHand, minuteHand).execute();
As you can see, in order to update TextView's text, a helper method was needed, which would always execute TextView.setText() inside your main thread. That way, your business logic can execute within its own thread (without causing main thread to wait, which would lead to unresponsive UI), while updating your TextViews asynchronously.
Also, you should never try to update UI elements outside your main thread's context as it will lead to an exception being thrown.
I have an app that has a Fragment with a ListView. I ping IP addresses on the network and when I get a response I add the IP address to a list. Once I've finished pinging the IP addresses, I put this list of IP addresses that replied to my ping into a ListView.
What I want to do is update this ListView as I'm pinging rather than doing after I've pinged all the IP addresses. To ping the IP addresses I'm using an AsyncTask which then calls a Runnable Thread. How do I tell the Fragment to update the UI from that Runnable class when I find an IP address?
The rough layout of my classes is below.
public class FinderFragment extends ListFragment {
private void CallFind(){
new Find().execute();
}
private class Find extends AsyncTask<Void, Void, Void> {
#Override
protected Void doInBackground(Void... voids) {
SearchNetwork searchNetwork = new SearchNetwork(ipAddress);
try {
searchNetwork.StartFind();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
#Override
protected void onPostExecute(Void aVoid) {
super.onPostExecute(aVoid);
UpdateDeviceList();
}
}
}
public class SearchNetwork {
public void StartFind() throws InterruptedException {
Thread t[] = new Thread[20];
for(int i=0; i<02; i++){
t[i] = new Thread(new FindDevices(i*5));
t[i].start();
}
for(Thread thread : t){
thread.join();
}
}
class FindDevices implements Runnable{
#Override
public void run() {
//Ping the IP addresses here
//I want to update UI from here
}
}
Try this....
getActivity().runOnUiThread(new Runnable(){
#Override
public void run(){
// Do whatever you want
}
});
use Handler, runOnUiThread or View.post
android.os.Handler handler = new android.os.Handler();
handler.post(new Runnable() {
#Override
public void run() {
//update here
}
});
You should use AsyncTask instead of raw threads! There is a method onPostExecute() which runs on the main UI thread and that's the point where you can update your UI ...
But you have to keep in mind, that you have to cancel the AsyncTask (threads as well) if the fragment gets destroyed, otherwise your task will continue to run and will try to change the UI of the fragment, but the fragment doesn't exist anymore which will lead to exceptions like IllegalStateException: Fragment not attached to Activity or View Not Attached Exceptions
We can use Handler, runOnUiThread, postDelayed and AsyncTask.
AsyncTask is good option other than rest as it extends the handler class.
When you have to code in thread use the doInBackground() method like this:
class myAsyncTask extends AsyncTask(Void,Void,Void){
public doInBackground(){
// do your code here
}
}
And call it on the UI thread like this:
new MyAsyncTask().execute();
if you want to auto update every x sec you can use this:
Runnable runnable = new Runnable() {
#Override
public void run() {
while(refreshing){
handler.post(new Runnable() {
#Override
public void run() {
//do your stuff here
}
});
try{
Thread.sleep(30000);
}catch(InterruptedException e){
e.printStackTrace();
}
}
}
};
new Thread(runnable).start();
Try
publishProgress()
and catch it in the overidden method
#Override
protected void onProgressUpdate(Void... values) {
super.onProgressUpdate(values);
}
I'm trying to make a simple little program that will increment a number once a second. In this case, I'm implementing a thread that should loop once per second and add 1 to "potato" each time it loops. This works fine until it gets back to the display method potatoDisp(). For some reason this causes my app to crash. Removing potatoDisp() from run() fixes the problem, but the display is not updated as "potato" increases.
public int potato = 0;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
potatoDisp();
start();
}
public void potatoDisp() {
TextView text = (TextView) findViewById(R.id.textView1);
text.setText("You currently have " + potato + " potatoes");
}
public void start() {
Thread thread = new Thread(this);
thread.start();
}
#Override
public void run() {
while (true) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
return;
}
potato++;
potatoDisp();
}
}
I'm doing this for an Android app, if that helps. I've tried searching for an answer but I'm pretty lost when it comes to the proper way to work threads.
You need a runnable / handler like this:
private Runnable potatoRun = new Runnable() {
#Override
public void run () {
potatoDisp();
}
};
then change
potatoDisp();
to:
runOnUiThread(potatoRun);
You can't update the views when you're not on the UI thread.
You are probably getting an exception for updating the UI in the background. Since, potatoDisp(); is called from a background Thread but that function updates the UI it will give you problems. You need to call it with runOnUiThread().
#Override
public void run() {
while (true) {
try
{
Thread.sleep(1000);
} catch (InterruptedException e) {
return;
}
potato++;
runOnUiThread(new Runnable()
{
#Override
public void run()
{
potatoDisp();
}
});
}
}
Something like this should work.
The issue is that you are trying to update the UI (calling text.setText(...)) on a thread other than the main UI thread.
While I would suggest using a TimerTask instead of calling Thread.sleep(...), there are two main ways to edit your current code to work as expected.
-- Use a Handler
Define a Handler class that will accept messages and update your UI as needed. For example:
private final String POTATO_COUNT = "num_potatoes";
Handler handler = new Handler() {
public void handleMessage(Message msg) {
int numPotatoes = msg.getData.getInt(POTATO_COUNT);
mText.setText("You currently have " + numPotatoes + " potatoes");
}
}
Then in your code where you want to call your handler to update your text view, whether or not you are on the main UI thread, do the following:
Bundle bundle = new Bundle();
bundle.putInt(POTATO_COUNT, potato);
Message msg = new Message();
msg.setData(bundle);
handler.sendMessage(msg);
-- Call runOnUiThread(...)
#Override
public void run() {
while (true) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
return;
}
potato++;
runOnUiThread(new Runnable()
{
public void run()
{
potatoDisp();
}
}
}
}
I think you should be using Async Task to update the UI from a thread: http://developer.android.com/reference/android/os/AsyncTask.html
I defined a splashscreen the following way:
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ExceptionHandler.register(this);
setFullscreen();
splashScreen();
}
private void splashScreen() {
runOnUiThread(new Runnable() {
#Override
public void run() {
setContentView(R.layout.splashscreen);
splash = (ImageView) findViewById(R.id.splashscreenLayer);
startSplashTime = new Date();
}
});
new LoadingThread().start();
}
private class LoadingThread extends Thread {
#Override
public void run() {
checkNetwork();
}
}
Somewhere at specific conditions in the checkNetwork() method, the stopSplash method is called:
public void stopSplash() {
Message msg = new Message();
msg.what = STOPSPLASH;
Date endSplashTime = new Date();
long time = endSplashTime.getTime() - startSplashTime.getTime();
System.out.println("Time Splashscreen was displayed: " + time);
if (time < SPLASH_MIN_TIME) {
long delay = SPLASH_MIN_TIME - time;
System.out.println("Delay Splashscreen for: " + delay);
try {
Thread.sleep(delay);
} catch (InterruptedException e) {
e.printStackTrace();
}
splashHandler.sendMessage(msg);
} else {
System.out.print("Show Splashscreen now");
splashHandler.sendMessage(msg);
}
}
private Handler splashHandler = new Handler() {
#Override
public void handleMessage(Message msg) {
switch (msg.what) {
case STOPSPLASH:
splash.setVisibility(View.GONE);
break;
}
super.handleMessage(msg);
}
};
The problem is, sometimes (maybe 1 of 10) if I started the app directly from Eclipse, the Splashscreen isn't showed, but instead just a black screen.
Other problem: if i restart the app, e.g. after onDestroy() was called after clicking the back button on the device, the Splashscreen is almost never shown.
Any hints why?
My assumption: could it be, that the LoadingThread starts "faster" than the Runnable, and so the network staff is done before the Splashscreen is set?
You might try using a CountdownTimer in your implementation. On your first activity, start a CountdownTimer that checks in onTick() every so often for a synchronized boolean finishedLoading with some kind of timeout in onFinish() (15 seconds or something), while your loading is done in another thread that sets finishedLoading to true when it is finished.
Maybe the splash screen isnt being terminated before the v=next activity starts.. just a thought..