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.
Related
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.
Consider this code:
class Solver {
private boolean abort = false;
public void solve(List<Case> cases) {
while(!abort) {
for(Case c : cases)
compute(c); // method that take too long to finish
}
}
// a bunch of methods
public void abort() {
abort = true;
}
}
// in another class
Solver solver = new Solver();
solver.solve(cases);
public void onSolveAborted() {
solver.abort();
}
How can I change this solution so I can abort the solve function instantly. I know I can implements the Runnable interface in Solver class so I can stop the thread. This will introduce many changes in our code and I don't know if the framework we are using allow creating threads.
This will not be possible without the use of threads. Something has to set abort() before the running thread will stop. Take a look at this example:
class Solver implements Runnable {
private List<Case> cases;
public Solver(List<Case> cases) {
this.cases = cases;
}
private void compute(Case c) {
try {
// Do some computation here
} finally {
// Sound the horns! Abandon ship!
}
}
public void solve(List<Object> cases) {
for (Case c : cases) {
try {
compute(c); // method that take too long to finish
} catch (InterruptedException e) {
// Hmm, maybe I should take the hint...
break;
}
}
}
public void run() {
solve(cases);
}
public static void main(String args[]) {
List<Case> cases = new ArrayList<Case>();
// Populate cases
Thread t = new Thread(new Solver(cases));
t.run();
do {
// Wait 30 seconds
t.join(30 * 1000);
// Not done yet? Lets drop a hint..
if(t.isAlive()) {
t.interrupt();
}
} while (t.isAlive());
}
}
Very simply, it launches solve in a thread. The main thread waits up to 30 seconds then interrupts solve method. The solve method catches the interruption and gracefully exits the computation. Unlike your solution using boolean abort, this launches an InterruptedException from anywhere in your thead code (and you should deal with the exception accordingly!) allowing you to halt execution at any time.
If you want more control, you can add the try.. catch inside compute so you can have a finally clause to close any opened files or whatnot. Perhaps better still, have a try.. finally in compute to deal with closing things in a "nice" way and the try.. catch (InterruptedException) in the solve method to handle what happens in the case of interruption (in short, cleanup logic and interruption logic don't have to be in the same method).
Do somthing like this
Let say, you have 100 cases, 10 has been solved and you want to abort remaing 90.
In your code, you are solving all the cases in one iteration, after that while loop check for abort.
public void solve(List<Case> cases) {
Iterator<Case> iterator = cases.iterator();
while (iterator.hasNext() && !abort) {
Case c=iterator.iterator.next();
compute(c);
}
}
Change your class to Runnable and use ExecutorService to run it. Then you can just use methods "shutDown()" or "shutDownNow()" methods. This is cleaner and less intrusive then what you suggested in your own question. Plus killing thread manually is a REALLY BAD idea. At some point in JDK itself in thread method "kill()" was killed as there is no clean way to do so properly
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 needed a while loop that would auto-refresh the text on the layout and I finally found a way like this (I only put the important part so you get the idea) :
public void restart() {
GetRate asyncRate = new GetRate();
asyncRate.execute();
}
private class GetRate extends AsyncTask<Void, Integer, String> {
#Override
protected String doInBackground(Void... params) {
try {
String p = urlfind();
return p;
}
catch (IOException e) {
return "0";
}
}
#Override
protected void onPostExecute(String p) {
price_text.setText(p);
restart();
}
now my question is, is this a good way of getting this done or could this cause problems ? the app seems to be working fine and I am not getting any errors for the moment, if there is a better or simpler way to do this please give an answer. thanks
Edit : this turned out to be a really bad idea even when I added sleep intervals the app would work for a 15min then crash so I dont advise anyone to use this.
Instead of calling the thread manually, AlarmManager or ScheduledExecutorService could be used to fire the task at certain intervals or at certain time.
AlarmManager is generally used for large intervals and the later one for short intervals.
Using these classes, you can fire your task and get your UI updated.
I have an app that needs to pull data from a server and insert it into an SQLite database in response to user input. I thought this would be pretty simple - the code that pulls the data from the server is a fairly straightforward subclass of AsyncTask, and it works exactly as I expect it to without hanging the UI thread. I implemented callback functionality for it with a simple interface and wrapped it in a static class, so my code looks like this:
MyServerCaller.getFolderContents(folderId, new OnFolderContentsResponseListener() {
#Override
public void onFolderContentsResponse(final List<FilesystemEntry> contents) {
// do something with contents
}
}
All still good. Even if the server takes an hour to retrieve the data, the UI still runs smoothly, because the code in getFolderContents is running in the doInBackground method of an AsyncTask (which is in a separate thread from the UI). At the very end of the getFolderContents method, the onFolderContentsResponse is called and passed the list of FilesystemEntry's that was received from the server. I only really say all this so that it's hopefully clear that my problem is not in the getFolderContents method or in any of my networking code, because it doesn't ever occur there.
The problem arises when I try to insert into a database via my subclass of ContentProvider within the onFolderContentsResponse method; the UI always hangs while that code is executing, leading me to believe that despite being called from the doInBackground method of an AsyncTask, the inserts are somehow still running on the UI thread. Here's what the problematic code looks like:
MyServerCaller.getFolderContents(folderId, new OnFolderContentsResponseListener() {
#Override
public void onFolderContentsResponse(final List<FilesystemEntry> contents) {
insertContentsIntoDB(contents);
}
}
And the insertContentsIntoDB method:
void insertContentsIntoDB(final List<FilesystemEntry> contents) {
for (FilesystemEntry entry : contents) {
ContentValues values = new ContentValues();
values.put(COLUMN_1, entry.attr1);
values.put(COLUMN_2, entry.attr2);
// etc.
mContentResolver.insert(MyContentProvider.CONTENT_URI, values);
}
}
where mContentResolver has been previously set to the result of the getContentResolver() method.
I've tried putting insertContentsIntoDB in its own Thread, like so:
MyServerCaller.getFolderContents(folderId, new OnFolderContentsResponseListener() {
#Override
public void onFolderContentsResponse(final List<FilesystemEntry> contents) {
new Thread(new Runnable() {
#Override
public void run() {
insertContentsIntoDB(contents);
}
}).run();
}
}
I've also tried running each individual insert in its own thread (the insert method in MyContentProvider is synchronized, so this shouldn't cause any issues there):
void insertContentsIntoDB(final List<FilesystemEntry> contents) {
for (FilesystemEntry entry : contents) {
new Thread(new Runnable() {
#Override
public void run() {
ContentValues values = new ContentValues();
values.put(COLUMN_1, entry.attr1);
values.put(COLUMN_2, entry.attr2);
// etc.
mContentResolver.insert(MyContentProvider.CONTENT_URI, values);
}
}).run();
}
}
And just for good measure, I've also tried both of those solutions with the relevant code in the doInBackground method of another AsyncTask. Finally, I've explicitly defined MyContentProvider as living in a separate process in my AndroidManifest.xml:
<provider android:name=".MyContentProvider" android:process=":remote"/>
It runs fine, but it still seems to run in the UI thread. That's the point where I really started tearing my hair out over this, because that doesn't make any sense at all to me. No matter what I do, the UI always hangs during the inserts. Is there any way to get them not to?
Instead of calling mContentResolver.insert(), use AsyncQueryHandler and its startInsert() method. AsyncQueryHandler is designed to facilitate asynchronous ContentResolver queries.
I think your original problem may have been that you are calling the run method on your new thread (which causes execution to continue on the current thread) instead of calling the start method. I think this is what Bright Great was trying to say in his/her answer. See Difference between running and starting a thread. It's a common mistake.
Man.Relax yourself.And anything would looks better.
At first,Start a Thread is Func start not Func run,if you want to start the new Thread
not only call the func run.
new Thread(Runnable runnable).start();
Then I bet use Handler sometimes would be better than AsyncTask.
You can run the query in the doInBackground(Integer int) overridden method of the AsynTask, and update the main UI on the onPostExecute(Integer int) method.