-
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
benjohnde
left a comment
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!
| if (queue.value.first().startTime.minus(Clock.System.now()) > 0.seconds) continue | ||
| lock.acquire() | ||
| val job = queue.value.removeFirst() | ||
| 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)
}
}
}
| if (queue.value.first().startTime.minus(Clock.System.now()) > 0.seconds) continue | ||
| lock.acquire() | ||
| val job = queue.value.removeFirst() | ||
| 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:
NetworkStateandnetworkTimeoutDuration. 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:NetworkRuleTimeoutwill be emitted.DidCancelwill be emitted.NetworkRuleSatisfiedwill be emitted andNetworkRuleTimeout@scope will be cancelled.WillRunwill be emitted.jobTimeout:resultwill be emitted which can be one of three events:DidSucceed,DidFail,DidFailOnRemoveJobTimeoutandDidCancelwill be emitted.Changes in the code:
withTimeout()toJobQueue