I have a background service which sets a repeating alarm, does its task and stops itself. Then when the alarm wakes up it starts the service again. If the program crashes the alarm is still around and wakes up the alarm broadcastreceiver. Is there any way to cancel the alarm on a crash - I suppose I might be able to cancel the alarm from any caught exceptions but what about other causes?
Or when the alarm Broadcast receiver is triggered is there any way to tell if the program that set it has crashed?
The activity lifecycle can detect a clean finish from an "unexpected" termination through use of the isFinishing() function in the onDestroy() callback. You could add this to each activity in your app:
protected void onDestroy () {
boolean crashedOnLastClose = false;
if (!isFinishing()) {
// The application is being destroyed by the O/S
crashedOnLastClose = true;
// Store the result somewhere for the BroadcastReceiver to check later
SharedPreferences settings = getSharedPreferences(PREFS_NAME, 0);
SharedPreferences.Editor editor = settings.edit();
editor.putBoolean("crashedOnLastClose", crashedOnLastClose);
editor.commit();
}
}
Then in your BroadcastReceiver, pull the result back from the SharedPreferences framework (or wherever you decide to store it), and cancel the action if the value is true:
public class MyAlarmReceiver extends BroadcastReceiver {
public void onReceive(Context context, Intent intent) {
SharedPreferences settings = getSharedPreferences(PREFS_NAME, 0);
boolean crashedOnLastClose = settings.getBoolean("crashedOnLastClose", false);
if (!crashedOnLastClose) {
// No crash on last run, handle the alarm
// ...
}
}
}
Remember to reset it back to false on next launch though! Something like this should do:
protected void onCreate (Bundle savedInstanceState) {
// Make sure this always resets to FALSE on launch
SharedPreferences settings = getSharedPreferences(PREFS_NAME, 0);
SharedPreferences.Editor editor = settings.edit();
editor.putBoolean("crashedOnLastClose", false);
editor.commit();
}
This should capture your crash events; but remember it will also detect other times when your app is force-closed - such as if the memory is low on the device. To distinguish between these kinds of events you would need to inspect the system status as well as isFinishing().
EDIT - Services
Your Service has a separate lifecycle, you have to treat it almost like a separate app. As you've noticed, there is no "isFinishing()" here, but Services are actually quite simple since they are only terminated cleanly by your code, which you can trap pretty easily.
In your Service, add a new boolean (perhaps called "isFinishing") and set it to true when your service is finished with. Then override onDestroy() as well and add a check similar to the one I described for your activities:
protected void onDestroy () {
// Store the result somewhere for the BroadcastReceiver to check later
SharedPreferences settings = getSharedPreferences(PREFS_NAME, 0);
SharedPreferences.Editor editor = settings.edit();
editor.putBoolean("crashedOnLastClose", !isFinishing);
editor.commit();
}
The broadcast receiver will then use the same code we added earlier to detect if either the service or an activity crashed.
Related
Currently when I run my application and if phone rings , phone get preference and my application is killed. Is there any way either my application takes a preference i.e. let phone call to hit voice mail or shift my app to background for short period of time , till user take a call , and bring back to foreground once he complete. thanks
you can do one thing. You can pause your application during the incoming call and after that, resume the application from the same place. I know this is not the exact solution of your issue but somehow, it will reduce your work load. hope this will help.
private class PhoneCallListener extends PhoneStateListener {
private boolean isPhoneCalling = false;
// needed for logging
String TAG = "PhoneCallListener";
#Override
public void onCallStateChanged(int state, String incomingNumber) {
if (TelephonyManager.CALL_STATE_RINGING == state) {
// phone ringing
Log.i(TAG, "RINGING, number: " + incomingNumber);
}
if (TelephonyManager.CALL_STATE_OFFHOOK == state) {
// active
Log.i(TAG, "OFFHOOK");
isPhoneCalling = true;
}
if (TelephonyManager.CALL_STATE_IDLE == state) {
// run when class initial and phone call ended,
// need detect flag from CALL_STATE_OFFHOOK
Log.i(TAG, "IDLE");
if (isPhoneCalling) {
Log.i(TAG, "restart app");
// restart call application
Intent i = getBaseContext().getPackageManager()
.getLaunchIntentForPackage(
getBaseContext().getPackageName());
i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK
| Intent.FLAG_ACTIVITY_CLEAR_TOP
| Intent.FLAG_ACTIVITY_SINGLE_TOP);
startActivity(i);
isPhoneCalling = false;
}
}
}
}
and also add this permission to manifest.xml file
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
I had come across a similar issue, resolved this by overriding onPause() and onResume() methods, save all the required variables in onPause() and restore these in onResume().
#Override
protected void onResume(){
super.onResume();
load();
}
#Override
protected void onPause(){
super.onPause();
save();
}
private void save() {
SharedPreferences sharedPreferences = getPreferences(Context.MODE_PRIVATE);
SharedPreferences.Editor editor = sharedPreferences.edit();
editor.putString("DeviceName", deviceName);
editor.putString("ConnectOption", connectOption.toString());
editor.commit();
}
private void load() {
SharedPreferences sharedPreferences = getPreferences(Context.MODE_PRIVATE);
deviceName = sharedPreferences.getString("DeviceName","");
String connectop = sharedPreferences.getString("ConnectOption","USB"); //You could provide a default value here
}
I think this is the default feature of android that any application
go inactive if incoming call is in active. We cannot change this.
While the user is in the phone call, though, they can change to another
app simply by pressing the home button and starting another app from
the home screen, or by double-pressing the home button and switching to another app, including yours.
I have an application where user will choose whether he wants Vibrate or Silent mode. And from the Main Activity I am passing the data as part of Intent to the service (the service is always running).
In the onStartCommand method of service, I get the data for first time and everything works fine. But when I exit the application, after some time may be the service's onStartCommand method is again invoked with no data in the Intent (may be Launcher or android OS is doing it).
Since I am setting a local String variable with data from Intent, I get Null Exception when the onStartCommand method is invoked by OS.
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
if(mode == null) {
mode = (String) intent.getExtras().get("mode");
}
return super.onStartCommand(intent, flags, startId);
}
Now my problem is how to get the data from activity for the first time and then refer to it in various member methods of service class.
I have tried making the mode variable as static but same issue. Looks as if OS will unload service class at its discretion and then load it back and invoke onStartCommand with plain Intent.
You must use a SharedPreference to save the state of the variable Vibrate or Silent mode. Here a possible solution:
SharedPreferences preferences = getApplicationContext()
.getSharedPreferences("preferences_name", Context.MODE_PRIVATE);
SharedPreferences.Editor editor = preferences.edit();
editor.putString("mode", "silent"); // Silent Mode
// editor.putString("mode", "vibrate"); //Vibrate Mode
editor.commit();
I have an activity with a checkbox: if the chekbox is unchecked then stop the service. this is a snippet of my activity code:
Intent serviceIntent = new Intent();
serviceIntent.setAction("com.android.savebattery.SaveBatteryService");
if (*unchecked*){
serviceIntent.putExtra("user_stop", true);
stopService(serviceIntent);
when I stop the service I pass a parameter "user_stop" to say at the service that has been a user to stop it and not the system (for low memory).
now I have to read the variable "user_stop" in void onDestroy of my service:
public void onDestroy() {
super.onDestroy();
Intent recievedIntent = getIntent();
boolean userStop= recievedIntent.getBooleanExtra("user_stop");
if (userStop) {
*** notification code ****
but it doesn't work! I can't use getIntent() in onDestroy!
any suggestion?
thanks
Simone
I see two ways of doing this:
Using Shared Preferences.
Using local broadcasts.
The first approach is an easy and straightforward way. But it is not very flexible. Basically you do:
a. Set "user stop" shared preference to true.
b. Stop service
c. In you service in onDestroy check what is the value of "user stop" preference.
The other approach is a better way but requires more code.
a. Define a string constant in you service class:
final public static string USER_STOP_SERVICE_REQUEST = "USER_STOP_SERVICE".
b. Create an inner class BroadcastReceiver class:
public class UserStopServiceReceiver extends BroadcastReceiver
{
#Override
public void onReceive(Context context, Intent intent)
{
//code that handles user specific way of stopping service
}
}
c. Register this receiver in onCreate or onStart method:
registerReceiver(new UserStopServiceReceiver(), newIntentFilter(USER_STOP_SERVICE_REQUEST));
d. From any place you want to stop your service:
context.sendBroadcast(new Intent(USER_STOP_SERVICE_REQUEST));
Note that you can pass any custom arguments through Intent using this approach.
I don't think relying on onDestroy() is a good thing. There are couple of approaches you can take.
suggest to bind the service so that you could write your userstop notification in onUnbind http://developer.android.com/guide/topics/fundamentals.html#servlife
Other option (not sure if this works) is to post your variable to SharedPreferences and obtain it from onDestroy(). [You need to check if this works in debug mode or LogCat messages.
I have the same setup in my app using CheckBoxPreference in my activity and getSharedPreference in service class's onCreate. Checking the checkbox starts the service. Unchecking stops the service.
Listen and handle preference click in my Activity:
getPreferenceManager().findPreference("prefEnable").setOnPreferenceClickListener(new OnPreferenceClickListener()
{
// not used: Intent intent = new Intent(this, MyService.class);
#Override
public boolean onPreferenceClick(Preference preference) {
CheckBoxPreference cb = (CheckBoxPreference) preference;
if (cb.isChecked()) {
Log.d(TAG, "User enabled service");
// code to start service
} else {
Log.d(TAG, "User disabled service");
// code to stop service
}
return true;
}
});
Get my "prefEnable" preference within my service onCreate:
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this);
Boolean isEnabled = preferences.getBoolean("prefEnable", false);
Perhaps in your onDestroy, you can re-purpose this and say:
if (isEnabled==false) {
*** notification code ***
// You can pass the parameter in this simple way:-
Intent serviceIntent = new Intent(this,ListenLocationService.class);
serviceIntent.putExtra("From", "Main");
startService(serviceIntent);
//and get the parameter in onStart method of your service class
#Override
public void onStart(Intent intent, int startId) {
super.onStart(intent, startId);
Bundle extras = intent.getExtras();
if(extras == null)
Log.d("Service","null");
else
{
Log.d("Service","not null");
String from = (String) extras.get("From");
if(from.equalsIgnoreCase("Main"))
StartListenLocation();
}
}
Enjoy :)
I am setting an alarm from my app, and when rebooting, I see I need to reset the alarm since it does not survive reboots. I created a broadcast receiver to receive BOOT_COMPLETED and this works so my manifest must be correct.
When I try this line of code below I get in trouble. I need to retrieve the time that I need to set the alarm to but it looks like I cannot access the prefs of my app (called S) because my app has never started. NullPointerException :
if ( S.prefs.getBoolean(S.SCHEDULEDSTATUS, false) == true ) { }
I suppose it should be obvious that I cannot read a public static final of an activity that has not been created.
Do I have to store my alarm time in a file or am I missing something here?
You have to access it via the context you get in your reciever:
public void onReceive(Context con, Intent intent) {
final SharedPreferences settings = con.getSharedPreferences(PREFS, 0);
boolean boolValue = settings.getBoolean(BOOL, false);
}
My android application requires a password to be entered in the first activity. I want to be able to automatically send the application back to the password entry screen after the application has been idle for a fixed amount of time.
The application has multiple activities, but I would like the timeout to be global for all activities. So, it wouldn't be sufficient to create a timer thread in the onPause() method of an Activity.
I'm not sure what the best definition for the application being idle is, but no activities being active would be sufficient.
I know another answer is accepted already, but I came across this working on a similar problem and think I'm going to try an alternate much simpler approach that I figured I may as well document if anyone else wants to try to go down the same path.enter code here
The general idea is just to track the system clock time in a SharedPreference whenever any Activity pauses - sounds simple enough, but alas, there's a security hole if that's all you use, since that clock resets on reboot. To work around that:
Have an Application subclass or shared static singleton class with a global unlocked-since-boot state (initially false). This value should live as long as your Application's process.
Save the system time (realtime since boot) in every relevant Activity's onPause into a SharedPreference if the current app state is unlocked.
If the appwide unlocked-since-boot state is false (clean app start - either the app or the phone restarted), show the lock screen. Otherwise, check the SharedPreference's value at the lockable activity's onResume; if it's nonexistent or greater than the SharedPreference value + the timeout interval, also show the lock screen.
When the app is unlocked, set the appwide unlocked-since-boot state to true.
Besides the timeout, this approach will also automatically lock your app if your app is killed and restarts or if your phone restarts, but I don't think that's an especially bad problem for most apps. It's a little over-safe and may lock unecessarily on users who task switch a lot, but I think it's a worthwhile tradeoff for reduced code and complexity by a total removal of any background process / wakelock concerns (no services, alarms, or receivers necessary).
To work around process-killing locking the app regardless of time, instead of sharing an appwide singleton for unlocked-since-boot, you could use a SharedPreference and register a listener for the system boot broadcast intent to set that Preference to false. That re-adds some of the complexity of the initial solution with the benefit being a little more convenience in the case that the app's process is killed while backgrounded within the timeout interval, although for most apps it's probably overkill.
I dealt with this by using the AlarmManager to schedule and cancel timeout action.
Then in the onPause() event of all of my activites, I schedule the alarm. In the onResume() event of all of my activities, I check to see if the alarm goes off. If the alarm went off, I shutdown my app. If the alarm hasn't gone off yet I cancel it.
I created Timeout.java to manage my alarms. When the alarm goes off a intent is fired:
public class Timeout {
private static final int REQUEST_ID = 0;
private static final long DEFAULT_TIMEOUT = 5 * 60 * 1000; // 5 minutes
private static PendingIntent buildIntent(Context ctx) {
Intent intent = new Intent(Intents.TIMEOUT);
PendingIntent sender = PendingIntent.getBroadcast(ctx, REQUEST_ID, intent, PendingIntent.FLAG_CANCEL_CURRENT);
return sender;
}
public static void start(Context ctx) {
ctx.startService(new Intent(ctx, TimeoutService.class));
long triggerTime = System.currentTimeMillis() + DEFAULT_TIMEOUT;
AlarmManager am = (AlarmManager) ctx.getSystemService(Context.ALARM_SERVICE);
am.set(AlarmManager.RTC, triggerTime, buildIntent(ctx));
}
public static void cancel(Context ctx) {
AlarmManager am = (AlarmManager) ctx.getSystemService(Context.ALARM_SERVICE);
am.cancel(buildIntent(ctx));
ctx.startService(new Intent(ctx, TimeoutService.class));
}
}
Then, I created a service to capture the intent generated by the alarm. It sets some global state in my instance of the application class to indicate that the app should lock:
public class TimeoutService extends Service {
private BroadcastReceiver mIntentReceiver;
#Override
public void onCreate() {
super.onCreate();
mIntentReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if ( action.equals(Intents.TIMEOUT) ) {
timeout(context);
}
}
};
IntentFilter filter = new IntentFilter();
filter.addAction(Intents.TIMEOUT);
registerReceiver(mIntentReceiver, filter);
}
private void timeout(Context context) {
App.setShutdown();
NotificationManager nm = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
nm.cancelAll();
}
#Override
public void onDestroy() {
super.onDestroy();
unregisterReceiver(mIntentReceiver);
}
public class TimeoutBinder extends Binder {
public TimeoutService getService() {
return TimeoutService.this;
}
}
private final IBinder mBinder = new TimeoutBinder();
#Override
public IBinder onBind(Intent intent) {
return mBinder;
}
}
Finally, I created a subclass of Activity that all of my app's activities subclass from to manage locking and unlocking:
public class LockingActivity extends Activity {
#Override
protected void onPause() {
super.onPause();
Timeout.start(this);
}
#Override
protected void onResume() {
super.onResume();
Timeout.cancel(this);
checkShutdown();
}
private void checkShutdown() {
if ( App.isShutdown() ) {
finish();
}
}
}
Using onPause and onResume to start and stop the timeout gives me the following semantics. As long as one of my application's activities is active, the timeout clock is not running. Since I used an Alarm type of AlarmManager.RTC, whenever the phone goes to sleep the timeout clock runs. If the timeout happens while the phone is asleep, then my service will pick up the timeout as soon as the phone wakes up. Additionally, the clock runs when any other activity is open.
For a more detailed version of these, you can see how I actually implemented them in my application https://github.com/bpellin/keepassdroid
Check out how OpenIntents Safe implements this functionality.
This has been a really helpful post for me. To back the concept given by #Yoni Samlan . I have implemented it this way
public void pause() {
// Record timeout time in case timeout service is killed
long time = System.currentTimeMillis();
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this);
SharedPreferences.Editor edit = preferences.edit();
edit.putLong("Timeout_key", time);// start recording the current time as soon as app is asleep
edit.apply();
}
public void resume() {
// Check whether the timeout has expired
long cur_time = System.currentTimeMillis();
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this);
long timeout_start = preferences.getLong("Timeout_key", -1);
// The timeout never started
if (timeout_start == -1) {
return;
}
long timeout;
try {
//timeout = Long.parseLong(sTimeout);
timeout=idle_delay;
} catch (NumberFormatException e) {
timeout = 60000;
}
// We are set to never timeout
if (timeout == -1) {
return;
}
if (idle){
long diff = cur_time - timeout_start;
if (diff >= timeout) {
//Toast.makeText(act, "We have timed out", Toast.LENGTH_LONG).show();
showLockDialog();
}
}
}
Call pause method from onPause and resume method from onResume.