Apparently, the AlarmManager is the cause for the application to crash. When excetuting the method setExactAndAllowWhileIdle the app stops working.
I'm working on an app to collect location of users and I'm using the AlarmManager to schedule when to collect the location to send through REST. It works fine in previous Android versions, but when tested on Android 8 (on an LG K12 to be more specific) it had this crashing problem. I tried putting "try-catches" throughout the code but none of them triggered.
The app itself checks constantly if the user is moving, and only if it moves a certain distance it sends the location. I've noticed that if I don't move, the app doesn't crash.
public static void startInMiliSeconds(Context ctx, String action, AlarmManager manager, long timeInMiliseconds) {
Intent intent = new Intent(action);
PendingIntent pending = PendingIntent.getBroadcast(ctx, 0, intent, 0);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
manager.setExactAndAllowWhileIdle(AlarmManager.RTC, timeInMiliseconds, pending);
} else {
manager.setExact(AlarmManager.RTC, timeInMiliseconds, pending);
}
}
The app itself is sending the current location at the moment you log in and synchronize, but if the telephone moves just a little bit from location, the app crashes.
StackTrace
Related
I am using Firebase (FCM) to show Push Notifications to the user and I am running into a weird problem.
The code I have works for the following scenarios (using FirebaseMessagingService):
App in foreground - Receiving data in onReceive() and showing a popup inside app.
App in background - Receiving data in onReceive() and showing a notification for the user. If this is clicked the app will be brought back to front. The intent from this is received in LauncherActivity followed by a finish() call which takes me to whatever activity I already had open.
App completely closed - same as background. App will be started and intent will be handled in LauncherActivity before calling finish() on that.
And here is where it gets interesting:
App completely closed -> open it through notification (intent received in LauncherActivity) -> put the app in background and send another notification -> when this notification is clicked the LauncherActivity is completely ignored (onCreate is no longer called) and I get taken straight to whatever activity I already had. The intent here has no extras or categories.
Why is LauncherActivity being bypassed in this specific case? Keep in mind that this works fine if the app was initially started normally (not by clicking on a notification)
Intent mainIntent = getPackageManager().getLaunchIntentForPackage(getPackageName());
if (mainIntent != null) {
mainIntent.addCategory(NOTIFICATION_CATEGORY);
mainIntent.putExtra(.........);
}
PendingIntent pendingMainIntent = PendingIntent.getActivity(this, SERVICE_NOTIFICATION_ID, mainIntent, PendingIntent.FLAG_UPDATE_CURRENT);
NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(context, context.getString(R.string.default_notification_channel_id));
notificationBuilder.setContentIntent(pendingMainIntent);
//.....icon, color, pririty, autoCancel, setDefaults, setWhen, setShowWhen, contentText, setStyle
NotificationManager notificationManager = (NotificationManager) this.getSystemService(Context.NOTIFICATION_SERVICE);
if (notificationManager != null) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationChannel channel = new NotificationChannel(
getString(R.string.default_notification_channel_id),
getString(R.string.default_notification_channel),
NotificationManager.IMPORTANCE_HIGH
);
notificationManager.createNotificationChannel(channel);
notificationBuilder.setChannelId(getString(R.string.default_notification_channel_id));
}
notificationManager.notify(SERVICE_NOTIFICATION_ID, notificationBuilder.build());
}
I'd appreciate any ideas. Thank you.
When you launch an app for the first time, Android remembers the Intent that was used to launch it. Normally, when you launch an app from the HOME screen, this is an Intent that contains ACTION=MAIN and CATEGORY=LAUNCHER. If your app then goes to the background (for whatever reason), and the user later taps the icon on the HOME screen, the same launch Intent is used. Android matches this against the Intent used to launch the app for the first time, and if these match, Android doesn't launch a new Activity, it just brings the task containing the app from the background to the foreground in whatever state it was in when it got moved to the background. Under normal circumstances, this is exactly the behaviour that you want (and that the user expects).
However, when the app is launched for the first time from a Notification, this can mess things up. In your case, this is what you are seeing. You launch the app from a Notification and Android remembers the Intent used (from the Notification), when you later launch the app (again from a Notification), android matches the Intent in the Notification with the Intent used to launch the app for the first time, and thinks you want to bring the existing app task from the background to the foreground.
There are several ways to deal with this, depending on the behaviour that you want to have. The best thing to do is probably not to launch your root Activity (the one with ACTION=MAIN and CATEGORY=LAUNCHER) from the Notification. Instead launch a different Activity and have that Activity determine what it should do next (ie: redirect to the root Activity or something else, depending on the state of your app). You should also set the NO_HISTORY and EXCLUDE_FROM_RECENTS flags on the Intent that you put in the Notification. This will ensure that Android won't remember this Intent as the one that launched the app.
(Before you mark this question duplicate, please note that I'm facing this problem in a specific API Level (API R), the app shows normal expected working behaviour on other API levels below R).
When running the app on Pixed 3a API R
I'm trying out the the Alarm Manager in android, by building a basic Alarm app. I have an AlarmPageActivity that I tried to start from my custom Broadcast Receiver (AlarmReceiver.java)
I have code in my MainActivity where I set the alarm using Alarm Manager. When the alarm goes off, the pending intent goes to the AlarmReceiver class where I try to start an activity using context.startActivity(context, alarmIntent).
The problem: [Before you go there, yes I have the appropriate flags required (Intent.FLAG_ACTIVITY_NEW_TASK)]. When I set a time for the alarm, and keep the app open, the AlarmPageActivity (the activity which is supposed to pop open when the alarm goes off) launches and the app executes normal behaviour. But when I set a time, and close the app (onDestroy()), the code follows through to AlarmReceiver.java (my Broadcast receiver), the ringtone starts playing, but my AlarmPageActivity doesn't show up (In fact, even it's onCreate() method does not execute). Neither does it show any error in the Log/run window. I tried changing everything from pending intent parameters, to adding new activity flags to the intent object, etc. but nothing worked! You can see below in the code that there is no syntax error that should be stopping the code from starting a new activity through broadcast receiver (when the user is outside the app).
I faced this issue for nearly 2 days. I read every article/post on the internet about starting an activity from a broadcast receiver class. But it wouldn't work, and I got so frustrated. Then MIRACULOUSLY, I thought why not try running the app on a different emulator. And guess what? it worked. sigh... I kept thinking I was doing something wrong or that my code was whack because I'm only so beginner-ish in android studio. But little did I know, that my code was correct all that time.
When running the app on Pixed 3a API 26
The app shows normal expected behaviour. Even after onDestroy(), the BroadCast receiver receives and launches the AlarmPageActivity, with the ringtone :)
My Question: This is the same code (I didn't change/add anything else) that was not working on the emulator device running API R, but works now in API 26. Can somebody explain why it wouldn't work? I would really appreciate. Or maybe my code wasn't compatible with API R in some way... who knows? Enlighten me!
MainActivity.java
protected void onCreate(Bundle savedInstanceState) {
...
other code...
...
// Alarm manager code ----------------------------------------
alarmMgr = (AlarmManager) getSystemService(ALARM_SERVICE);
Calendar calendar = Calendar.getInstance();
calendar.setTimeInMillis(System.currentTimeMillis());
calendar.set(Calendar.HOUR_OF_DAY, 0);
calendar.set(Calendar.MINUTE, 56);
calendar.set(Calendar.SECOND, 0);
long startTime = calendar.getTimeInMillis();
Intent alarmIntent = new Intent(this, AlarmReceiver.class);
final PendingIntent alarmPendingIntent = PendingIntent.getBroadcast(this, 0,
alarmIntent, 0);
alarmMgr.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, startTime, alarmPendingIntent);
}
AlarmReceiver.java
public class AlarmReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent){
// TODO: This method is called when the BroadcastReceiver is receiving
// an Intent broadcast.
Log.d("asdfasdf", "BroadcastReceiver (alarmReceiver) activity reached");
Intent alarmIntent = new Intent(context.getApplicationContext(), AlarmPageActivity.class);
alarmIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(alarmIntent);
alarmUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_ALARM);
// ringtone code ...
ringtone.play();
Log.d("asdfasdf", "Reached end of alarmReceiver ");
}
} // mind the wrong indenting here
AlarmPageActivity.java
has the default empty activity code...
And yes, I know I shouldn't run an activity from a Broadcast receiver as it shouldn't do heavy task that takes > 10s and everything, but I just wanted to see how to it would look like before improving on that code later.
I think it not work from android 10 and higher. Please read more detail about Restrictions on starting activities from the background. Try to request permisison SYSTEM_ALERT_WINDOW for this case
it kills background service, to solve your issue you should use foreground service.
my background sticky service is kill on oreo and heigher devices any solution for getting location on background service when Activity is on backound
It's because of Android Oreo behaviour changes for background execution. Suggested alternatives are
1) Foreground service.
Use this if you are ok displaying notification to the user when you are trying to retrieve the location using Location API. This would be reliable and is suggested in the documentation.
Sample app from the documentation :
LocationUpdatesForegroundService project on GitHub
An example of an app that allows the app to continue a user-initiated
action without requesting all-the-time access to background location.
References
https://developer.android.com/training/location/receive-location-updates
2) Work Manager
This approach would be less reliable as you will not be able to control when exactly this would be called but can be used if you don't want to show notification to the user at all.
You would not be able to run background services long running in Oreo as there are behavior changes, now Oreo to optimize system memory, battery etc, it kills background service, to solve your issue you should use foreground service.
Have a look at Background execution limits https://developer.android.com/about/versions/oreo/android-8.0-changes
A suggestion from me, if you can use FCM then go for it, because apps like WeChat, Facebook uses it, to deliver notifications and they don't face any problem.
Alternate solution which I have opted without FCM due to client requirement to run service that updates location in background. Here are steps below:-
You need to start your background service with showing notification in Oreo and above.
Them after that you need to keep in mind that after some time phone enters into Doze mode so you have to tackle that also
In Addition, Battery optimization must be disabled for the application too.
In Some custom ROM you need to manage the Auto-start permission to restart your service if the service is killed by the android.
And the most important part is, if the service is killed by the android system then send a Broadcast Message to the Broadcast receiver to restart your service one again
Hope you will do some more R&D work.
I have shared my experience and the process by which i have done the same to run the service in the background.
you must show notification for ForegroundService
public class ForegroundService extends Service {
public static final String CHANNEL_ID = "ForegroundServiceChannel";
#Override
public void onCreate() {
super.onCreate();
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
String input = intent.getStringExtra("inputExtra");
createNotificationChannel();
Intent notificationIntent = new Intent(this, MainActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(this,
0, notificationIntent, 0);
Notification notification = new NotificationCompat.Builder(this, CHANNEL_ID)
.setContentTitle("Foreground Service")
.setContentText(input)
.setSmallIcon(R.drawable.ic_stat_name)
.setContentIntent(pendingIntent)
.build();
startForeground(1, notification);
//do heavy work on a background thread
//stopSelf();
return START_NOT_STICKY;
}
#Override
public void onDestroy() {
super.onDestroy();
}
#Nullable
#Override
public IBinder onBind(Intent intent) {
return null;
}
}
and add permission
<uses-permissionandroid:name=”android.permission.FOREGROUND_SERVICE” />
I'm trying to develop simple android application which monitors specified url using http client and once defined condition is met it should perform notification actions.
I have one activity which starts separate thread and put reference to it via static value. (When activity is recreated I'm chechking reference for not null to determinate if child thread was already started). In this child thread I have while loop which gets json data from url and parse it.
I've noticed weird behavour (maybe because I'm android dev newbie). Once application is in foreground it works quite fast, when android device goes into sleep mode it doesn't perform requests to often. (maybe some energy safe policy?). What's the most weird is that once I connected phone to computer via usb cable to works fast (even when application is in background and phone has black screen).
Is there any relationship to activating /disactivating applications based on connected charger?
I can't debug it because once I connected cable it works fine, and I can't debug without being connected to computer.
The matter probably is that phone goes to sleep mode when it stops almost all activity and slows down CPU. It is used to save battery. Timers on Handler.postDelayed() for example won't work properly (not called on time).
There's special concept for this matter - for activities that needs to be performed in sleep mode, you need to use AlarmManager, see Scheduling Repeating Alarms
The matter is that your app needs to register with AlarmManager, and then it will receive scheduled events when phone wakes up from sleep mode. Your app needs to get lock with PowerManager to perform activities (in your case it's downloading JSON from network), which you do not want to be interrupted with sleep mode while you're executing them. Consider this example:
public class AlarmManagerBroadcastReceiver extends BroadcastReceiver {
/**
* This method is called when we are waking up by AlarmManager
*/
#Override
public void onReceive(Context context, Intent intent) {
PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
PowerManager.WakeLock wl = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "pulse app");
//Acquire the lock
wl.acquire();
//You can do the processing here.
//Release the lock
wl.release();
}
/**
* Register our app with AlarmManager to start receiving intents from AlarmManager
*/
public static void setAlarm(Context context)
{
int interval = 10; // delay in secs
AlarmManager am=(AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
Intent intent = new Intent(context, AlarmManagerBroadcastReceiver.class);
PendingIntent pi = PendingIntent.getBroadcast(context, 0, intent, 0);
am.setRepeating(AlarmManager.RTC_WAKEUP, System.currentTimeMillis(), interval*1000 , pi);
}
/**
* Unregister the app with AlarmManager, call this to stop receiving intents from AlarmManager
*/
public static void cancelAlarm(Context context)
{
Intent intent = new Intent(context, AlarmManagerBroadcastReceiver.class);
PendingIntent sender = PendingIntent.getBroadcast(context, 0, intent, 0);
AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
alarmManager.cancel(sender);
}
}
This code needs android.permission.WAKE_LOCK permission in Manifest file.
Another post about AlarmManager usage: Android: How to use AlarmManager
And this: prevent mobile from going into sleep mode when app is running
Article at the first link says that it's preferable to use Sync Adapters for such purpose, but I haven't used them myself.
I am working on an Android application in which I have to compare current time , with a time (saved) in a file, though everything is working fine. I have use services and in service i have use THREAD to run the service infinitely, and in addition to this i have also used PARTIAL_WAKE_LOCK to continue service even the device is sleep but the issue is that instead of acquiring PARTIAL_WAKE_LOCK my service runs for 1/2 hours and then again go to sleep. I don't want to acquire FULL_WAKE_LOCK. Is there any one who can guide me what i have to do in order to run this comparison, i.e. my service will run perfectly once the user set the time.
Thank you in advance.
You are doing it the wrong way. To create permanent service you must
declare it as foreground. No other way about it:
myService.startForeground(MY_NOTIFICATION_ID, my_notification);
If your interest with such a service is to periodically perform fast-ending
actions, and if the in between periods are long, you probably want to use
the alarm API and improve your app's battery consumption.
Edit:
To set a foreground service you must supply the system with a notification
object to be displayed at notification bar for as long as the service is in foreground
Why is that? Because foreground services cannot be killed, and Android needs to know
that the user is aware of that fact.
Setting as foreground:
static final int NOTIF_ID = 100;
// Create the FG service intent
Intent intent = new Intent(getApplicationContext(), MyActivity.class); // set notification activity
showTaskIntent.setAction(Intent.ACTION_MAIN);
showTaskIntent.addCategory(Intent.CATEGORY_LAUNCHER);
showTaskIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
PendingIntent pIntent = PendingIntent.getActivity(
getApplicationContext(),
0,
intent,
PendingIntent.FLAG_UPDATE_CURRENT);
Notification notif = new Notification.Builder(getApplicationContext())
.setContentTitle(getString(R.string.app_name))
.setContentText(contentText)
.setSmallIcon(R.drawable.ic_notification)
.setContentIntent(pIntent)
.build();
startForeground(NOTIF_ID, notif);
And reverting to 'stardard' service mode:
stopForeground(true).
Both setting to foreground and reverting to background can be called by either the service itself (e.g. its onCreate() method) or by external code (e.g. the activity that initiated the service). No problems here.