In the below code I am trying to run a thread when a button is clicked. In the button listener I create a new thread and run it...but at run time, when the button is pressed, the button itself freezes and the app does not respond and I receive ANR dialog. Moreover, when the socket is connected successfully even the TexView
mtvStatus.setText("RFC-SOCKET CONNECTED");
displays nothing.
Please let me know why this is happening.
button listener:
this.mbtnConnect.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
BluetoothSocket rfcSocket = mSPPCtrl.rfcConnect();
if (rfcSocket.isConnected()) {
mtvStatus.setText("RFC-SOCKET CONNECTED");
Thread rx = new Thread(new RxRun(rfcSocket));
rx.run();
} else {
mtvStatus.setText("RFC-SOCKET NOT CONNECTED");
}
}
});
runnable class
private class RxRun implements Runnable {
private BluetoothSocket mRFCSocket = null;
private OutputStream mRFCOS = null;
private InputStream mRFCIS = null;
public RxRun(BluetoothSocket rfcSocket) {
this.mRFCSocket = rfcSocket;
try {
this.mRFCOS = rfcSocket.getOutputStream();
} catch (IOException e) {
e.printStackTrace();
}
try {
this.mRFCIS = rfcSocket.getInputStream();
} catch (IOException e) {
e.printStackTrace();
}
}
#Override
public void run() {
try {
this.mRFCOS.write(DIRON.getBytes());
} catch (IOException e) {
e.printStackTrace();
}
while (this.mRFCSocket.isConnected()) {
try {
int readBytes = this.mRFCIS.read(new byte[5120]);
Log.d(TAG, CSubTag.bullet("RxRun", "readBytes:" + readBytes));
//mtvRx.setText(""+readBytes);
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
...when the button is pressed, the button itself freezes and the app does not respond and I receive ANR dialog. Moreover, when the socket is connected successfully even the TexView displays nothing.
It's expected, because you haven't actually started the rx thread. Here is what is going on:
mSPPCtrl gets connected,
mtvStatus's text is set to "RFC-SOCKET CONNECTED", but you cannot visually see it because
run() method of the RxRun instance is called manually where the loop while (this.mRFCSocket.isConnected()) may last as long as the socket is connected.
All the above said is invoked on UI-thread and that's the reason of ANR.
You should not call run() manually. Start the rx thread with
rx.start();
Also I highly recommend to move all the rfcSocket logic inside of the thread and notify the UI-thread on success/failure of connection.
EDIT
Here is one the option mentioned in my comment.
Start the rx thread on a button click
this.mbtnConnect.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
new Thread(new RxRun(rfcSocket)).start();
}
});
Move your logic inside of the run() method:
public void run() {
BluetoothSocket rfcSocket = mSPPCtrl.rfcConnect();
if (rfcSocket.isConnected()) {
mtvStatus.post(new Runnable() {
#Override
public void run() {
mtvStatus.setText("RFC-SOCKET CONNECTED");
}
});
} else {
mtvStatus.post(new Runnable() {
#Override
public void run() {
mtvStatus.setText("RFC-SOCKET NOT CONNECTED");
}
});
return;
}
// the rest of your logic
}
Some links that might help:
Android documentation on Threads
SO question Android basics: running code in the UI thread
another SO post on Update UI from Thread
The error here is really simple - do not use run() on the Thread instance, as this will actually run it's Runnable on the current Thread! Use start() and it will work fine :-)
Related
I need to make an app which, when a button is pressed, a character is sent to Arduino via Bluetooth with a delay of 50ms. I'm new to Android and surfing on the internet I found this solution:
base_destra.setOnHoverListener(new View.OnHoverListener() {
#Override
public boolean onHover(View v, MotionEvent event) {
while(event.getAction() == MotionEvent.ACTION_DOWN){
sendData("d");
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
if(event.getAction() == MotionEvent.ACTION_UP){
sendData("10");
}
return false;
}
});
Where sendData is this:
private void sendData(String data){
if(OutStream == null){
return;
}
byte[] comando = data.getBytes();
try {
OutStream.write(comando);
}
catch (IOException e){
Toast.makeText(getBaseContext(),"Errore: comando non mandato",Toast.LENGTH_SHORT).show();
}
}
I saw a tutorial on YouTube but although I copy-pasted the code shown, the app doesn't send anything.
PS: Arduino and the phone are correctly connected.
PPS: sorry for my bad English.
You can use a Timer. In the toggle button onClick method you can call timedDataSender() and in the button off method you can set shouldCancel boolean variable to true and the sending task should be finished. Here is a sample code:
/* A sendTimer is used to periodically send data */
private Timer sendTimer;
private Boolean shouldCancel = false;
private void timedDataSender(){
TimerTask task = new TimerTask() {
public void run() {
runOnUiThread(new Runnable(){
#Override
public void run() {
sendData("10");
}
});
if(shouldCancel)
cancel();
}
};
sendTimer = new Timer("Timer");
/* TimerTask, startDelay, period */
sendTimer.scheduleAtFixedRate(task,0,50);
}
I'm making an app in Android Java, using Android Studio.
Every 0.1 seconds I want to update the text within a certain TextView.
I already managed to use a Handler to execute a method every 0.1 seconds (100 ms), but it doesn't automatically update the TextView.
It only updates the TextView to what I want when the user interacts with the app. It only updates it when, for example, the user slides the SeekBar or presses a button. It doesn't update when the user clicks on the screen, and not automatically without input either.
How can I make it such that it will update the value automatically, without user input?
Thanks in advance.
P.S: I'm new to Android and Java, and I'm using threads to get the value, in xml format, from a website.
Should I post any code, and if so, what exactly?
you can try updating the value of text view on the UI thread.
runOnUiThread(new Runnable() {
#Override
public void run() {
//update TextView here
}
});
Thread t = new Thread() {
#Override
public void run() {
try {
while (!isInterrupted()) {
Thread.sleep(100);
runOnUiThread(new Runnable() {
#Override
public void run() {
// update TextView here!
}
});
}
} catch (InterruptedException e) {
}
}
};
t.start();
You can update your textview using thread also, like this.
int prStatus = 0;
new Thread(new Runnable() {
public void run() {
while (prStatus < 100) {
prStatus += 1;
handler.post(new Runnable() {
public void run() {
tv.setText(prStatus + "/");
}
});
try {
Thread.sleep(150);
} catch (InterruptedException e) {
e.printStackTrace();
}
if(prStatus == 100)
prStatus = 0;
}
}
}).start();
I have started the Thread, In that thread i am trying to connect to the server, After receiving the response, I have to update the UI with event listeners(implemented through Interface). Here After receiving the response i need to show the popup Dialog once user clicks OK, need to continue the thread and complete the other process.
class ConnectionThread extends Thread {
ConnectionThread() {
this.setName("ConnectionThread");
}
#Override
public void run() {
// Need to pause the thread for sometime, Need to do the functionality here.
((Activity)mContext).runOnUiThread(new Runnable() {
public void run() {
// custom dialog
showAlertDialog();
// start the thread functionality again from that position.
}
});
}
I have tried with wait() concept and also join, which are not helped as expected. Any help appreciated.
you can use countdownlatch
class ConnectionThread extends Thread {
CountDownLatch countDownLatch = new CountDownLatch(1);
public ConnectionThread() {
this.setName("ConnectionThread");
}
#Override
public void run() {
try {
sleep(2000);
runOnUiThread(new Runnable() {
#Override
public void run() {
//update ui then
countDownLatch.countDown();
}
});
countDownLatch.await();
//start process again
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
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
Can someone explain to me 2 things about the thread code that I finally made almost working the way it should. I want to do a periodic task in the background every x seconds and be able to stop and start it at will. I coded that based on the examples I found, but I'm not sure if I made it in the right way. For the purpose of debugging, the task is displaying a time with custom showTime().
public class LoopExampleActivity extends Activity {
TextView Napis, Napis2;
Button button1,button_start,button_stop;
Handler handler = new Handler();
Boolean tread1_running = true;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
Napis = (TextView) findViewById(R.id.textView1);
Napis2 = (TextView) findViewById(R.id.textView2);
button1 = (Button) findViewById(R.id.button1);
button_stop = (Button) findViewById(R.id.button_stop);
button_start = (Button) findViewById(R.id.button_start);
button_stop.setOnClickListener(new OnClickListener() {
#Override
public void onClick (View v) {
tread1_running = false;
}
});
button_start.setOnClickListener(new OnClickListener() {
#Override
public void onClick (View v) {
tread1_running = true;
}
});
thread.start();
}// endof onCreate
final Runnable r = new Runnable()
{
public void run()
{
handler.postDelayed(this, 1000);
showTime(Napis2);
}
};
Thread thread = new Thread()
{
#Override
public void run() {
try {
while(tread1_running) {
sleep(1000);
handler.post(r);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
}
Now the questions are:
1)Will my thread quit forever if I stop it with the stop button?
2)Why can't I start it again with the start_button? If I add the tread.start() in a button, will it crash?
3) I tried a second version when I let the thread run and put a condition into the handler. The only way I can get it to work is to loop conditionaly in the handler by adding an
if (thread1_running) {
handler.postDelayed(this, 2000);
showTime(Napis2);
}
And changing the condition in a thread start to while (true) but then I have an open thread that is running all the time and I start and stop it in a handler, and it posts more and more handlers.
So, finally I get to the point it looks like that:
final Runnable r = new Runnable()
{
public void run()
{
if (thread1_running) handler.postDelayed(this, 1000);
showTime(Napis2);
}
};
Thread thread = new Thread()
{
#Override
public void run() {
try {
while(true) {
sleep(1000);
if (thread1_running) handler.post(r);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
Is the proper way to do that is to start and stop a whole thread? Or that is the best way?
The best way to achieve something like that would be, in my humble opinion, to postDelayed(Runnable, long).
You could do something like this. Class definition:
private Handler mMessageHandler = new Handler();
private Runnable mUpdaterRunnable = new Runnable() {
public void run() {
doStuff();
showTime(Napis2);
mMessageHandler.postDelayed(mUpdaterRunnable, 1000);
}
};
And control true run/stop like this:
To start:
mMessageHandler.postDelayed(mUpdaterRunnable, 1000);
And to stop:
mMessageHandler.removeCallbacks(mUpdaterRunnable);
It's much much simpler, in my humble opinion.
Threads a described by a state machine in java.
When a thread get outs of its run method, it enters in the stopped state and can't be restarted.
You should always stop a thread by getting it out of its run method as you do, it s the proper way to do it.
If you want to "restart the thread", start a new instance of your thread class.
You should better encapsulate your thread and its running field. It should be inside your thread class and the class should offer a public method to swich the boolean. No one cares about your datastructure, hide them. :)
You should consider using runOnUIThread for your runnable, its much easier to use than handlers.