I found some info about how to access the context from the subclass and also some info about
runOnUiThread(new Runnable() {
public void run() {
// Do something
}
});
But in my case it does not work. The application is still running, but the activity is maybe already destroyed. The first (main) activity is the parent activity of the one where my TimerTask is created. My code:
TimerTask tt = new TimerTask() {
#Override
public void run() {
// do something (cut)
// and at the end show info
getParent().runOnUiThread(new Runnable() {
#Override
public void run() {
Toast.makeText(getParent(),
getResources().getString(R.string.st_toast_msg_stopped),
Toast.LENGTH_LONG).show();
}
});
}
};
curTimer.schedule(tt, millisecondsUntilStop);
There is no error/ exception at log. But the toasted message is not shown. :-(
Now I have no Idea what I can else do/ try. I hope someone of you can help me.
P.S.: Maybe I use the wrong context? But I tried also some other context like the Context of the current activity, the ApplicationContext, ... .
Well you are using the wrong context here that is getParent(). Instead of using getParent() try to use the current_Activity.this like this,
TimerTask tt = new TimerTask() {
#Override
public void run() {
// do something (cut)
// and at the end show info
Activity_name.this.runOnUiThread(new Runnable() {
#Override
public void run() {
Toast.makeText(Activity_name.this,
getResources().getString(R.string.st_toast_msg_stopped),
Toast.LENGTH_LONG).show();
}
});
}
};
Rather than using a TimerTask, why not user the AlarmManager and set a PendingIntent to fire off a broadcast? When you fire of the broadcast and catch it in your own BroadcastReciever that you've made, you'll have context in your BroadcastReciever with which to display your toast. Here's a quick high level example.
Where ever you're setting up your TimerTask in your activity, do this instead:
AlarmManager alarmManager = (AlarmManager)Context.getSystemService(Context.ALARM_SERVICE);
Intent broadcastIntent = new Intent("yourBroadcastAction");
PendingIntent pendingIntent = PendingIntenet.getBroadcast(this, 0, broadcastIntent, 0);
alarmManager.set(AlarmManager.ELAPSED_REALTIME, millisecondsUntilStop, broadcastIntent);
Then just create a BroadcastReciever that has a filter for the yourBroadcastAction and in the onRecieve() method do your toast like so:
public void onRecieve(Context context, Intent intent){
Toast.makeText(context,
getResources().getString(R.string.st_toast_msg_stopped),
Toast.LENGTH_LONG).show();
}
Related
I have an alarm application.
Flow looks like this :
WakefulBroadcastReceiver(Acquires wakelock) --->> Intent service -->> startActivity
public class AlarmService extends IntentService {
protected void onHandleIntent(Intent intent) {
Intent activityIntent = new Intent(this, TriggeredActivity.class);
activityIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(activityIntent);
Basically WakefulBroadcaseReceiver starts an intent service using startWakefulService(). Inside intent service's onHandleIntent(), only work I am doing is further starting a new activity using startActivity(). That new activity is where I am using mediaPlayer in a loop, which sounds the alarm. That activity has a dismiss button, which waits for user click to stop the media player & activity finishes.
Now the problem I am facing is that after calling startactivity() inside intent service, I can not wait for TriggeredActivity to finish(no equivalent to startActivityForResult in Service) and then complete wakeful intent. Related link
startActivity(activityIntent);
WakefulBCastReceiver.completeWakefulIntent(intent); /* can't do here */
So I am not explicitly releasing wakelock here.
My question is will the wakelock be released automatically(link-to-death), when the process that is holding it is killed.
If yes, then in my particular scenario, I need not call WakefulBCastReceiver.completeWakefulIntent().
Yes, you need to use completeWakefulIntent.
You need to put your TriggeredActivity intent into EXTRAs.
#Override
public void onReceive(Context context, Intent intent) {
Intent intentService = new Intent(context, NotificationsIntentService.class);
// Inserting data inside the Intent
intentService.putExtra(NotificationsIntentService.EXTRA_NOTIF, new Intent(context, TriggeredActivity.class));
startWakefulService(context, intentService);
}
NotificationsIntentService.class
public class NotificationsIntentService extends IntentService {
private static final String TAG = "DebugNotifIntent";
public static final String EXTRA_NOTIF = "extra_notif";
public NotificationsIntentService(){
super(NotificationsIntentService.class.getSimpleName());
}
#Override
protected void onHandleIntent(Intent intent) {
Log.d(TAG, "onHandleIntent: ");
Intent extraIntent = intent.getParcelableExtra(EXTRA_NOTIF);
extraIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(extraIntent);
NotificationWakefulBroadcastReceiver.completeWakefulIntent(intent);
}
#Override
public void onDestroy() {
super.onDestroy();
Log.d(TAG, "onDestroy: ");
}
}
I have managed to find a solution for my problem. I am now using a Messenger for message based cross process communication between intent service & triggered activity.
I am passing a handler - alarmServiceHandler, from intent service to activity through a messenger.
Handler alarmServiceHandler = new Handler(){
#Override
public void handleMessage(Message msg) {
if(msg.arg1 == 1) {
completedTriggeredActivity = true;
}
}
};
Inside onHandleIntent(), I am passing handler through Messenger object in intent's extra data.
Messenger alarmServiceMessenger = new Messenger(alarmServiceHandler);
Intent activityIntent = new Intent(this, TriggeredActivity.class);
activityIntent.putExtra("AlarmServiceMessenger", alarmServiceMessenger);
activityIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(activityIntent);
while(!completedTriggeredActivity){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
WakefulBCastReceiver.completeWakefulIntent(intent);
In TriggeredActivity, I am retrieving messenger in Dismiss button's OnClickListener, just before calling finish() on the activity. And sending back a message to AlarmService with arg = 1, implying end of processing in triggered activity.
buttonDismiss.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Messenger alarmServiceMessenger = getIntent().getParcelableExtra("AlarmServiceMessenger");
Message alarmServiceMessage = Message.obtain();
alarmServiceMessage.arg1 = 1;
try {
alarmServiceMessenger.send(alarmServiceMessage);
} catch (RemoteException e) {
e.printStackTrace();
}
finish();
}
After starting triggered activity, I am putting AlarmService in sleep mode till boolean variable completedTriggeredActivity has not been set to true in handleMessage(). Once true, it means triggered activity has finished & now I can proceed with releasing wake lock.
I would be glad to receive comments about my approach & any suggestions towards a better solution to my problem, than the one I have deviced.
I have read every question there is about Android, AlarmManager and cancelling.
I currently use an Activity starting a receiver through:
long msInterval = 1;
Intent intent = new Intent(this, Updater.class);
intent.setAction("theAction");
PendingIntent pendingIntent = PendingIntent.getBroadcast(this, 12, intent, 0);
Updater.origin = pendingIntent;
AlarmManager alarmManager = (AlarmManager) getSystemService(ALARM_SERVICE);
alarmManager.setRepeating(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + (msInterval), msInterval, pendingIntent);
This starts the receiver Updater one millisecond after this code was called, with request code 12 (chosen arbitrarily, using 0 produces the same incorrect behaviour). It also sets the origin of Updater to the currently scheduled PendingIntent, which is later used to cancel the alarm.
Updater looks like this:
public class Updater extends BroadcastReceiver {
public static int flaggedClose = 0;
public static PendingIntent origin;
#Override
public void onReceive(Context context, Intent intent) {
// Do some work
Log.w("running", "run");
if (Updater.flaggedClose != 0) {
if(flaggedClose == 1) {
Toast.makeText(context, "Finished!", Toast.LENGTH_SHORT).show();
}
flaggedClose++; // Only show Toast once
Log.w("running", "close");
origin.cancel();
AlarmManager alarms = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
alarms.cancel(origin);
}
}
}
What it does at the moment is just to log the message "run", which is done ~1000 times/s. When the Activity's onStop() is called, Updater.flaggedClose is set to 1. I can be see this in Logcat since it starts printing the log warning "close". However, the alarm is still on, so every other logged message is "run" and every other is "close". In best case, the alarm is closed after a few seconds. Worst case I need to restart the phone. In the description of AlarmManager, it specifically states that close closes "Any alarm, of any type, whose Intent matches this one (as defined by filterEquals(Intent)), will be canceled". Why are there still alarms being triggered?
As ci_ mentioned in the comments, it is possible that "those 100 "extra" alarms already triggered before the cancel happens". For anyone else who has the same problem, here is a solution. I tested the AlarmManager and it seems to work best if you have a delay of at least 200 ms. for a lower delay use a Handler. Example from the question using Handler:
public class MainActivity extends Activity {
private boolean pressed = false;
private boolean done = false;
private Handler worker;
private Runnable method;
long msInterval = 1;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
worker = new Handler();
method = getMethod();
Button toggle = (Button)(findViewById(R.id.toggle));
toggle.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
if(!pressed) {
worker.post(method);
pressed = true;
} else {
done = true;
}
}
});
}
#Override
protected void onDestroy() {
super.onDestroy();
done = true;
}
private Runnable getMethod() {
return new Runnable() {
public void run() {
Log.w("running", "run");
if (done) {
Toast.makeText(getApplicationContext(), "Finished!", Toast.LENGTH_SHORT).show();
Log.w("running", "close");
} else {
worker.postDelayed(method, msInterval);
}
}
};
}
}
On first button press the handler starts the runnable, and on each call the runnable calls itself. On second button press condition done is set to true, thus the runnable finishes after one (cleanup) round.
I'd like to send http requests every N seconds.
The response should be shown is some textViews.
I've used timer. I guess a simple loop is not a good way.
I got error that
"Can't create handler inside thread
that has not called Looper.prepare()"
My test Async requests in main activity (not in timer thread) work okay,
and I can see responses in textView.
My code is below:
private void runTimer() {
MyTimerTask myTask = new MyTimerTask();
Timer myTimer = new Timer();
myTimer.schedule(myTask, 3000, 1500);
}
class MyTimerTask extends TimerTask {
public void run() {
asyncGetRequest();
}
}
private void asyncGetRequest(){
new DownloadWebPageTask().execute("http://www.google.com");
}
....
//this method is called automatically after receiving http response
#Override
protected void onPostExecute(String result) {
someTextView.setText("some text");
}
Thanks!!!
###########################
UPDATED!!! Now it works!!!!
###########################
###########################
UPDATED!!! Now it works!!!!
###########################
I tried different examples of AlarmManagers.
They don't work.
But this one works (answer number 4 there)
Alarm Manager Example
My code to get HTTP responses periodically is below.
It works!
But it works only once.
(even if I comment the line with
context.unregisterReceiver( this )
So I run "runAlarm()" after getting HTTP response.
So it is recursive performance.
Will I have stack overflow at least?
Any comments, please?
Thanks!
public void SetAlarm()
{
BroadcastReceiver receiver = new BroadcastReceiver() {
#Override public void onReceive( Context context, Intent _ )
{
asyncGetRequest();
Toast.makeText(context, "Alarm !!!!!!!!!!", Toast.LENGTH_LONG).show();
context.unregisterReceiver( this ); // this == BroadcastReceiver, not Activity
}
};
this.registerReceiver( receiver, new IntentFilter("com.blah.blah.somemessage") );
PendingIntent pintent = PendingIntent.getBroadcast( this, 0, new Intent("com.blah.blah.somemessage"), 0 );
AlarmManager manager = (AlarmManager)(this.getSystemService( Context.ALARM_SERVICE ));
// set alarm to fire 5 sec (1000*5) from now (SystemClock.elapsedRealtime())
manager.set( AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime() + 1000*5, pintent );
}
private void runAlarm() {
SetAlarm();
}
#Override
protected void onPostExecute(String result) {
showMyHttpResponseSomewhere();
runAlarm();
}
And how should I replace this bla-bla-bla?
Not understood the purpose of this line
this.registerReceiver( receiver, new IntentFilter("com.blah.blah.somemessage") );
You can't do network request in Main UI thread. Try using AsyncTask instead.
As per your update to question:
private final static int INTERVAL = 1000 * 60 * 1; //interval is 1 minute to repeat
Handler mHandler;
Runnable mHandlerTask = new Runnable()
{
#Override
public void run() {
//call your asynctask i.e. asyncTask.execute();
mHandler.postDelayed(mHandlerTask, INTERVAL);
}
};
use the following to stop it
void stopRepeatingTask()
{
mHandler.removeCallbacks(mHandlerTask);
}
use the following to restart it
void startRepeatingTask()
{
mHandlerTask.run();
}
I don't know if this is resolved, but Activity.runOnUiThread may work for you. And I can't figure out to link properly but that's a link to the documentation. :-)
enter link description here
This question already has answers here:
How to stop displaying message from Toast when Application is closed?
(5 answers)
Closed 9 years ago.
Assume that you have a Button that whenever you click on it, it displays a Toast with the message "Hello".
If you decide to click on that Button 20 times repeatedly, the Toasts will be displayed asynchronously, waiting each one its turn. However, this is not what I really want.
What I want is the following:
Whenever I press the Button, I want it to cancel the previous displayed Toast and display the actual one. So that when I close the app, no Toasts will be displayed if ever the user decides to mess with the app by clicking on the Button 100 times within a very small period.
You'll need to declare your Toast at a class level, and then call toast.cancel() before constructing a new Toast object and showing it.
public class XYZ extends Activity {
Toast mToast;
public void onCreate(Bundle b) {
.....
button.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
if(mToast != null)
mToast.cancel();
mToast = Toast.makeText.....;
}
});
}
}
Here' another solution. If all you want is to prevent multiple toasts from being displayed for fast clicks then a combination of AlarmManager and a PendingIntent should work too. Now bear in mind, I haven't tested this and haven't checked if it compiles.
AlarmManager mAlarm;
PendingIntent mPendingIntent;
//toast delay for a second
int toastDelay = 1000;
#Override
public void onCreate (Bundle savedInstanceState) {
Intent i = new Intent(context, MySimpleBroadcastReceiver.class);
//optionally set an action
i.setAction("show:toast");
mPendingIntent = PendingIntent.getBroadcast(context, 0, i, PendingIntent.FLAG_CANCEL_CURRENT);
mAlarm = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
}
public void onClick(View v) {
//set the alarm to trigger 1 second from current time
mAlarm.set(AlarmManager.RTC_WAKEUP, (System.currentTimeMillis() + toastDelay), mPendingIntent);
}
#Override
protected void onDestroy () {
if (mAlarm != null) {
mAlarm.cancel(mPendingIntent);
}
mAlarm = null;
mPendingIntent = null;
}
Create the broadcast receiver and remember to add it to your AndroidManifest.xml:
public class MySimpleBroadcastReceiver extends BroadcastReceiver {
#Override
public void onReceive (Context context, Intent intent) {
//optionally check the action that triggered the broadcast..useful if you want to use this broadcast for other actions
if (intent.getAction().equals("show:toast")) {
Toast.makeText(context, "Hello", Toast.LENGTH_SHORT).show();
}
}
}
You can read about PendingIntent.FLAG_CANCEL_CURRENT.
I was searching over the internet for last 2 days but I couldn't find any tutorial helpful. I have created a service and I am sending a notification in status bar when the service starts. I want that service to stop after showing the notification and start it again after 5 minutes. Please let me know if it is possible and provide me some helpful tutorials if you have any. I heard of TimerTask and AlarmManager and I tried to use them as well but I wasn't able to get the desired result.
EDIT: I need the service to be started every 5 minutes even if my application is not running.
You do not want to use a TimerTask since this depends on your application running continuously. An AlarmManager implementation makes it safe for your application to be killed between executions.
Stating that you tried to use AlarmManager but did not get the desired result is not a helpful statement, in that it tells no one how to help you to get it right. It would be much more useful to express what happened.
http://web.archive.org/web/20170713001201/http://code4reference.com/2012/07/tutorial-on-android-alarmmanager/ contains what appears to be a useful tutorial on AlarmManager. Here are the salient points:
1) Your alarm will cause an Intent to fire when it expires. It's up to you to decide what kind of Intent and how it should be implemented. The link I provided has a complete example based on a BroadcastReceiver.
2) You can install your alarm with an example such as:
public void setOnetimeTimer(Context context) {
AlarmManager am=(AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
Intent intent = new Intent(context, AlarmManagerBroadcastReceiver.class);
intent.putExtra(ONE_TIME, Boolean.TRUE);
PendingIntent pi = PendingIntent.getBroadcast(context, 0, intent, 0);
am.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + (1000 * 60 * 5), pi);
}
Below I have provided three files, MainActivity.java for start service, Second file MyService.java providing service for 5 Minute and Third is manifest file.
MainActivity.java
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
startService(new Intent(this, MyService.class)); //start service which is MyService.java
}
}
MyService.java
public class MyService extends Service {
public static final int notify = 300000; //interval between two services(Here Service run every 5 Minute)
private Handler mHandler = new Handler(); //run on another Thread to avoid crash
private Timer mTimer = null; //timer handling
#Override
public IBinder onBind(Intent intent) {
throw new UnsupportedOperationException("Not yet implemented");
}
#Override
public void onCreate() {
if (mTimer != null) // Cancel if already existed
mTimer.cancel();
else
mTimer = new Timer(); //recreate new
mTimer.scheduleAtFixedRate(new TimeDisplay(), 0, notify); //Schedule task
}
#Override
public void onDestroy() {
super.onDestroy();
mTimer.cancel(); //For Cancel Timer
Toast.makeText(this, "Service is Destroyed", Toast.LENGTH_SHORT).show();
}
//class TimeDisplay for handling task
class TimeDisplay extends TimerTask {
#Override
public void run() {
// run on another thread
mHandler.post(new Runnable() {
#Override
public void run() {
// display toast
Toast.makeText(MyService.this, "Service is running", Toast.LENGTH_SHORT).show();
}
});
}
}
}
AndroidManifest.xml
<service android:name=".MyService" android:enabled="true" android:exported="true"></service>
Create a Timer object and give it a TimerTask that performs the code you'd like to perform.
Timer timer = new Timer ();
TimerTask hourlyTask = new TimerTask () {
#Override
public void run () {
// your code here...
}
};
// schedule the task to run starting now and then every hour...
timer.schedule (hourlyTask, 0l, 1000*60*60); // 1000*10*60 every 10 minut
The advantage of using a Timer object is that it can handle multiple TimerTask objects, each with their own timing, delay, etc. You can also start and stop the timers as long as you hold on to the Timer object by declaring it as a class variable or something.