I have following design in my app: I have a one activity that sets repeating alarm that launches receiver that starts my service. Every minute. In my service, I set Start Sticky but once Android decides to kill my service, I can't get it to restart. It's Android 4.4.2. Here is my code:
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
AlarmManager am = (AlarmManager) getSystemService(ALARM_SERVICE);
Intent i1 = new Intent(MainActivity.this, AlarmReceiver.class);
PendingIntent pi = PendingIntent.getBroadcast(MainActivity.this, 0, i1, PendingIntent.FLAG_UPDATE_CURRENT);
am.setRepeating(AlarmManager.RTC_WAKEUP, 0, 60 * 1000, pi);
}
here is the receiver
public void onReceive(Context arg0, Intent arg1) {
// For our recurring task, we'll just display a message
Log.d(Constants.TAG, "Starting Service");
Intent intent = new Intent(arg0, MyLocationService.class);
arg0.startService(intent);
}
and service:
private static PowerManager.WakeLock wl;
private static OkHttpClient client;
#Nullable
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d(Constants.TAG, "Service onStartCommand");
PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
wl = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, Constants.TAG);
wl.acquire();
client = new OkHttpClient();
new GetLocation(MyLocationService.this).execute();
return START_STICKY;
}
You're most likely seeing an interaction with power management and alarms. Starting with API 19 all alarms are inexact by default, so they will be collated with other alarms. Further, once the device has entered into a lower power state, alarms are delivered to BroadcastReceivers and the device guaranteed to stay awake as long as the receiver is executing its onReceive() method. Once it returns from that method (and any other BroadcastReceiver associated with the alarm runs), the device will immediately go to a low power state again. Since your app had previously been killed, the Service is no longer running and will not get CPU time.
The way to resolve this is to use a WakefulReceiver which takes a wakelock when it runs onReceive(), starts your Service for processing. The Service will then release the wakelock when it is done processing. This article will give you a good explanation of this: http://po.st/7UpipA
Note that waking up every minute is going to seriously degrade the battery life of the device, so you should consider backing this off.
Related
I have created an alarm clock app using setAlarmClock. For some people the alarm does not start properly. I know you must disable all kind of energy saving modes on devices otherwise it could by that the alarm does not fire. But I have some rare cases where the alarm starts on time but it has time gaps until it finishes alls steps of onCreate and onPostCreate of my activity.
It could be related with the fact that my wakelock is activated very late. When the Broadcastreceiver is called with an alarm event it starts my main activity. The activity starts a thread which must run at least once. It checks if the alarm should fire. If yes then it creates a wakelock to keep the device awake.
I could try to create the wakelock ealier but I have seen log files where onResume has not been even called, only onStart. There is a gap of 5 minutes between onStart and onResume. So there is no chance to make it early enough.
Is there something wrong with my alarm and wakelock concept?
Is it possible/wise to start the wakelock in the BroadcastReceiver and stop it in the Activity?
public class AlarmManagement extends BroadcastReceiver implements Serializable
{
#Override
public void onReceive (Context context, Intent intent)
{
Intent i = new Intent(context, FullscreenActivity.class);
i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
context.startActivity(i);
}
public void scheduleSystemAlarmApi21(Context context,long alarmTime, PendingIntent pi) {
AlarmManager am =( AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
Intent i2=new Intent(context, FullscreenActivity.class);
PendingIntent pi2=PendingIntent.getActivity(context, 0, i2, 0);
am.setAlarmClock(new AlarmManager.AlarmClockInfo(alarmTime,pi2),pi);
}
public void scheduleSystemAlarm(Context context,int id, long alarmTime) {
Intent i = new Intent(context, AlarmManagement.class);
PendingIntent pi = PendingIntent.getBroadcast(context, id, i, 0);
scheduleSystemAlarmApi21(context,alarmTime,pi);
}
}
public class FullscreenActivity extends AppCompatActivity {
Thread thread;
#Override
protected void onResume() {
super.onResume();
if (threadStopped)
alarmServiceThread();
}
void alarmServiceThread() {
thread = new Thread() {
#Override
public void run() {
...
if (needToStartWakeup(currentTime))
startWakeup();
...
}
}
thread.start();
}
PowerManager powerManager;
PowerManager.WakeLock wake;
public void startWakeup() {
powerManager = ((PowerManager) getSystemService(Context.POWER_SERVICE));
int levelAndFlags=PowerManager.ACQUIRE_CAUSES_WAKEUP;
levelAndFlags |= PowerManager.SCREEN_BRIGHT_WAKE_LOCK;
wake = powerManager.newWakeLock( levelAndFlags , WAKE_LOCK_TAG);
wake.acquire();
}
}
You are trying to start an activity from the receiver in the onReceive method, that is problematic if your app is in the background.
Please read the following documentation on the subject:
https://developer.android.com/guide/components/activities/background-starts
You should present a notification for the user and open the activity on user action.
So I made an app that upon a button click sets up a repeating task using an Alarm Manager.
In on create:
Intent alarmIntent = new Intent(this, AlarmReceiver.class);
servicePendingIntent = PendingIntent.getBroadcast(this, 0, alarmIntent, 0);
On the button click:
alarmManager = (AlarmManager)getSystemService(Context.ALARM_SERVICE);
firingCal= Calendar.getInstance();
firingCal.setTimeInMillis(System.currentTimeMillis());
firingCal.set(Calendar.HOUR_OF_DAY, 1); // At the hour you want to fire the alarm
firingCal.set(Calendar.MINUTE, 47); // alarm minute
firingCal.set(Calendar.SECOND, 0); // and alarm second
long intendedTime = firingCal.getTimeInMillis();
long interval = 1000 * 60 * 1;
alarmManager.setInexactRepeating(AlarmManager.RTC_WAKEUP, intendedTime, interval, servicePendingIntent);
In the AlarmReceiver class:
public void onReceive(Context context, Intent intent) {
Intent myIntent = new Intent(context, WallpaperService.class);
context.startService(myIntent);
Log.d(TAG,"Am apelat serviciul");
context.stopService(myIntent);
}
And in the WallpaperService class I just make a get request and set an wallpaper.
public class WallpaperService extends Service {
String requestLink="";
boolean requestFinished = false;
public final String TAG = "Service";
public static int SERVICE_ID = 1;
#Override
public void onCreate() {
super.onCreate();
Log.d(TAG,"Wallpaper Service started");
Toast.makeText(WallpaperService.this,"Service started",Toast.LENGTH_LONG);
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d(TAG,"In onStartCommand");
taskToBeRepeated();
stopSelf();
return START_STICKY;
}
.....
}
And the behaviour is that when I start the app and I click the button everything works well the first time the Alarm Manager fires ( With the app in the background). The second time the receiver gets triggered I get the error in the tile. To be more specific :
java.lang.RuntimeException: Unable to start receiver com.example.dailywallpaper.AlarmReceiver: java.lang.IllegalStateException: Not allowed to start service Intent { cmp=com.example.dailywallpaper/.WallpaperService }: app is in background uid UidRecord{3e313bf u0a357 RCVR bg:+1m21s273ms idle change:uncached procs:1 seq(0,0,0)}
What seems to be the problem ? And why is working the first time and then it gives the error? How can I fix it ?
you need to read android official documentation about the policy of using background service or alarms in android 8 and above and adapt your app with this limitations.
I suggest you to read this two articles very carefully :
https://developer.android.com/guide/components/services
https://developer.android.com/about/versions/oreo/background
I want to fetch some data from the background in fixed interval of time say 30 minutes. I have implemented the solution using a alarm manager where I call a Service in fixed interval. The process is working fine but the problem I am facing is that it is taking quite few battery power. I want to utilize the battery consumption so that the users don't run away from the application. Alarm is set up like the first part of the code.
AlarmManager am = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
Intent i = new Intent(context, PollingClass.class);
PendingIntent pi = PendingIntent.getService(context, 0, i, 0);
am.cancel(pi);
am.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime() + 60 * 1000, 30 * 60 * 1000, pi);
In the second part Service class is called by the alarm to execute the task.
public class PollingClass extends Service {
private WakeLock mWakeLock;
public PollingClass() {
}
#Override
public IBinder onBind(Intent intent) {
return null;
}
private void handleIntent(Intent intent) {
PowerManager pm = (PowerManager) getSystemService(POWER_SERVICE);
mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "NEW");
if ((mWakeLock != null) && (mWakeLock.isHeld() == false)) {
mWakeLock.acquire();
}
ConnectivityManager cm = (ConnectivityManager) getSystemService(CONNECTIVITY_SERVICE);
if (!cm.getBackgroundDataSetting()) {
stopSelf();
return;
}
//Calling an Async class to fetch the data from the server
stopSelf();
}
#Override
public void onStart(Intent intent, int startId) {
handleIntent(intent);
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
handleIntent(intent);
return START_NOT_STICKY;
}
public void onDestroy() {
super.onDestroy();
mWakeLock.release();
}
}
Thanks in advance.
IntentService is a base class for Services that handle asynchronous requests (expressed as Intents) on demand. Clients send requests through startService(Intent) calls; the service is started as needed, handles each Intent in turn using a worker thread, and stops itself when it runs out of work. IntentService has a background thread, but only to invoke onHandleIntent(). Once onHandleIntent() returns, not only does the thread go away, but the service gets destroyed. so this will help your code to run in different thread when you implement your code in onHandleIntent().
for example:
#Override
protected void onHandleIntent(Intent intent) {
//This is where you will put your volley code or some code that you want to perform in background
}
ThankYou. I hope this was helpful.
I want to make a background running service (independent of an app) which would download weather data from server periodically every day. I already have code to download data from the server and store it in the database.
What I would like to know is, what is the best way to run the service periodically.
You can Create a Android Intent Service :-
public class BackendService extends IntentService {
public BackendService() {
super("BackendService");
}
#Override
protected void onHandleIntent(Intent intent) {
// Your Download code
}
}
Then set a Alarm Receiver to set the interval in which service will be called.
public void backendscheduleAlarm() {
// Construct an intent that will execute the AlarmReceiver
Intent intent = new Intent(getApplicationContext(), BackendAlarm.class);
// Create a PendingIntent to be triggered when the alarm goes off
final PendingIntent pIntent = PendingIntent.getBroadcast(this, BackendAlarm.REQUEST_CODE,
intent, PendingIntent.FLAG_UPDATE_CURRENT);
// Setup periodic alarm every 1 hour
long firstMillis = System.currentTimeMillis(); // first run of alarm is immediate
int intervalMillis = 3000; //3600000; // 60 min
AlarmManager backendalarm = (AlarmManager) this.getSystemService(Context.ALARM_SERVICE);
backendalarm.setInexactRepeating(AlarmManager.RTC_WAKEUP, firstMillis, intervalMillis, pIntent);
}
And Create a Broadcast Receiver class to call that service:
public class BackendAlarm extends BroadcastReceiver {
public static final int REQUEST_CODE = 12345;
// Triggered by the Alarm periodically (starts the service to run task)
#Override
public void onReceive(Context context, Intent intent) {
Intent i = new Intent(context, BackendService.class);
i.putExtra("foo", "bar");
context.startService(i);
} }
read about Android Services which are mainly made for such background work:
http://developer.android.com/guide/components/services.html
all you need is to start the service on a certain time you set.
If I start a service with startService in a Activity I get:
1 processes and 1 service
If I now swipe that Activity away. I.e remove it, I get:
0 processes and 1 service
Why is this? And what is a Process and what is a Service in the Android world?
I use START_STICKY and if I stop the service via Settings, Apps and Running, it is not started again, why?
Update1 some code:
Activity:
startService(new Intent(getApplicationContext(), MyService.class));
Service:
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d(TAG, "Starting service");
return(START_STICKY);
}
what is the definition of process in android world? same as defined at any operating system - your application is "alive" from the system's point of view, it has active memory allocation stack, and may run or not Activities, Services and so on...
I think that you struggling your had "how can it be that running process = 0" but services = 1 not making scenes, and you are right.
the running applications display shown from the settings app is not made only for developers, but also for users, I guess that's why most vendors decided to show active tasks as process. basically, in this display - running process = running task.
most application starts only one task (the main activity with the launcher flag starts automatically in that mode). there will be more tasks only if other activities would start explicitly with that flag.
so, if your app have 2 activities that started at new task mode - you'll see "2 process".
if your app not running at all (your process really not alive) - then you won't see the app in the running apps screen.
Turned out to be a bug in KitKat.
(Sometimes I think getting anything done in Android is a big hassle!)
Android Services: START_STICKY does not work on Kitkat
https://code.google.com/p/android/issues/detail?id=63793
Fix in Service:
#Override
public void onTaskRemoved(Intent rootIntent) {
Intent restartService = new Intent(getApplicationContext(), this.getClass());
restartService.setPackage(getPackageName());
PendingIntent restartServicePI = PendingIntent.getService(
getApplicationContext(), 1, restartService,
PendingIntent.FLAG_ONE_SHOT);
AlarmManager alarmService = (AlarmManager)getApplicationContext().getSystemService(Context.ALARM_SERVICE);
alarmService.set(AlarmManager.ELAPSED_REALTIME, SystemClock.elapsedRealtime() +1000, restartServicePI);
}
The Main problem in your case is ur unable to start the service when app closed,that time android OS will kill the service, If you are not able to restart the service then call a alam manger to start the reciver like this,
Manifest is,
<service
android:name=".BackgroundService"
android:description="#string/app_name"
android:enabled="true"
android:label="Notification" />
<receiver android:name="AlarmReceiver">
<intent-filter>
<action android:name="REFRESH_THIS" />
</intent-filter>
</receiver>
IN Main Activty start alarm manger in this way,
String alarm = Context.ALARM_SERVICE;
AlarmManager am = (AlarmManager) getSystemService(alarm);
Intent intent = new Intent("REFRESH_THIS");
PendingIntent pi = PendingIntent.getBroadcast(this, 123456789, intent, 0);
int type = AlarmManager.RTC_WAKEUP;
long interval = 1000 * 50;
am.setInexactRepeating(type, System.currentTimeMillis(), interval, pi);
this will call reciver and reciver is,
public class AlarmReceiver extends BroadcastReceiver {
Context context;
#Override
public void onReceive(Context context, Intent intent) {
this.context = context;
System.out.println("Alarma Reciver Called");
if (isMyServiceRunning(this.context, BackgroundService.class)) {
System.out.println("alredy running no need to start again");
} else {
Intent background = new Intent(context, BackgroundService.class);
context.startService(background);
}
}
public static boolean isMyServiceRunning(Context context, Class<?> serviceClass) {
ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
List<ActivityManager.RunningServiceInfo> services = activityManager.getRunningServices(Integer.MAX_VALUE);
if (services != null) {
for (int i = 0; i < services.size(); i++) {
if ((serviceClass.getName()).equals(services.get(i).service.getClassName()) && services.get(i).pid != 0) {
return true;
}
}
}
return false;
}
}
And this Alaram reciver calls once when android app is opened and when app is closed.SO the service is like this,
public class BackgroundService extends Service {
private String LOG_TAG = null;
#Override
public void onCreate() {
super.onCreate();
LOG_TAG = "app_name";
Log.i(LOG_TAG, "service created");
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.i(LOG_TAG, "In onStartCommand");
//ur actual code
return START_STICKY;
}
#Override
public IBinder onBind(Intent intent) {
// Wont be called as service is not bound
Log.i(LOG_TAG, "In onBind");
return null;
}
#TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
#Override
public void onTaskRemoved(Intent rootIntent) {
super.onTaskRemoved(rootIntent);
Log.i(LOG_TAG, "In onTaskRemoved");
}
#Override
public void onDestroy() {
super.onDestroy();
Log.i(LOG_TAG, "In onDestroyed");
}
}