How can I make a certain function execute after every 10 seconds for an infinite time?
What I have done till now: I am getting the location values of the user from the App and storing them on the server. I am using a service, so that, the code keeps running for an infinite time, I am using a Broadcast receiver, so that, if the phone is booted, the service should start again and starts sending me the location.
The issue Everything works perfectly fine for about first 10-15 minutes, but, after this, the service gets stopped by itself. Also, when the user signs up for the App, authorized tokens are generated. These tokens are also sent in the POST call as one of the parameters, for security purposes. Even these tokens are lost, despite working perfectly fine for the initial 10 minutes. I am storing these tokens in SharedPreferences. Any help in this regard would be highly appreciated.
Code for SharedPreferences
Log.i("onCreate", "onCreate");
Log.i("atoken value", ConfirmToken.avalue);
Log.i("utoken value", ConfirmToken.uvalue);
atoken = ConfirmToken.avalue;
utoken = ConfirmToken.uvalue;
Log.i("atoken value", atoken);
Log.i("utoken value", utoken);
Log.i("Starting SharedPref", "Starting SharedPref");
SharedPreferences settings = getSharedPreferences(PREFS_NAME, 0);
SharedPreferences.Editor editor = settings.edit();
editor.putString("atoken", atoken);
editor.putString("utoken", utoken);
editor.commit();
if (settings.contains("atoken")) {
atoken = settings.getString("atoken", "nulll");
Log.i("Inside SharedPref", atoken);
}
if(settings.contains("utoken")) {
utoken = settings.getString("utoken", "nulll");
Log.i("Inside Sharedprefs", utoken);
}
//Calling UpdateData here so that values of lat, lng get updated, before they are used by MyService.java
UpdateData();
startService(new Intent(this, MyService.class));
Now, the update data function simple makes a POST call, using these tokens and Location values as parameters.
This is how I run a task every few seconds. Note that it runs in another thread, so accessing UI elements needs runOnUiThread call, but since you are in a service, you will not have any issues with that.
private ScheduledThreadPoolExecutor taskExecutor;
private void stopTimerTask() {
if (taskExecutor != null)
taskExecutor.shutdownNow();
// keep one task at any given time
taskExecutor = new ScheduledThreadPoolExecutor(1);
}
private void startTimerTask() {
stopTimerTask();
taskExecutor.scheduleWithFixedDelay(Timer_Tick, TIMER_INITIAL_DELAY, TIMER_PERIOD, TimeUnit.MILLISECONDS);
Log.d("Pool", "Timer Task Running");
}
private Runnable Timer_Tick = new Runnable() {
#Override
public void run() {
// Do something
}
};
You should however be aware that Android OS may terminate your service at any time when running low on resources or it feels like the service is doing too much work. You should start focusing on how to restore it's state, just like every one else does.
I think the following is the easiest way.
Also remember if you are making a network call, make an asynchronous request so that your app doesn't stop working while your app waits for the response.
for(long startTime = new Date().getTime();new Date().getTime() - startTime ==10000; startTime++)
{ /*your code goes here*/ };
No matter what you do, if the system is low on resources, it will terminate your app.Please let me know weather this works for you or not :)
Related
Android Periodic work will run periodically but i need to schedule a task within specific time. Suppose i want to run a task every day for 20 days only. I didn't find any way to do that.
How can i do that?
The offered API allows to cancel a work unconditionally:
If you no longer need your previously enqueued work to run, you can ask for it to be cancelled. Work can be cancelled by its name, id or by a tag associated with it.
But you can use your favorite storage to tack the number of days until they reach to 20 days. And every time the work is performed; i.e. when the doWork() callback gets called; you can increment the no. of days.
Here's an example of SharedPreference:
public static final String WORK_TAG = "WORK_TAG";
public static final String DAY_NO = "DAY_NO";
#androidx.annotation.NonNull
#Override
public Result doWork() {
SharedPreferences prefs = getApplicationContext().getSharedPreferences("prefs", MODE_PRIVATE);
int dayNo = prefs.getInt(DAY_NO, 1);
if (dayNo == 23) {
// cancel the work
WorkManager.getInstance(getApplicationContext()).cancelAllWorkByTag(WORK_TAG);
Log.d(TAG, "The periodic work has been stopped");
} else {
// Increment the number of days
dayNo++;
SharedPreferences.Editor editor = prefs.edit();
editor.putInt("DAY_NO", dayNo);
editor.apply();
// Do your work here
}
return Result.success();
}
Make sure that you tag the work with the same tag when you initialize the workRequest:
PeriodicWorkRequest workRequest = new PeriodicWorkRequest.Builder(
MyWorkerclass.class, // Worker class
.......
.addTag(WORK_TAG)
.build();
Also notice that any other work with the same tag will be cancelled as well, so make sure it's a unique tag.
I have an Activity and a Service.
In my Activity, a button interacts with the Service to start/stop GPS logging.
My Service has 3 state indicators: One for being connected to Google Play Services, one for actively logging GPS, and one for processing what was logged.
When connected to Google Play Services the Service flow is this:
Ready -> Logging -> Processing -> Ready
The Service will broadcast these states as follows:
private void UpdateStatusBroadcast() {
//Save status variables to intent
Intent intent = new Intent(this.getString(R.string.BroadcastStatusIntent));
intent.putExtra(getString(R.string.BroadcastIsConnected), mIsConnected);
intent.putExtra(getString(R.string.BroadcastIsTripActive), mIsTripActive);
intent.putExtra(getString(R.string.BroadcastIsProcessing), mIsProcessing);
//Send the broadcast
LocalBroadcastManager.getInstance(this).sendBroadcast(intent);
}
My Activity receives the states as follows:
private class StatusReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
mIsConnected = intent.getBooleanExtra(getString(R.string.BroadcastIsConnected), false);
mIsTripActive = intent.getBooleanExtra(getString(R.string.BroadcastIsTripActive), false);
mIsProcessing = intent.getBooleanExtra(getString(R.string.BroadcastIsProcessing), false);
HandleConnectionStatus();
HandleTripStatus();
}
}
Then comes my problem. In HandleTripStatus(), posted below, i change the text and background of a button to reflect what the Service is currently doing. This works fine for the first and the third case. I never see the second background drawn however, in spite of receiving the correct boolean values.
private void HandleTripStatus() {
Button tripButton = (Button) findViewById(R.id.TripButton);
Button liveMapButton = (Button) findViewById(R.id.LiveMapButton);
if (mIsTripActive) {
tripButton.setText(R.string.TripButtonTitleStop);
tripButton.setBackground(ContextCompat.getDrawable(mContext, R.drawable.trip_button_stop_shape));
liveMapButton.setEnabled(true);
} else if (mIsProcessing) {
tripButton.setText(R.string.TripButtonTitleStopping);
tripButton.setBackground(ContextCompat.getDrawable(mContext, R.drawable.trip_button_stopping_shape));
liveMapButton.setEnabled(false);
} else {
tripButton.setText(R.string.TripButtonTitleStart);
tripButton.setBackground(ContextCompat.getDrawable(mContext, R.drawable.trip_button_start_shape));
liveMapButton.setEnabled(false);
}
}
To debug the issue i verified the following:
Text and background resource is correctly defined (i.e. trying to use
it instead of the first and third case works)
The if-else conditions runs when expected (i.e. the "else if" condition actually runs when I expect it to. Verified by breakpoint.)
No other if-else condition is used in the process. (i.e, only the correct condition is run.)
Some other code that could possibly be relevant:
This is how the Activity requests that the GPS logging should stop (Leading to the processing step before finishing)
private void EndTrip() {
//Create message to TripService with intent to run case for END_TRIP
Message message = Message.obtain(null, TripService.END_TRIP, 0, 0);
//Send the Message to the Service
try {
mMessenger.send(message);
Toast.makeText(mContext, R.string.TripStopToast, Toast.LENGTH_SHORT).show();
} catch (RemoteException e) {
Log.e("Debug", "Failed to contact TripService");
}
}
This is the structure of what happens in the Service after receiving the message from the Activity.
private void EndTrip() {
//Stop retrieving location updates
//Broadcast the updated status and begin processing the trip
mIsTripActive = false;
mIsProcessing = true;
UpdateStatusBroadcast();
//Processing the collected data
//Finish up
mIsProcessing = false;
UpdateStatusBroadcast();
stopForeground(true);
}
I am all out of ideas. What can the cause be? Why does the button background not change in the else-if?
After too many hours of trial and error, I found the cause to be thread-related.
What I learned:
My service doing its work (Processing) would hang up the UI thread until done
This was quite simply because the service was running on the UI thread
Android does not automatically run services in a thread seperate from the rest of your application.
It is possible to run your service on a different thread. To do this, add the following to your AndroidManifest, inside your service:
android:process=":WhateverNameYouLikeForYourThread"
Note that this of course broke the broadcasts i relied on. This was however easy to fix; The consequence is that I can no longer use LocalBroadcastManager
By example - Instead of
LocalBroadcastManager.getInstance(this).sendBroadcast(intent);
i now use
sendBroadcast(intent);
instead. This does however mean that the broadcasts are less private.
I am trying to write an app that shows the user a string on textview, and replace the text due to user input. This should happen on time intervals of 60 seconds.
I tried to use a countdown timer, which sets a boolean var to false on it's onFinish method. While this boolean is true, I am trying to change the strings in the textview (further work will be to change the text due to the user actions, but I'm still trying to get this work for simple actions).
For now, the app seems to be stucked and nothing happens, but if I am removing the while loop, there is a single text on the screen (as it should be).
Is there a problem using a while loop for that purpose? Is there a different way to work along with the timer?
(p.s I know there are some problems in the code, but this is just for isolating the problem. Thanks!)
EDIT:
I will try to clarify my intentions:
I want to give the user tasks, which he should perform in a given time. I want to display the time remaining on the screen, along with the task to perform. When the user finishes the task, if there is still time on the clock, he will press a button and another task will appear. The goal is to finish as many tasks as possible in the given time.
My code:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_start_game);
Bundle b = getIntent().getExtras();
game = b.getParcelable("game_record");
figuresList = new ArrayList<String>(game.getFiguresList());
clockView = (TextView) findViewById(R.id.clockView);
figureText = (TextView) findViewById(R.id.figureText);
timer = new CountDownTimer(10000, 1000) {
public void onTick(long millisUntilFinished) {
clockView.setText(Long.toString(millisUntilFinished / 1000));
}
public void onFinish() {
clockView.setText("done!");
isTurn = false;
}
};
playRound();
}
private void playRound() {
figuresIterator = figuresListUsage.iterator();
isTurn = true;
String nextFigure = figuresIterator.next();
timer.start();
while (isTurn == true) {
figureText.setText(nextFigure);
nextFigure = figuresIterator.next();
}
}
It's a little difficult to understand your games logic but the main problem i see with your code is that you are entering the loop in a lifecycle event handler. Take a look at this lifecycle description. You are stopping it in onCreate and there is still work to be done before the activity will finish its lifecycle handling.
I suggest you try making play round event bound or use a diffrent thread for it. there are alot of threading APIs for android and as i dont know the nature of your game rounds i cant recommend any.
I have a requirement to get data from server by sending a call after specified interval like 5 minutes. So app would keep checking for new data after 5 minutes. It is just like gmail or facebook. Which automatically get new feeds or emails after some time and show in list. I am using service for this like following:
public class MessagesLoaderService extends Service {
// constant
// run on another Thread to avoid crash
private Handler mHandler = new Handler();
// timer handling
private Timer mTimer = null;
//********************************************************************************************************************************/
#Override
public IBinder onBind(Intent intent) {
return null;
}
//********************************************************************************************************************************/
#Override
public void onCreate() {
// cancel if already existed
if (mTimer != null)
{
mTimer.cancel();
}
else
{
// recreate new
mTimer = new Timer();
}
// schedule task
mTimer.scheduleAtFixedRate(new MessageLoaderTask(), 0, Commons.TIME_INTERVAL_REFRESH_MESSAGES);
}
//********************************************************************************************************************************/
class MessageLoaderTask extends TimerTask
{
#Override
public void run() {
// run on another thread
mHandler.post(new Runnable()
{
#Override
public void run() {
//Get Data from Server and store in local db
}
});
}
}
#Override
public void onDestroy() {
// TODO Auto-generated method stub
super.onDestroy();
Utils.showToast_msg(getApplicationContext(), "Service Destroyed");
}
//********************************************************************************************************************************/
}
//////////////////////////////////////////////////////////////////
Starting service from main activity MainActivity:
startService(new Intent(this, MessagesLoaderService.class));
I want service to run (send calls after 5 minutes) only when the app is running or in foreground/background. But the problem is that it keeps running even if I exit from the application. I want service to stop when Application is closed. Any solution for this?
Shouldn't you stop your timer in "OnDestroy" with mTimer.cancel() if you want it to stop ?
This method works when you enter the activity which actually queries the server. Call the method in onCreate. If value returned is true, then fetch data from server, if false, do whatever is in youf flow.
This Example below uses Singleton class. The current system time, plus five minutes is stored in singleton class variable, while local variable stores the current time. If current time exceeds the time of Singleton variable, then true is returned and it is time to call server.
SingletonClass app;
app = (SingletonClass ) getApplication();
public boolean serverQueryFrequency() {
boolean isTimeElapsed;
Calendar cal = Calendar.getInstance();
long time = cal.getTimeInMillis();
// If No Time is set, only then Set the Current time + 10 into
// application variable. This should fire only once, until 10 minutes
// have passed
if (app.getServerCallTime() == 0) {
Calendar cal2 = Calendar.getInstance();
// updating calendar to get current time + 10
cal2.add(Calendar.MINUTE, 5);
long timeTen = cal2.getTimeInMillis();
app.setServerCallTime(timeTen);
// returning true, to enable server check
return true;
}
// Log.v("******", "Current : " + time);
// Log.v("******", "App Time : " + app.getServerCallTime());
// Comparing current time with SeverCalltime which is set 10 minutes
// ahead. Code below fires conditionally as stated
if (time == app.getServerCallTime() || time > app.getServerCallTime()) {
isTimeElapsed = true;
// Once true fired from here, reset serverCallTime
app.setServerCallTime(0);
} else {
// 5 minutes have not passed
isTimeElapsed = false;
}
// returning the related value
return isTimeElapsed;
}
you can stop service by using this line
stopService(new Intent(this, MessagesLoaderService.class));
so your service get stopped
you need to identify in your app from where your exiting the app at that point you need to call above code also OS automatically kill the service in certain circumstances like low battery and so on but this is not good solution so you can stop it by above line in your exit point of application
I have learned when the app is closed the service get closed also because they are in a one thread, so the service should be on another thread in order fot it not to be closed, look into that and look into keeping the service alive with alarm manager here an example http://www.vogella.com/articles/AndroidServices/article.html this way your service won't be shown in notification.
lastly, after all the research I've done I'm coming to realize that the best use of a long running service is start foreground(); because it is made for that and the system actually deals with your service well.
when the user presses back button on the first page of your app..means they want out.
override the onbackpressed and put the stopService call there.
else..
use an exit button..give it an onclick and inside it put the stopService there
I am trying to prevent my application calling the same method twice in the event of a double-click, or if the user presses different buttons quickly, almost at the same time.
I have clickable Views, acting as buttons, that call the same method but passing different parameters. This is the call:
startTheSearch(context, getState(), what, where);
Inside this method I'm creating a new Thread, because it queries a web server for the result:
new Thread(new Runnable() {
public void run() {
progDiag = ProgressDialog.show(ctx, null, "Searching", true);
getServerXML(context, what, where, searchIsCustom, mOffset);
handler.sendEmptyMessage(0);
}
}).start();
The problem is that upon two quick clicks, the method is fired twice, two threads are created, and consequently two new activities are created. That makes my app crash.
When the methods are done, and we have the result from the server, we call the handler:
private Handler handler = new Handler() {
#Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
try {
Intent i = new Intent(Golf.this, Result.class);
Bundle b = new Bundle();
b.putString("what", mWhat);
b.putString("where", mWhere);
b.putInt("offset", mOffset);
b.putBoolean("searchIsCustom", searchIsCustom);
i.putExtras(b);
startActivityForResult(i, Activity.RESULT_OK);
progDiag.dismiss();
} catch (Exception e) {
Alerts.generalDialogAlert("Error", "settings", ctx);
}
}
};
I tried to have a global boolean variable called "blocked" initially set to false, creating a condition like:
if(!blocked){
blocked = true;
new Thread(new Runnable() {
public void run() {
But this only seems to work on slower phones like the G1, I tried on Nexus and before it set blocked = true, the second request has was granted. So is there any way I can block the method being called if it's already running, or if the thread has started so it wont create a new one?
In the book Hello Android the author Ed Burnette gives a very nice solution for this problem.
To leave your app snappy and responsive you should create a second thread to do the web request.
Instead of creating a new thread for every request you can use a SingleThreadExecutor. If you start a second thread you can check if you are waiting for the result of another thread and block until this thread is ready, or cancel the old thread and start a new one if you think that the user now wants to do some other thing.
You can find the source code from the book on this page. Look for the Translate folder inside the source. It is a nice example on how to react to user commands and how to prevent too many web requests because of a user touching a lot on the gui.
Instead of using the "blocked" Boolean, you might investigate using a mutex. They're designed for this kind of problem.
I tried on Nexus and before it set
blocked = true, the second request has
was granted
That is impossible. Android UIs are single-threaded. There is no way that two onClick() methods of OnClickListeners will be called at the same time.