This has been killing me lately. I'm making a quick settings tile that should show as active or inactive based on whether it can communicate with a specific machine over a socket. Here are my declarations:
public class WakeUpTileService extends TileService {
private static volatile boolean online;
private final TimerTask timerTask;
private Timer timer;
Here's the constructor:
public WakeUpTileService() {
super();
online = true;
timer = new Timer();
timerTask = new TimerTask() {
#Override
public void run() {
boolean shouldBeOn = false;
try {
Socket s = new Socket();
// 3000 is the timeout in milliseconds.
s.connect(myInetSocketAddress, 3000);
// Connection was successfully established.
s.close();
shouldBeOn = true;
} catch (Exception e) {
// Connection failed.
// shouldBeOn is already false.
} finally {
if (shouldBeOn != WakeUpTileService.online) {
WakeUpTileService.online = shouldBeOn;
// This method causes onStartListening() to be called
// on the main thread so I can update the Tile.
requestListeningState(
getApplicationContext(),
new ComponentName(
getApplicationContext(),
WakeUpTileService.class
)
);
}
}
}
};
}
Here's where the timer gets started:
#Override
public void onTileAdded() {
super.onTileAdded();
timer.scheduleAtFixedRate(timerTask, 0, 60000);
// At the moment I have it checking once per minute
// For debugging purposes. I plan to make it less frequent.
}
And here's the code that uses the value of online to update the Tile. This gets called on the main thread after WakeUpTileService.online = shouldBeOn; in the TimerTask.
#Override
public void onStartListening() {
Tile t = getQsTile();
if(WakeUpTileService.online)
t.setState(Tile.STATE_ACTIVE);
else
t.setState(Tile.STATE_INACTIVE);
t.updateTile();
}
When I step through the code in the debugger, the TimerTask code is definitely finished before onStartListening gets called, and within the context of the TimerTask, online holds the correct value. Then, when onStartListening is called, online seems to revert to the value it had at the beginning.
Thoughts I've had about what might be going on:
The online being referenced in WakeUpTileService is somehow not the same object as is being referenced in the Runnable code (that's why I made online static and used WakeUpTileService.online instead of just online.)
The assignment to online is actually not happening before online is read by onStartListening(). Again, when I stepped through the code with the debugger, this doesn't appear to be happening, and just by looking at the code below, this doesn't seem reasonable.
I don't know what else could be happening here. Please help!
Update: korolar suggested that the two classes might have been loaded by different classloaders, and after some investigation, I found that that is the cause. My service is being loaded by dalvik.system.PathClassLoader and java.util.Timer is being loaded by java.lang.BootClassLoader. I don't, however, know how to work around or solve this issue. Can anyone provide some suggestions?
In case anyone else runs into this problem, I'll let you know what I eventually did to fix it.
Apparently using the java.util.Timer class at all is generally bad practice in Android programming. Instead, I rewrote my program using the Android AlarmManager class and IntentService. That completely bypasses the classloader problem.
Related
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.
Hello Developers This question may seem foolish to some readers and it may have answered before but I have seen many answers on this forum regarding my issue but I could not understand single one of them. So please be kind and answer my question if you could.
The thing is I am trying to develop something like a game and trying to add computer or AI player against a human player.
For that I added a CountdownTimer in OnCreate method and it worked fine only once as OnCreate executes only once. But when I tried to add a while loop inside a runnable and inside that while loop I added the same CountdownTimer but it started giving me errors.
if(player2_name.equals("Computer")) {
Runnable r = new Runnable() {
#Override
public void run() {
while (player2_name.equals("Computer")) {
CountDownTimer computer_player;
ct = 5;
firstplayer_game_button.setClickable(false);
computer_player = new CountDownTimer(5000, 1000) {
#Override
public void onTick(long millisUntilFinished) {
ct = ct - 1;
// timer_counter.setText("" + ct);
if (ct == 1) {
firstplayer_game_button.setClickable(true);
}
}
#Override
public void onFinish() {
// secondplayer_game_button.setBackgroundResource(R.drawable.button_player_2);
// firstplayer_game_button.setBackgroundResource(R.drawable.button_player_1);
// firstplayer_game_button.setClickable(true);
if (player2_name.equals("Computer")) {
secondplayer_game_button.setClickable(false);
} else {
secondplayer_game_button.setClickable(true);
}
if (checker != 1) {
Click_Condition_checker(2);
}
}
};
computer_player.start();
}
}
};
Thread mythread = new Thread(r);
mythread.start();
I know that a Runnable cannot interact with UI directly and for that we need handlers that is why I commented all the UI interfaces. But still no luck. And unfortunately I am not able to identify the errors. If Someone can help then it would be very kind for a foolish developer like me.
First, you don't need to use Runnable to make a CountDownTimer works. It's because CountDownTimer can directly interact with the UI if the code running inside the Activity.
Second, you should not use while loop inside your code. Especially for the following code:
while (player2_name.equals("Computer")) {
CountDownTimer computer_player;
...
computer_player = new CountDownTimer(5000, 1000) {}
...
}
which means you're creating new object until you exhausted all the device memory or until player2_name is not "Computer".
You should using a boolean flag instead.
I think what you need is can be achieved by restarting the CountDownTimer with something like this:
// use a more readable variable name instead of computer_player
final CountDownTimer cdtPlayer = new CountDownTimer(5000, 1000) {
#Override
public void onTick(long millisUntilFinished) {
// do something
}
#Override
public void onFinish() {
// do something
// restart to do all again
cdtPlayer.start();
}
};
cdtPlayer.start();
Regarding your following statement:
If Someone can help then it would be very kind for a foolish developer like me.
Everybody is a foolish when starting to learn something. Don't be too hard with yourself ;)
Not sure about your goal but your while loop seems troublesome. A while testing a string and not changing its value, it's going to run over and over (and by then create that many CountdownTimer which I don't think was your intent)
So, I have an activity with a handler.
private final Runnable m_Runnable = new Runnable()
{
public void run()
{
if(LiveAPI.getStatus() == 1){
matches = LiveAPI.getMatches();
listAdapter.notifyDataSetChanged();
}
LivePage.this.mHandler.postDelayed(m_Runnable, 5000);
}
};
Here I get some data and update my list with it. It works.
When I click on an item of my list, this functon is called
private void showLiveMatch(int position) {
Intent i = new Intent(this, LiveMatch.class);
i.putExtra("match", matches.get(position));
startActivity(i);
}
My new activity appears, wich also contains another handler:
private final Runnable m_Runnable = new Runnable()
{
public void run()
{
if(LiveAPI.getStatus() == 1){
match = LiveAPI.getMatch(match.getId());
displayCommentaries();
}
LiveMatch.this.mHandler.postDelayed(m_Runnable, 5000);
}
};
Sometimes this works as I want.
But in some cases it seems like in second activity is still called LiveAPI.getMatches() from the first handler rather than LiveAPI.getMatch(match.getId());
Every function displays a console text, and that's how I figure it out what function is called.
Can someone explain me why?
Once you post either m_Runnable (from LivePage or LiveMatch), it does its stuff and then schedules itself to run in 5 seconds. Basically, each time you start one with a click, it creates an infinite loop. Enough clicks and you will have the logic for each of these running constantly. (That is, unless you have some other code that periodically calls mHandler.removeCallbacks(m_Runnable); that you haven't shown us.) Without knowing more about what you're trying to do, it's hard to recommend how to fix this, but you should somehow avoid creating these kind of infinite loops.
Be aware that all handlers you create on the UI thread simply feed Runnable objects into the (single) MessageQueue for the thread. So there's no such thing as something being called from one handler or another.
So I have been trying to implement a progress indicator with no luck. I am not sure I understand managing threads with JavaFx very well, despite having read a bit about the Platform.RunLater and Tasks. So here is my use case.
My program allows users to connect to a database and look at some of the schemas and other objects in the database. Sometimes connecting to a large database and pulling up all its tables and info takes a while, so I would like to show a progress indicator. I am not trying to update the progress at all I would just like to make the progress indicator visible at a value of -1 while the process is running to pull everything from the database. Ideally I will have a progress indicator loaded in from an FXML file invisible. When I start the process of pulling info from the database I would like to make it visible.
When trying to make my progress visible it never showed up, so I decide to start out having it visible and making it invisible, just to see what happens. The progress indicator rotated nicely when I opened the program up, but as soon as I try to connect to the database it stopped rotating and just froze. I assume this is what happens when I try to make it visible too which is why it was never showing up.
The following is my current code, I would appreciate any detailed help with explanations so I can understand what is going on. Thanks
from the method that is doing most of the work.
//make progress indicator visible
pi.setVisible(true);
// separate non-FX thread
ExtractorThread t = new ExtractorThread();
t.setCp(cp);
t.start();
//Wait until the thread is done
try{
t.join();
}
catch(Exception e){
e.printStackTrace();
}
//Retrieve the dbextractor from the thread
DbExtractor dbe = t.getDbe();
//move on to the next page in the application
this.caster.goToDataSource(c, cp, dbe);
The ExtractorThread which does the work.
private class ExtractorThread extends Thread{
private ConnectionProperties cp;
private DbExtractor dbe;
public void run() {
dbe = new DbExtractor(cp);
try {
dbe.extract();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public DbExtractor getDbe() {
return dbe;
}
public void setCp(ConnectionProperties cp) {
this.cp = cp;
}
}
If I am supposed to use the Platform.RunLater I am not sure where to use it or why. Any help would be appreciated, thanks!
Use the javafx.concurrent API. Extend Task instead of Thread:
private class ExtractorThread extends Task<DbExtractor>{
private ConnectionProperties cp;
public DbExtractor call() throws Exception {
dbe = new DbExtractor(cp);
dbe.extract();
return dbe;
}
public void setCp(ConnectionProperties cp) {
this.cp = cp;
}
}
Then do:
//make progress indicator visible
pi.setVisible(true);
// separate non-FX thread
final ExtractorThread t = new ExtractorThread();
t.setCp(cp);
t.setOnSucceeded(new EventHandler<WorkerStateEvent>() {
public void handle(WorkerStateEvent event) {
DbExtractor dbExtractor = t.getValue();
this.caster.goToDataSource(c, cp, dbe);
}
});
t.setOnFailed(...); // similarly, to handle exceptions
new Thread(t).start();
I don't code JavaFX, and so I can't give you chapter and verse, but this line:
t.join();
will block the calling code until the background thread is through. Don't do this. Instead use some type of listener to get notified when the background thread finishes. If this were Swing, I'd use a PropertyChangeListener added to a SwingWorker to notify me when the background thread was done. I think that you can still use a PropertyChangeListener to do a similar thing with with JavaFX, but I cannot tell you if this would represent the canonical solution.
Also, don't extend Thread but instead implement Runnable. This won't fix your problem but is basic Java common sense.
I am trying to implement a repeat function on a custom MIDI player, but I am unable to implement a repeat function. Here are the classes I am using:
NotePlayer - Plays MIDI notes using Java's MIDI package.
GuitarTunerGUI
Interface to the NotePlayer class.
Provides six JButtons for each guitar string, a JComboBox for selecting the desired tuning, and a JCheckBox for toggling the repeat function.
Provides toggleRepeat() for toggling the repeatEnabled field, a private field of the GuitarTunerGUI class.
I created a SwingWorker that is responsible for playing a MIDI note in a separate thread. This solves the issue of keeping the GUI responsive while the note is being played.
However, a problem arises when repeat is enabled and the user pushes more than one button.
When the user pushes one of the six JButtons the listener does the following:
public void actionPerformed(ActionEvent event) {
// The note param is a private field of the listener object
MusicianWorker clapton = new MusicianWorker(note);
clapton.execute();
}
The execute method does the following:
protected Void doInBackground() throws Exception {
do {
NotePlayer.playNote(thisNote);
try {
Thread.sleep(3000);
} catch (InterruptedException ex) {
System.out.println(ex.getMessage());
}
} while (repeatEnabled);
return null;
}
An issue arises when the user pushes multiple buttons without toggling repeat. For example, when the 'A' button and the 'E' button are pushed sequentially, two threads are created and the 'A' and 'E' notes are both played repeatedly until repeatEnabled is toggled off. When the user pushes a JButton I need to first determine if any worker threads are currently executing and, if so kill those threads before playing the specified note. Thanks in advance for your time and feedback.
You need to maintain shared state between your workers. Introduce new boolean variable "playing". Before execution check whether playing flag is set to true, after execution set it to false again.
The code you have given is great, it just needs to be tweaked a little bit. When you create your SwingWorker, you should keep track of it in an instance variable (maybe in a List if you are going to be wanting to play multiple notes at some point?). Then, before playing a new note you check to see if the last note has finished, and if not, you cancel it.
Whether or not cancellation will have any effect on your MusicianWorker is up to you. The worker thread will be interrupted, which would mean that your Thread.sleep method would prematurely terminate if it is running - you would have to check your docs to see what effect it would have on NotePlayer.
Lastly, it seems that you don't actually need to be using the SwingWorker at all, since your background task is not interacting with the UI. You might want to investigate Executors.
You could try something like this:
public class AlbertHall {
private final ExecutorService es = Executors.newSingleThreadExecutor();
// No longer a local variable in the listener
private Future<Void> clapton; // ... or maybe a Collection of Futures
private class Listener implements ActionListener {
private final Note note;
public Listener(Note note) {
this.note = note;
}
public void actionPerformed(ActionEvent event) {
// Watch out, Clapton may finish after you have asked if he is done
// but before you call cancel
if (clapton != null && !clapton.isDone()) clapton.cancel(true);
// You may need to have a wait loop here if Clapton takes a while
// to leave the stage
// Next note
clapton = es.submit(new MusicianWorker(note));
}
}
static class MusicianWorker implements Runnable {
private final Note note;
public MusicianWorker(Note note) {
this.note = note;
}
public void run() {
boolean cancelRequested = false;
do {
NotePlayer.playNote(thisNote);
try {
Thread.sleep(3000);
} catch (InterruptedException ex) {
// Looks like we got cancelled
cancelRequested = true;
}
} while (repeatEnabled && !cancelRequested);
}
}
}