Create Chron on Android

16

I'm trying to make a cron run every X time in the system tag to call a service that I have made.

The features of cron that I want to do are the following:

  • run independently if the Application is running or not (partially done with the OnBootReceiver )
  • run when restart the device (I have it done, see code below in OnBootReceiver )
  • run every X time interval (every 10 minutes for example)
  • if there is no connection at the time of running the service when the cron has been activated, that a Receiver connection is activated so that when there is connection the service is executed and then deactivated this Receiver (I have done see code below in ConnectivityReceiver ).
  • Some of these characteristics I have already achieved by doing them separately, then I put you a 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);
        }
    }
    

    SendServiceStandard

    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 restarting independently of the execution of the application, in addition to controlling the subject of the connection at the time of 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?

        
    asked by Joacer 27.10.2016 в 10:00
    source

    3 answers

    17

    I have found a solution to my problem by fulfilling the points I wanted the cron described in the question.

    Explanation

    After investigating and looking to use the Timer in the OnBootReceiver as @sioesi advised me, I found that the AlarmManager consumed fewer resources and according to this question from StackOverflow in English is more advisable to use them, since they work at Kernel level. In this link you can see how an alarm is defined and the different types there are.

    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 in ConnectivityReceiver as in OnAlarmWakefulReceiver , as explained < a href="https://developer.android.com/training/scheduling/wakelock.html"> here . Since these receivers are responsible for calling the service that will do the operations and it does not matter if they run out of operations, to ensure proper operation.

    Code

    I modified the OnBootReceiver to create an alarm that    will take care of waking up the sending process every 10 minutes in my case,     independently of whether the application is running or not. The code would look like this:

    public class OnBootReceiver extends BroadcastReceiver {
    
        private static final String TAG = OnBootReceiver.class.getSimpleName();
    
        private static final int PERIOD = 1000 * 60 * 10;  // 10 minutes
    
        @Override
        public void onReceive(Context context, Intent intent) {
    
            if (intent.getAction().equals("android.intent.action.BOOT_COMPLETED")) {
                setAlarm(context);
            }
        }
    
        public static void setAlarm(Context context) {
            AlarmManager mgr =(AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
    
            //configuramos una alarma para que se haga el envio de las estadisticas sino esta creada ya
            boolean alarmUp = (PendingIntent.getBroadcast(context, 0,
                    new Intent(context, OnAlarmWakefulReceiver.class),PendingIntent.FLAG_NO_CREATE) != null);
    
            if (!alarmUp){    
                mgr.setRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP,
                        SystemClock.elapsedRealtime()+60000,
                        PERIOD,
                        getPendingIntent(context));
            }else{
                Log.i(TAG, "EnvioEstadisService: Alarm is already active");
            }
        }
    
        public static void cancelAlarm(Context ctxt) {
            AlarmManager mgr=(AlarmManager)ctxt.getSystemService(Context.ALARM_SERVICE);
    
            mgr.cancel(getPendingIntent(ctxt));
        }
    
        private static PendingIntent getPendingIntent(Context ctxt) {
            Intent i=new Intent(ctxt, OnAlarmWakefulReceiver.class);
    
            return(PendingIntent.getBroadcast(ctxt, 0, i, PendingIntent.FLAG_UPDATE_CURRENT));
        }
     }
    

    also to call the service of sending statistics from the     alarm I created the following Receiver

    public class OnAlarmWakefulReceiver extends WakefulBroadcastReceiver {
    
        @Override
        public void onReceive(Context context, Intent intent) {
    
            Intent i =  new Intent(context, EnvioEstadisticasService.class);
            i.putExtra(Constants.proviene, Constants.provAlarm);
    
            startWakefulService(context, i);
        }
    }
    

    to the manifest of the question would have to be added:

    <receiver android:name=".cron.OnAlarmWakefulReceiver"></receiver>
    

    the ConnectivityReceiver would be the same as the question except that you would have to add i.putExtra(Constants.proviene, Constants.provConnectivity); when doing the intent to be able to tell the Service from where it is called

    And finally to the service you would have to add the following code to indicate to the WakefulBroadcastReceiver who have called you that you have finished doing the operations:

     // Release the wake lock provided by the WakefulBroadcastReceiver.
     if(Constants.provConnectivity.equals(proviene))
          ConnectivityReceiver.completeWakefulIntent(intent);
     else
         OnAlarmWakefulReceiver.completeWakefulIntent(intent);
    

    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.

        
    answered by 28.10.2016 / 10:51
    source
    5

    The first thing you must have in mind to execute a task equivalent to a cronjob is that it must be an asynchronous task. For this you can see the Android documentation AsyncTask Android .

    The important thing is that this task is always 1 and only 1 its instance. Therefore you have to fill in the design pattern Singleton . You can create a class that extends from AsyncTask , you create the methods.

    EDITING

    Within your service you can create a class

    private class MyAsyncTask extends AsyncTask < String, Void, String > {
    
        @Override
        protected String doInBackground(String...params) {
    
            return "Executed";
        }
    
        @Override
        protected void onPostExecute(String result) {
    
        }
    
        @Override
        protected void onPreExecute() {}
    
        @Override
        protected void onProgressUpdate(Void...values) {}
    }
    

    After you identify in your service when the recipient identifies:

    public class OnBootReceiver extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent) {
            if ("android.intent.action.BOOT_COMPLETED".equals(intent.getAction())) {
                new Timer().schedule(new TimerTask() {
                    @Override
                    public void run() {
                        new MyAsyncTask().execute();
                    }
                }, 0, 10000); //Son ms
            }
        }
    }
    

    Where doInBackground does the task of your function and then onPostExecute is executed. Using the onProgressUpdate method is to show progress of your task, for example a counter, a progress bar etc etc. I do not know if it's so necessary, but you can use it!

    I could not be very specific with the code since I do not fully understand your exercise, but I hope I can guide you!

        
    answered by 27.10.2016 в 13:42
    0

    The most common option is through ScheduledExecutorService

    final Runnable myTask = new Runnable() {
                    public void run() {
                        //llama método cada x segundos.
                        Servicio(); 
                    }
                };
                ScheduledExecutorService timer = Executors.newSingleThreadScheduledExecutor();
                timer.scheduleAtFixedRate(myTask, 10, 10, TimeUnit.SECONDS);
    
        
    answered by 06.10.2017 в 01:47