Method in Toggle Button crashes - java

Hey guys I am having an issue that a method I am trying to run every thirty seconds is causing my toggle button to crash. My goal is to send data to a database based on the button click, and while the toggle button is on to continue sending that data through a method every thirty seconds. When I click the button, I get the Unfortunately error and the app crashes.
To save the length of this post, this button to send the data only one time works fine:
uploadOnce.setOnClickListener(new View.OnClickListener()
{
#Override
public void onClick(View view) {
try {
SendCode(socket);
}catch(Exception e){
String stackTrace = Log.getStackTraceString(e);
sendEmail(stackTrace);
}
}
});
Notice that the above button click uses the SendCode method and it works correctly.
The error that I am having is using that same method with a timer on it, like so:
This is the toggle button onClick:
toggle.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
try {
if (toggle.isChecked()) {
makeToast("On");
sendForever();
}
}catch (Exception e){
String stackTrace = Log.getStackTraceString(e);
sendEmail(stackTrace);
}
}
});
If I take the sendForever() method out, the toggle button works fine as expected.
This is the sendForever method in the toggle button:
public void sendForever(){
if(toggle.isChecked()) {
while (toggle.isChecked()) {
try {
new Handler().postDelayed(new Runnable() {
// Runs a timer to send code to database every 30 seconds
#Override
public void run() {
try {
SendCode(socket);
} catch (Exception e2) {
String stackTrace = Log.getStackTraceString(e2);
sendEmail(stackTrace);
}
}
}, 30000);
} catch (Exception e) {
String stackTrace = Log.getStackTraceString(e);
sendEmail(stackTrace);
}
sendForever();
}
}
}
So the goal is that when this method is called, it checks to see if the toggle button is in the "ON" state, then while it is on it will run the SendCode method (which works fine in the button to send it only once) then wait 30 seconds. After the 30 seconds is over I am going to call the method again until the toggle button is hit again and breaks the loop.
My problem I am having is that I am using this on an OBD2 sensor in my car, and it is not hitting the sendEmail methods to shoot me a stacktrace of the error, so I am not able to post the stacktrace as of now.
If anybody has any advice on what is going wrong or what I can look at to fix this, it would be greatly appreciated. Once again, sorry for not being able to put the stacktrace up right now, I will edit the post if I am able to acquire it.

You call sendForever() from the method itself (in a loop, even). That will result in a stack overflow as the recursion is limited only by the toggle condition. Just remove the recursive call, since you already have the loop handling the repetition (well, that would solve the stack overflow, but see next paragraph for further issues).
Note also that you have a blocking task running in the UI thread. Since you block the UI thread, the toolkit will have no chance to make the button not toggled anymore, essentially locking up your application. Simplest is probably checking the condition after the previous sending is done, and schedule a new one if needed.
A side note: It's needless to do an if (toggle.isChecked()) check in sendForever() since you have the same condition in the while loop.

try to dump the stack trace on file system instead of email... may be your whole application is crashing
or may be you should post the code of SendCode(socket) that function might be locking some resources that are not released in 30 sec i.e. before the next function call is made

Related

How to utilize sleep method for calculator error

I am trying to set the calculator text to an error message, wait for 2 seconds, then clear the field text. Below is my current code.
public static void wait(int ms) {
try {
Thread.sleep(ms);
} catch(InterruptedException ex) {
Thread.currentThread().interrupt();
}
}
// TODO: 6/30/22 if decimal is clicked multiple times -> setText("Error")
if (field.getText().contains(".") && e.getSource() == decButton) {
field.setText("error text here");
wait(1000);
field.setText("blank here");
} else if (e.getSource().equals(decButton)) {
field.setText(field.getText().concat("."));
}
So far, the field text sets directly to ("blank here") and completely skips the error message. I have tried moving the error message to different areas of the program (within the if statement) but have yet to find a conclusion.
Are you using Swing? Is the code you show running from an event handler (triggered by keypress, mouseclick or something)? Then this cannot work.
The code is running in the Event Dispatcher Thread (EDT). Once you use something like field.setText() you have to exit your code and allow the EDT to fire the updates into the UI. But instead, you 'keep it busy' by waiting a second, then requesting the next update into the UI. Only then your method exits, and the user did not see the first message.
What you need to do is to set the update, then free up the EDT. How do you get the message to disappear one second later? Use a Swing Timer to trigger the action:
field.setText("error text here");
ActionListener errorHider = new ActionListener() {
public void actionPerformed(ActionEvent evt) {
field.setText("blank here");
}
};
Timer t = new Timer(1000, errorHider);
t.setRepeats(false);
t.start();

The work manader's work is not controlled

