I am trying to make a cron that runs every X time on the android system so that it calls a service that I have made.
The features of the cron that I want to do are the following:
- run regardless of whether the Application is running or not (partially done with the OnBootReceiver )
- to run when the device reboots (I have it done, see code below on OnBootReceiver )
- run every X time interval (every 10 minutes for example)
- if there is no connection at the time of executing the service when the cron has been activated, a
Receiver
connection is activated so that when there is a connection the service is executed and then it is deactivatedReceiver
(I have done it see code below in ConnectivityReceiver ).
Some of these features I have already achieved by doing them separately, below I put the code of what I have.
ConnectivityReceiver
public class ConnectivityReceiver extends WakefulBroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(ConnectivityManager.CONNECTIVITY_ACTION)) {
boolean noConnectivity =
intent.getBooleanExtra(ConnectivityManager.EXTRA_NO_CONNECTIVITY, false);
if(!noConnectivity){
ConnectivityManager cm = (ConnectivityManager) context
.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo netInfo = cm.getActiveNetworkInfo();
// only when connected or while connecting...
if (netInfo != null && netInfo.isConnectedOrConnecting()) {
// if we have mobile or wifi connectivity...
if ((netInfo.getType() == ConnectivityManager.TYPE_MOBILE)
|| (netInfo.getType() == ConnectivityManager.TYPE_WIFI)) {
Intent i = new Intent(context, EnvioEstadisticasService.class);
startWakefulService(context, i);
// disable receiver after we started the service
disableReceiver(context);
}
}
}
}
}
/**
* Enables ConnectivityReceiver
*
* @param context
*/
public static void enableReceiver(Context context) {
ComponentName component = new ComponentName(context, ConnectivityReceiver.class);
context.getPackageManager().setComponentEnabledSetting(component,
PackageManager.COMPONENT_ENABLED_STATE_ENABLED, PackageManager.DONT_KILL_APP);
}
/**
* Disables ConnectivityReceiver
*
* @param context
*/
public static void disableReceiver(Context context) {
ComponentName component = new ComponentName(context, ConnectivityReceiver.class);
context.getPackageManager().setComponentEnabledSetting(component,
PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP);
}
}
ShippingStatisticsService
public class EnvioEstadisticasService extends IntentService {
private static EstadisticasDAO daoEst;
public EnvioEstadisticasService() {
super("EnvioEstadisticasService");
}
@Override
protected void onHandleIntent(Intent intent) {
//hago todas las operaciones en envio de estadisticas
// Release the wake lock provided by the WakefulBroadcastReceiver.
ConnectivityReceiver.completeWakefulIntent(intent);
}
}
OnBootReceiver
public class OnBootReceiver extends BroadcastReceiver {
private static final String TAG = OnBootReceiver.class.getSimpleName();
@Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals("android.intent.action.BOOT_COMPLETED")) {
Log.i(TAG, "EnvioEstadisService: entra en el on bootreceiver");
Intent i = new Intent(context, EnvioEstadisticasService.class);
startWakefulService(context, i);
}
}
}
And finally in manifest
<!-- Cron -->
<receiver android:name=".cron.OnBootReceiver">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
<receiver
android:name=".cron.ConnectivityReceiver"
android:enabled="false" >
<intent-filter>
<action android:name="android.net.conn.CONNECTIVITY_CHANGE" />
</intent-filter>
</receiver>
<service android:name=".services.EnvioEstadisticasService"
android:exported="false"
android:enabled="true">
</service>
<!-- -->
I have managed to activate the service when the device is turned on or restarted regardless of the execution of the application, in addition to controlling the connection issue when sending to be able to call another receiver.
Does anyone know how to make it run every X interval regardless of whether the application is running or not?
I have found a solution to my problem fulfilling the points that I wanted the cron to have as described in the question.
Explanation
After researching and looking to use the Timer in the OnBootReceiver as @sioesi advised me, I found that they
AlarmManager
consume less resources and according to this StackOverflow question it is more advisable to use them, since they work at the Kernel level. In this link you can see how an alarm is defined and the different types that exist.On the other hand, to guarantee that the service operations are carried out without the service falling asleep and not completing its operations, I have decided to use
wakefulBroadcastReceiver
both inConnectivityReceiver
andOnAlarmWakefulReceiver
, as explained here . Since these Receivers are in charge of calling the service that will carry out the operations and it is not important that they remain without finishing their operations, to guarantee their correct operation.Code
I have modified the OnBootReceiver so that it creates an alarm that will take care of waking up the dispatch process every 10 minutes in my case, regardless of whether the application is running or not. The code would be as follows:
In addition, to call the service for sending statistics from the alarm , I have created the following
Receiver
to the manifest of the question should be added:
the ConnectivityReceiver would be the same as the one in the question except that it would have to be added
i.putExtra(Constants.proviene, Constants.provConnectivity);
when making the intent to be able to tell the Service from where it is calledAnd finally , the following code should be added to the service
WakefulBroadcastReceiver
to indicate to those who have called it that it has finished performing the operations:I think I have described it quite detailed, but if there is something that is not very clear I will try to explain it as best as possible by editing the answer.
The first thing you must be clear about in order to execute a task equivalent to a
cronjob
is that it must be an asynchronous task. For this you can see the Android AsyncTask Android documentation .The important thing is that this task is always 1 and only 1 its instance. Therefore you have to occupy the design pattern
Singleton
. You can create a class that extends fromAsyncTask
, you create the methods.EDITION
Inside your service you can create a class
After that in your service you identify when the receiver identifies:
Where
doInBackground
does the task of your function and then executesonPostExecute
. Use the methodonProgressUpdate
is to show progress of your task, for example a counter, a progress bar etc etc. I don't know if it's so necessary, but you can use it!I couldn't be very specific with the code as I don't fully understand your exercise, but I hope it can guide you!
It's better to do it with ScheduledExecutorService, since the WakefulBroadcastReceiver class is deprecated. Schedule also works when the app is closed.
I am going to put an example:
This class is responsible for printing a toast on the screen (this is where you have to do the actions you want in the background). It will do this when it receives an alarm.
To configure an alarm you simply have to do it with the AlarmManager class, indicate the Intent you want it to execute and indicate the period with the setRepeating function.
If you have any questions do not hesitate to ask. All the best.
The most common option is via ScheduledExecutorService