-
Notifications
You must be signed in to change notification settings - Fork 0
Feat: Network Rule #7
base: main
Are you sure you want to change the base?
Conversation
…are already added
- use StateFlow - take NetworkManager as parameter
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like the implementation overall @canonall, well done!
serializers, | ||
configuration, | ||
store | ||
serializers = serializers, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like the use of named arguments.
|
||
constructor(task: Task, info: JobInfo) : this (UUIDFactory.create(), info, task, Clock.System.now()) | ||
constructor(task: Task, info: JobInfo) : this( | ||
UUIDFactory.create(), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not that important, you could also use named arguments here :)
listener.emit(result) | ||
var shouldRunJob = false | ||
try { | ||
withTimeout(job.info.networkRuleTimeout) NetworkRuleTimeout@{ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just curious why the withTimeout
, my understanding is that NetworkListener.currentNetworkState
gives instant feedback.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
withTimeout(job.info.networkRuleTimeout) NetworkRuleTimeout@{
networkListener.currentNetworkState.collect { currentNetworkState ->
val isNetworkRuleSatisfied =
networkListener.isNetworkRuleSatisfied(
jobInfo = job.info,
currentNetworkState = currentNetworkState
)
if (isNetworkRuleSatisfied) {
shouldRunJob = true
jobEventListener.emit(JobEvent.NetworkRuleSatisfied(job))
[email protected]("Network rule satisfied")
}
}
}
The aim of using the withTimeout
function was to ensure that the network requirement is met within a specified time frame. With each emission of the currentNetworkState, the 'isNetworkRuleSatisfied' flag is checked, instantly. If the flag is true, the NetworkRuleTimeout scope is canceled. If the flag is false, the currentNetworkState continues to be observed until the timeout expires. Without withTimeout
the job would never be canceled until the network requirement is met, which I thought not desirable.
networkManager | ||
.observeNetworkConnection() | ||
.collect { currentNetworkState -> | ||
_currentNetworkState.emit( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If I did not misunderstand your implementation, you could also dismiss the when
block and directly write:
_currentNetworkState.emit(currentNetworkState ?: NetworkState.NONE)
networkManager.startMonitoring() | ||
scope.launch { | ||
networkManager.network.collectLatest { currentNetworkState -> | ||
_currentNetworkState.emit( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If I did not misunderstand your implementation, you could also dismiss the when
block and directly write:
_currentNetworkState.emit(currentNetworkState)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Small note: in this case you could simplify it with (same applies to the android side):
_currentNetworkState.emit(it)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually, I don't like using it
. I choose the long names most of the time 🙃
|
||
private val connectionPublisher = MutableStateFlow(getCurrentNetworkConnection()) | ||
|
||
fun observeNetworkConnection(): Flow<NetworkState?> = connectionPublisher |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do I understand the correctly that on iOS
we basically observe the connection, hence the Flow
could emit changes and on Android
we only get the state at the time of the function invocation?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well, I just realized that now and I guess the answer is yes. I will do the following change:
fun observeNetworkConnection(): Flow<NetworkState?> = connectionPublisher | |
val network: Flow<NetworkState?> = connectionPublisher |
and then listen it just as in IOS
in Android NetworkListener
override fun observeNetworkState() {
scope.launch {
networkManager
.network
.collectLatest { currentNetworkState ->
_currentNetworkState.emit(currentNetworkState ?: NetworkState.NONE)
}
}
}
|
||
|
||
// not needed for Android | ||
override fun stopMonitoring() {} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We could either implement that in a way that the state gets refreshed every 5 seconds (or in any other interval) to mimic the iOS behaviour a bit. But this is not subject to be implemented instantly but rather - if I understood everything right - to keep it as an issue for future improvements.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually, I can just create a job variable and assign it in observeNetworkState. Then, I can simply cancel it. I will add that for now. I had a wrong thinking at first. I will also do the same for ios.
override fun stopMonitoring() {} | |
override fun stopMonitoring() { | |
job?.cancel() | |
} |
preAndroidMNetworkConnection(connectivityManager) | ||
} | ||
|
||
@TargetApi(Build.VERSION_CODES.M) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Lovely to have it backwards compatible. We should internally discuss which API level makes sense for Flowify. I would rather start with Android 6 or 7 to get rid of some old chunks. But nice that you've implemented it with old versions in mind! Just leave it as is at the moment, we may remove it later on!
@@ -135,18 +145,49 @@ abstract class AbstractJobQueue( | |||
job.delegate = delegate |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess somewhere in the while(isActive)
loop we need to invoke NetworkListener.observeNetworkState()
. The invocation is currently missing in the code :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🥴 sir yes sir
} | ||
|
||
queue.schedule(TestData(id), ::TestTask) { | ||
minRequiredNetwork(NetworkState.NONE, 3.seconds) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As there is no real or simulated device to observe the network, the default value for the network is always NetworkState.NONE. If the minRequiredNetwork is also set to NetworkState.NONE, the test will pass. However, in any other case, the test will fail. Any ideas to deal with this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think that was because I did not call observeNetworkState as you mentioned. Now, it behaves normally. The test environment network is set to MOBILE I guess. But I do not know how to change it to WIFI if I need to.
listener.emit(result) | ||
var shouldRunJob = false | ||
try { | ||
withTimeout(job.info.networkRuleTimeout) NetworkRuleTimeout@{ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
withTimeout(job.info.networkRuleTimeout) NetworkRuleTimeout@{
networkListener.currentNetworkState.collect { currentNetworkState ->
val isNetworkRuleSatisfied =
networkListener.isNetworkRuleSatisfied(
jobInfo = job.info,
currentNetworkState = currentNetworkState
)
if (isNetworkRuleSatisfied) {
shouldRunJob = true
jobEventListener.emit(JobEvent.NetworkRuleSatisfied(job))
[email protected]("Network rule satisfied")
}
}
}
The aim of using the withTimeout
function was to ensure that the network requirement is met within a specified time frame. With each emission of the currentNetworkState, the 'isNetworkRuleSatisfied' flag is checked, instantly. If the flag is true, the NetworkRuleTimeout scope is canceled. If the flag is false, the currentNetworkState continues to be observed until the timeout expires. Without withTimeout
the job would never be canceled until the network requirement is met, which I thought not desirable.
networkManager.startMonitoring() | ||
scope.launch { | ||
networkManager.network.collectLatest { currentNetworkState -> | ||
_currentNetworkState.emit( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually, I don't like using it
. I choose the long names most of the time 🙃
|
||
private val connectionPublisher = MutableStateFlow(getCurrentNetworkConnection()) | ||
|
||
fun observeNetworkConnection(): Flow<NetworkState?> = connectionPublisher |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well, I just realized that now and I guess the answer is yes. I will do the following change:
fun observeNetworkConnection(): Flow<NetworkState?> = connectionPublisher | |
val network: Flow<NetworkState?> = connectionPublisher |
and then listen it just as in IOS
in Android NetworkListener
override fun observeNetworkState() {
scope.launch {
networkManager
.network
.collectLatest { currentNetworkState ->
_currentNetworkState.emit(currentNetworkState ?: NetworkState.NONE)
}
}
}
@@ -135,18 +145,49 @@ abstract class AbstractJobQueue( | |||
job.delegate = delegate |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🥴 sir yes sir
|
||
|
||
// not needed for Android | ||
override fun stopMonitoring() {} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually, I can just create a job variable and assign it in observeNetworkState. Then, I can simply cancel it. I will add that for now. I had a wrong thinking at first. I will also do the same for ios.
override fun stopMonitoring() {} | |
override fun stopMonitoring() { | |
job?.cancel() | |
} |
The network rule requires two parameters:
NetworkState
andnetworkTimeoutDuration
. The NetworkState parameter specifies the desired state of the network that needs to be satisfied, while the networkTimeoutDuration parameter specifies the duration to observe changes in the network state.The network rule follows the flow outlined below:
networkTimeoutDuration
:NetworkRuleTimeout
will be emitted.DidCancel
will be emitted.NetworkRuleSatisfied
will be emitted andNetworkRuleTimeout@
scope will be cancelled.WillRun
will be emitted.jobTimeout
:result
will be emitted which can be one of three events:DidSucceed
,DidFail
,DidFailOnRemove
JobTimeout
andDidCancel
will be emitted.Changes in the code:
withTimeout()
toJobQueue