I have a wok manager that I run from the Main Activity when the user logs into the application. So, I will tell you in more detail what I do in the manager: in it I start a stream in which every second there is a mining of the virtual currency of my application, that is, simply put, I just increase the variable every second.
Moving on to the problem, here's how I run the manager
Constraints constraints = new Constraints.Builder()
.setRequiredNetworkType(NetworkType.CONNECTED)
.build();
miningWorkRequest = new
OneTimeWorkRequest.Builder(MiningManager.class)
.setConstraints(constraints)
.build();
WorkManager.getInstance(getApplicationContext()).enqueue(miningWorkRequest);
Launching the manager completely as in the documentation.
And now the manager himself is with my mining stream. Before increasing the variable, I get it every second from Firebase Realtime, and then the miningMoneyFun() method is triggered to increase it.
#NonNull
#Override
public Result doWork() {
firebaseModel.initAll();
RecentMethods.UserNickByUid(firebaseModel.getUser().getUid(), firebaseModel, new Callbacks.GetUserNickByUid() {
#Override
public void PassUserNick(String nick) {
RecentMethods.GetActiveMiner(nick, firebaseModel, new Callbacks.GetActiveMiners() {
#Override
public void GetActiveMiners(ArrayList<Miner> activeMinersFromBase) {
if(activeMinersFromBase.size()>0){
Thread thread = new Thread()
{
#Override
public void run() {
try {
RecentMethods.UserNickByUid(firebaseModel.getUser().getUid(), firebaseModel, new Callbacks.GetUserNickByUid() {
#Override
public void PassUserNick(String nick) {
RecentMethods.GetTodayMining(nick, firebaseModel, new Callbacks.GetTodayMining() {
#Override
public void GetTodayMining(double todayMiningFromBase) {
todayMining=todayMiningFromBase;
}
});
}
});
while(true) {
Thread.sleep(1000);
miningMoneyFun();
Log.d("#####", "go "+ todayMining);
}
} catch (InterruptedException e) {
}
}
};
thread.start();
}
}
});
}
});
return Result.success();
What specifically does not suit me, I see from the log that the thread can be executed 5, 10 or even 15 times per second. I thought it was a thread, but when I commented it out and put the log in the DoWork() method, the log also appeared many times per second. I want the DoWork() method to run once, and then the thread itself functions every second and as expected. I saw 2 similar questions on StackOverflow, but none had clear answers, please help and sorry for the English
Are you sure that it is only one worker?
Please check like this the number of active works:
https://developer.android.com/studio/inspect/task#view-workers
Also, you should be using unique work to be sure that there are no multiple workers:
https://developer.android.com/topic/libraries/architecture/workmanager/how-to/managing-work#unique-work
EDITED:
I am more confused by your comment. Please provide screenshots and the code the enqueue the work. At the moment you don't use periodic and unique.
Please note that you can have only 1 work in the inspector, but you can see a big list of all the executions of it.
Also, are you sure about the id? Do you generate it or it is static. It should not be possible to have multiple works when it is unique.
What flag do you use? KEEP?
Also now I saw your code in the Worker. I don't see how you block the doWork() to finish. I think you return Success, but at the same time, you have another Thread running with nothing to prevent the app to be killed.

Creating another Thread

I'm doing too much work on the main thread, and so I want to learn how to run some things on other threads. But I'm having difficulties understanding how threads work.
From what I've gathered, you can no longer stop or cancel a thread, and the way that I need it to work is that, if Button A is clicked, do the function, if Button B is clicked, stop as to not use resources.
Here's where I stand:
> //Inside OnCreate
Thread newthread = new Thread(new Runnable(){
#Override
public void run(){
while(bool) {
...
}
...
}
});
newthread.start();
Button A Listener(){
bool = true;
}
Button A Listener(){
bool = false;
}
when I first start the thread it runs, but after changing bool to false, and back to true it doesn't.
putting return at the end destroys the thread, but calling start again isn't allowed.
I've tried putting 2 while loops, but it still doesn't switch between the loops.
Is there even a reason to use interrupt? and how would it be implemented?
What I need is to create another thread, have it keep doing something until I click button A, then resume when I click on Button B, that's all.
but calling start again isn't allowed.
Not sure why don't you can not call start again, but I think it's good solution. Base on your requirement tries this code put inside run
while (!Thread.currentThread().isInterrupted()) {
try {
if (loop) {
...
}
} catch (InterruptedException e) {
return;
}
}
Why we need to check Thread.currentThread().isInterrupted() because when your Activity destroy you should call newthread.interrupt() to release your Thread because it can be leak memory.

Toast Messages do not show up

Hey I have a problem with toast messages which does not show up. I'm creating an app which displays user‘s message into Morse code and I want toast message show up to inform which character is diplayed now. But when I put it like below, toast messages do not show up. It's probably because next function which is called are somehow killing the previous one, cause when I removed other commands and left only showStatus() the messages appeared.
How can I deal with this situation?
public void dispDot()
{
final Parameters pM = cameraMorse.getParameters();
pM.setFlashMode(Parameters.FLASH_MODE_TORCH);
cameraMorse.setParameters(pM);
MorseActivity.backgroundMorse.setBackgroundResource(R.drawable.background1on);
waitFor(1);
pM.setFlashMode(Parameters.FLASH_MODE_OFF);
cameraMorse.setParameters(pM);
MorseActivity.backgroundMorse.setBackgroundResource(R.drawable.background1a);
}
//function that displays the line in Morse code
public void dispLine()
{
final Parameters pM = cameraMorse.getParameters();
pM.setFlashMode(Parameters.FLASH_MODE_TORCH);
cameraMorse.setParameters(pM);
MorseActivity.backgroundMorse.setBackgroundResource(R.drawable.background1on);
waitFor(3);
pM.setFlashMode(Parameters.FLASH_MODE_OFF);
cameraMorse.setParameters(pM);
MorseActivity.backgroundMorse.setBackgroundResource(R.drawable.background1a);
}
public void showStatus(char character)
{
//status1.setTextColor(Color.WHITE);
//status1.setText("Status: Now displaying "+character);
toast = Toast.makeText(MorseActivity.this, "Displaying "+character,Toast.LENGTH_LONG);
toast.setGravity(Gravity.TOP, 0, 30);
toast.show();
}
public void morseMessageTranslator()
{
morseMessage = textBox.getText().toString();
morseMessage = morseMessage.toUpperCase();
if(morseMessage.length()>0)
{
char character;
for(int a=0;a<morseMessage.length();a++)
{
character=morseMessage.charAt(a);
switch (character)
{
case 'A': //.-
showStatus('A');
dispDot();waitFor(1);dispLine();waitFor(1);
break;
case 'B': //-...
showStatus('B');
dispLine();waitFor(1);dispDot();waitFor(1);dispDot();waitFor(1);dispDot();waitFor(1);
break;
UPDATE:
Ok it turns out that waitFor() function is the cause.
public void waitFor (final int time)
{
Thread waitthread = new Thread(new Runnable() {
// After call for background.start this run method call
public void run() {
try {
Thread.sleep(time*500);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
});
waitthread.start();
try {
waitthread.join();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
But still don't know how to show toast before the wait is launched.
As your comments on this question show, the reason your Toasts aren't showing is because of your waitFor() method. As described, this performs a pointless calculation, which a) wastes precious CPU time (and therefore battery) and b) performs this on the UI thread. This means that anything that should happen on the UI thread won't happen all the time waitFor() is running, including Toasts.
You'll have to include some sort of threading here to get over this issue (the Android Developers website has a good tutorial). You'll want the dispDot, dispLine and waitFor calls to happen on a background thread. Bear in mind that if any of these three methods interact with your UI, they must do that back on the UI thread (see Communicating with the UI thread in the linked tutorial).
Previous (wrong) answer
You're creating your Toasts, but not calling show() on them! It's a very easy mistake to make. Just change the line:
toast = Toast.makeText(MorseActivity.this, "Displaying "+character,Toast.LENGTH_LONG);
to
toast = Toast.makeText(MorseActivity.this, "Displaying "+character,Toast.LENGTH_LONG).show();
Add .show() to the end of you toast codes
toast = Toast.makeText(MorseActivity.this, "Displaying "+character,Toast.LENGTH_LONG).show();

How to get and return user input in the same method on Android

(I know this question has been asked in many different flavors, thanks for considering the specifics of my situation).
I am implementing a 3rd party library. One of the methods requires user input, and the return value of this method needs to be the user's response. Specifically, this method needs to pop up an alert dialog. The user will have 3 buttons to choose from and the return value of this method must be an int identifying the button the user clicked.
The only way I've gotten this working is by calling this alert method from an AsyncTask and from there calling runOnUIThread to pop up the alert and then wait for the response. Where I'm stuck is the case where this alert method is being called from the UI thread. Is there a similar work around to wait on a response in this case?
This is what my code currently looks like... as long as this method is called from an AsyncTask I'm good to go. BUT, I need to be able to call it from the UI thread.
public int getInput(final Object [] args)
{
getActivity().runOnUiThread(new Runnable(){
#Override
public void run()
{
buildDialog(args).show();
}
});
try
{
synchronized(this)
{
wait();
}
}
catch (Exception ex)
{
return 0;
}
return m_Result;
}
public void setRetValue(int result)
{
m_Result = result;
synchronized(this)
{
notify();
}
}
(setRetValue gets called from onClick in the OnClickListeners of the buttons in the dialog.)

Categories

Resources