Idea for the Linter: Hint about replacing `withCon...
# android
n
Idea for the Linter: Hint about replacing
withContext(Dispatchers.Main) { invalidate() }
with
postInvalidate()
a
Are you running into this often?
n
just beginning with Android and Kotlin, found this in an App source and thought that this is something easily fixable... so not sure whether that is particularly widespread
a
It's the first time I've seen it, anyway 🙂
n
yeah me too, was just a spontaneous idea
z
It probably doesn’t matter in 99% of cases, but those two bits of code have slightly different behaviors. The first one won’t return until the
invalidate()
call is finished (i.e. all the necessary flags have been set), the second will return immediately.
launch(Dispatchers.Main) { invalidate() }
would be a better potential candidate for linting.
a
that, plus in either case I'd be very suspicious about why the code is using a
postInvalidate
-alike at all instead of a plain
invalidate
. Most code that calls
View.invalidate
should generally be part of a view subclass itself and call it when custom view state changes. Meaningful
postInvalidate
usages tend to date back before the
Choreographer
was introduced in jellybean and should be so rare today that a lint warning to prefer a particular version of something you shouldn't use at all anymore isn't really necessary.
❤️ 1
n
How would you invalidate a custom view when data was loaded e.g. via a Web API from a Non-UI Thread? Like in
Copy code
class MyView : SomeViewClass {
  fun onCreate () {
    GlobalScope.launch(<http://Dispatchers.IO|Dispatchers.IO>) {
      loadWebAPI ()
      postInvalidate() 
      // or
      withContext(Dispatchers.Main) { invalidate() }
    }
  }
}
a
By calling
theView.setData(data)
. The view would then call
invalidate()
on itself in the implementation of
setData
. Invalidating isn't the responsibility of code that binds data to views.
And code that binds data to views should only call view methods on the UI thread, which seems to be the root of the question 🙂
n
Hmm, probably right. Currently the code sets the data in the IO thread directly (no setData function on the custom view) and then calls invalidate
a
Generally you want to keep views ignorant of things like data sources. Have custom views expose setters intended to be called on the UI thread
data loading should generally happen from outside the view and set the data on the view when loading is complete
n
The view is tightly intertwined with the loading process, as it is capable of displaying incomplete data while it is loading and also needs to trigger data reloads upon user interaction
a
That's fine, the loading process can push those intermediate states to the view too
I realize this sounds complex and potentially unnecessary, you certainly can keep this intertwined with the view, but two things create a pressure to separate them: testability and scoping of async operations.
n
The data structure is rather complicated, and it makes no sense to supply this custom view with other data sources... It's conceptually tied to this one specific web api, so reusing it with some other data source makes little sense
a
Testability in that if you separate them, you can isolate testing and verification of the interface between them. You can test the loading code independent of the code that displays it, and you can test the display code independent of the code that loads it.
Scoping because most of the time operations like this end up being cached and continued beyond the lifetime of a single view instance, often due to things like activity recreation. You'll often want to keep a load operation alive but re-bind it to a new view instance later.
If you scope the load to the view, then the load has no choice but to shut down and restart across such events.
n
the activity doesn't get recreated, it's basically a Kiosk-application. Why would I test the display code independantly from the loading code? I can just always use the actual loading code with the actual web API
a
You'll find out. 🙂
😂 1
☝️ 2
n
it worked fine for the last few years