Check for network status update while application killed

63 views Asked by At

My scenario is the following:

I'm working on a chat application and I would like to implement some type synchronization service that starts itself when device recovers network connection. Anytime device has network connection again, unsent messages are going to be automatically sent. With independence of application state (foregorund, background or killed).

Options tried:

1. Broadcast Receiver with android.net.conn.CONNECTIVITY_CHANGE
This scenario only works when the application is active (Foreground or Backround) but stops working when app is killed.

2. Foreground service
A notification is going to be shown all the time which is not ideal. Also I want to avoid draining users' battery.

3. AndroidX.Work.Worker

 PeriodicWorkRequest networkCheckingPeriodicWork = PeriodicWorkRequest.Builder.
                From<ConnectivityChangeWroker>(repeatInterval:30, repeatIntervalTimeUnit: Java.Util.Concurrent.TimeUnit.Minutes, flexInterval:25, flexIntervalTimeUnit:Java.Util.Concurrent.TimeUnit.Minutes)
                .SetConstraints(new Constraints.Builder().SetRequiredNetworkType(AndroidX.Work.NetworkType.Connected)
                .SetRequiredNetworkType(AndroidX.Work.NetworkType.Unmetered).Build()).Build();

            WorkManager.Instance.EnqueueUniquePeriodicWork("", ExistingPeriodicWorkPolicy.Replace, networkCheckingPeriodicWork);


public class ConnectivityChangeWroker : AndroidX.Work.Worker
{
    public ConnectivityChangeWroker(Context context, WorkerParameters workerParameters) : base(context, workerParameters)
    {

    }

    public override Result DoWork()
    {
        try
        {
            //Start synch service

            return Result.InvokeSuccess();
        }
        catch (Exception)
        {
            return Result.InvokeFailure();
        }
    }
}

But in this case, I'm not achieving the desired behaviour. For my undestanding, I just set a periodic work that checks for network connection, and if there is one, runs DoWork() method.

-- EDIT -- 4.JobService

Java.Lang.Class javaClass = Java.Lang.Class.FromType(typeof(ConnectivityChangeJob));
            ComponentName component = new ComponentName(Application.Context, javaClass);

            JobInfo jobInfo = new JobInfo.Builder(1, component)
                                     .SetRequiredNetworkType(Android.App.Job.NetworkType.Any)
                                     .SetOverrideDeadline(5000)
                                     .SetPersisted(true)
                                     .Build();

            JobScheduler jobScheduler = (JobScheduler)GetSystemService(JobSchedulerService);
            jobScheduler.Schedule(jobInfo);




[Service(Name = "Extintores.ConnectivityChangeJob", Permission = "android.permission.BIND_JOB_SERVICE")]
public class ConnectivityChangeJob : JobService
{
    private Intent startServiceIntent;

    public ConnectivityChangeJob()
    {

    }

    public override bool OnStartJob(JobParameters jobParams)
    {
       //Start synchService

        return true;
    }

    public override bool OnStopJob(JobParameters jobParams)
    {
        return true; //Reschedule the job
    }
}

But in this case, OnStartJob is only fired the first time the applicatio is opened and, apparently, never again.

Is there any way I can achieve what I'm aming for?

-- EDIT --
I want to achieve the same bahaviour as applications as WhatsApp. When it detects network connection again, automatically all unsent messages are going to be send.

1

There are 1 answers

1
Aleksei Sinelnikov On

I guess the AndroidX.Work.Worker is the best option.

In DoWork you should update databases and send requests.

Besides worker supports long-running workers

Example DownloadWorker:

class DownloadWorker(context: Context, parameters: WorkerParameters) :
   CoroutineWorker(context, parameters) {

   private val notificationManager =
       context.getSystemService(Context.NOTIFICATION_SERVICE) as
               NotificationManager

   override suspend fun doWork(): Result {
       val inputUrl = inputData.getString(KEY_INPUT_URL)
                      ?: return Result.failure()
       val outputFile = inputData.getString(KEY_OUTPUT_FILE_NAME)
                      ?: return Result.failure()
       // Mark the Worker as important
       val progress = "Starting Download"
       setForeground(createForegroundInfo(progress))
       download(inputUrl, outputFile)
       return Result.success()
   }

   private fun download(inputUrl: String, outputFile: String) {
       // Downloads a file and updates bytes read
       // Calls setForeground() periodically when it needs to update
       // the ongoing Notification
   }
   // Creates an instance of ForegroundInfo which can be used to update the
   // ongoing notification.
   private fun createForegroundInfo(progress: String): ForegroundInfo {
       val id = applicationContext.getString(R.string.notification_channel_id)
       val title = applicationContext.getString(R.string.notification_title)
       val cancel = applicationContext.getString(R.string.cancel_download)
       // This PendingIntent can be used to cancel the worker
       val intent = WorkManager.getInstance(applicationContext)
               .createCancelPendingIntent(getId())

       // Create a Notification channel if necessary
       if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
           createChannel()
       }

       val notification = NotificationCompat.Builder(applicationContext, id)
           .setContentTitle(title)
           .setTicker(title)
           .setContentText(progress)
           .setSmallIcon(R.drawable.ic_work_notification)
           .setOngoing(true)
           // Add the cancel action to the notification which can
           // be used to cancel the worker
           .addAction(android.R.drawable.ic_delete, cancel, intent)
           .build()

       return ForegroundInfo(notificationId, notification)
   }

   @RequiresApi(Build.VERSION_CODES.O)
   private fun createChannel() {
       // Create a Notification channel
   }

   companion object {
       const val KEY_INPUT_URL = "KEY_INPUT_URL"
       const val KEY_OUTPUT_FILE_NAME = "KEY_OUTPUT_FILE_NAME"
   }
}