getodk / collect

ODK Collect is an Android app for filling out forms. It's been used to collect billions of data points in challenging environments around the world. Contribute and make the world a better place! ✨📋✨
https://docs.getodk.org/collect-intro
Other
719 stars 1.37k forks source link

Replace reliance on CONNECTIVITY_ACTION broadcasts to be able to target API 24 #2499

Closed lognaturel closed 5 years ago

lognaturel commented 6 years ago

Collect currently uses CONNECTIVITY_ACTION broadcasts to trigger auto-send attempts.

Because of #1992, Collect will need to target API 26 by November 2018.

From https://developer.android.com/about/versions/nougat/android-7.0-changes#bg-opt:

Mobile devices experience frequent connectivity changes, such as when moving between Wi-Fi and mobile data. Currently, apps can monitor for changes in connectivity by registering a receiver for the implicit CONNECTIVITY_ACTION broadcast in their manifest. Since many apps register to receive this broadcast, a single network switch can cause them all to wake up and process the broadcast at once.

Apps targeting Android 7.0 (API level 24) and higher do not receive CONNECTIVITY_ACTION broadcasts if they declare their broadcast receiver in the manifest. Apps will still receive CONNECTIVITY_ACTION broadcasts if they register their BroadcastReceiver with Context.registerReceiver() and that context is still valid.

the JobScheduler API provides a robust mechanism to schedule network operations when specified conditions, such as connection to an unmetered network, are met. You can even use JobScheduler to react to changes to content providers.

yanokwa commented 6 years ago

@grzesiek2010 you are too fast! We've talked to @jd-alexander about this issue, so I'll be reassigning it to him.

grzesiek2010 commented 6 years ago

@grzesiek2010 you are too fast! We've talked to @jd-alexander about this issue, so I'll be reassigning it to him.

No problem. I have other issues, just wanted to do that after them or in the middle if I'm free. Btw not that fast, I claimed the issue one hour after you had created it :smile:

jd-alexander commented 6 years ago

Background So I have been doing some research on this matter and based on my findings the recommended approach to running any form of background jobs is to utilize the job scheduling capabilities that are provided in Android JetPack's Work Manager. The JobScheduler was being touted as the new defacto standard for backgrounding jobs but it's only compatible with apps targeting API 21 and above while the Firebase dispatcher was created as a compat variant for lower APIs. The introduction of the WorkManager created a unified approach to this where it acts as an abstraction layer above the JobScheduler, Firebase Dispatcher and AlarmManager choosing the appropriate execution operation based on API level, constraints, and availability of system dependencies ( such as Google Play Services).

Current Architecture

Currently, the auto send functionality works with the NetworkReceiver where it's triggered when a form is marked as finalized and when a connectivity event occurs eg. wifi connectivity. It doesn't care for individual form uploads as it operates on the entire group of finalized forms that haven't been submitted.

Migration options with the Work Manager

Based on what I have seen with the WorkManager there's no way to mimic the NetworkReceivers behavior because it's API doesn't allow jobs to be triggered periodically with network changes. The only way this could be done is if a single job is being triggered when a network event occurs and it gets marked as failed even if it succeeds so it never leaves the queue.

So the approaches we can take are

Let me know if you have any thoughts on this. I am trying to think of the best approach to take with the direction the Android APIs are going in with respect for the different API levels and constraints being posed for background operations.

lognaturel commented 6 years ago

Thanks, @jd-alexander. Would also be great to get your thoughts, @grzesiek2010, since it seems you may have had some ideas as well.

It looks like WorkManager is nearly identical in goals to the android-job dependency we already have and that android-job will soon be deprecated. So perhaps one first thing to do is migrate current uses of android-job to WorkManager - https://github.com/evernote/android-job/issues/520.

What about enqueuing a job that sends all forms with unsent status and, if the job succeeds and auto-send is still on, enqueues the same job again?

jd-alexander commented 6 years ago

It looks like WorkManager is nearly identical in goals to the android-job dependency we already have and that android-job will soon be deprecated. So perhaps one first thing to do is migrate current uses of android-job to WorkManager - evernote/android-job#520.

Yes, I could work on doing this migration.

What about enqueuing a job that sends all forms with unsent status and, if the job succeeds and auto-send is still on, enqueues the same job again?

Yes, that would work well. Good idea.

lognaturel commented 6 years ago

There are some questions at https://github.com/opendatakit/collect/pull/2508#issuecomment-416070180 about whether a Play Services update will be needed and how likely that would be to impact users.

This line from https://developer.android.com/topic/libraries/architecture/workmanager/ makes me think we're probably ok:

WorkManager chooses an appropriate way to schedule a background task--depending on the device API level and included dependencies

I think the way to check will be to update all the dependencies as needed, make some WorkManager calls, compile, and see if google_play_services_version changes. It's currently set to 10084000.

jd-alexander commented 6 years ago

I think the way to check will be to update all the dependencies as needed, make some WorkManager calls, compile, and see if google_play_services_version changes. It's currently set to 10084000.

Okay!

jd-alexander commented 6 years ago

Next steps for moving this forward will be to :

In terms of the migrating the auto-send functionality, the worker will be enqueued every time a finalized form is saved if another job isn't current on the queue. This is necessary because each AutoSendWorker performs sends on all finalized forms that are marked with auto-send so once the current job hasn't been executed it will automatically send the most recent finalized forms. the AutoSendWorker has a network constraint attached to it so that could cause it to remain in the queue until a network event occurs.

getodk-bot commented 6 years ago

Hello @jd-alexander, you claimed this issue to work on it, but this issue and any referenced pull requests haven't been updated for 10 days. Are you still working on this issue?

If so, please update this issue by leaving a comment on this issue to let me know that you're still working on it. Otherwise, I'll automatically remove you from this issue in 5 days.

If you've decided to work on something else, simply comment @opendatakit-bot unclaim so that someone else can claim it and continue from where you left off.

Thank you for your valuable contributions to Open Data Kit!