Mar 17 | 7:20 PM |
Mark M. | has entered the room |
Mark M. | turned on guest access |
Mar 17 | 7:30 PM |
Jan S. | has entered the room |
Mark M. |
hello, Jan!
|
Mark M. |
how can I help you today?
|
Mar 17 | 7:35 PM |
sudokai | has entered the room |
sudokai |
Hi
|
Mark M. |
hello, sudokai!
|
Jan S. |
View paste
|
Mark M. |
(sudokai: let me take a question from Jan, and I'll be with you shortly!)
|
Mark M. |
Jan: that sample is using Koin for dependency inversin
|
Mark M. |
er, dependency inversion
|
Mark M. |
it is Koin's responsibility to ensure that a single is truly a singleton and does not wind up creating multiple instances
|
sudokai |
Yes, no problem. Other people's questions can be interesting as well.
|
Mark M. |
so, whatever locking exists (and there should be some) would be inside of Koin, which is why you don't see it in the app code
|
Mark M. |
the same thing holds true for other DI frameworks, like Dagger -- you rely on the DI container to handle thread safety for initializing singletons
|
Mark M. |
if you are not using DI, though, you probably do want to have some sort of synchronization on creating your own singletons
|
Jan S. |
Okay. I had the FTS project open and did a search and that is what it gave me.
|
Mark M. |
yes, most of the examples in *Elements of Android Room* will be using Koin for DI
|
Mark M. |
Koin is lightweight, with an easy API, making it great for book examples :-)
|
Mark M. |
I have more material on Koin for DI in *Elements of Android Jetpack*
|
Mar 17 | 7:40 PM |
Mark M. |
let me take a question from sudokai, and I'll swing back to you in a bit if you have another question or follow-up from this one
|
Mar 17 | 7:40 PM |
Mark M. |
sudokai: your turn! how can I help you today?
|
sudokai |
I was wondering about your views on using fragments vs custom views.
|
Mark M. |
generally, I try to avoid custom views, as I find them to be aggravating
|
Mark M. |
so, in a case where those solutions are equally possible, I would use a fragment
|
sudokai |
So far, I have seen that's not straight forward to access the viewModel from a custom view
|
Mark M. |
ideally, IMHO, a custom view knows nothing about viewmodels, lifecycles, or any of that stuff
|
Mark M. |
it just draws the UI and provides basic processing for user input
|
Mark M. |
the less code you have in a custom view, the better
|
sudokai |
What would you use custom views for then?
|
Mark M. |
only for cases where I needed to subclass an existing view to tweak functionality, particularly where it involves protected methods
|
Mark M. |
also, possibly, for cases where I need it to go places where a fragment can't go very easily (e.g., rows in a RecyclerView)
|
Mar 17 | 7:45 PM |
sudokai |
I've inherited a project that has a custom view that's the upload status bar of an item detail.
|
Mark M. |
what is it customizing?
|
sudokai |
The item detail is a fragment, and it passes the model to the custom view. Then the custom view checks the model's properties and changes dynamically. It also exposes an "retry upload" button that's conditionally shown depending on the state of the model.
|
sudokai |
When you tap on the button, the custom view itself launches the upload through a JobScheduler
|
Mark M. |
um, to be completely honest: that sounds awful
|
sudokai |
Would you use a fragment for this instead? It seems like things are a bit coupled.
|
Mark M. |
I'd start by getting that upload logic out of a view and into a viewmodel or possibly a repository
|
Mark M. |
is this being reused anywhere else? if not, and if there is no custom onDraw() or similar rendering logic, I'd just try to flatten it all into the fragment that is hosting it
|
Mar 17 | 7:50 PM |
sudokai |
I don't think it's being used anywhere else. The custom view is extending framelayout and the layout file starts with <merge>
|
sudokai |
Inside there's a linearlayout
|
Mark M. |
my guess is that you could move that stuff into the fragment's layout and the rest of the logic have split between the fragment and a viewmodel, but that is just a guess, given that I can't exactly see your code from here :-)
|
Mark M. |
let me take another question from Jan, and I'll swing back to you in a bit
|
Mark M. |
Jan: your turn! do you have another question?
|
Jan S. |
If you started out with an activity and now need a viewModel, should you change to use a fragment+viewModel? Or try to keep with activity and just add the viewModel?
|
Mark M. |
there is nothing wrong with using a viewmodel with an activity
|
Mark M. |
many of my samples do that, if I do not have a particular reason to need a fragment
|
Jan S. |
I'm using recyclerview and adapter with activity if that makes a difference.
|
Mark M. |
Google's stated preference is for fewer activities and more fragments, so it is possible that you might introduce a fragment as part of a wholesale shuffling of your architecture
|
Mar 17 | 7:55 PM |
Mark M. |
but, looking at a single activity in isolation, unless you are embarking on a "put everything in fragments" policy with an eye towards long-term conversion, do not feel that you have to force in a fragment without a reason
|
Jan S. |
I use a fragment on the detail view just not its "master".
|
Mark M. |
there, it is possible that you might want to have a fragment as part of supporting larger screens, such as tablets
|
Mark M. |
the master-detail pattern was the classic reason for introducing fragments in the first place, after all
|
Mark M. |
however, you may have other plans for larger screens
|
Mark M. |
(such as "we'll worry about them sometime later")
|
Mark M. |
so, add a fragment if the fragment adds value
|
Mark M. |
if you do not perceive near-term value and aren't sure about long-term value, don't worry about it
|
Mark M. |
let me take another question from sudokai, and I'll be back with you in a bit
|
Jan S. |
Okay. Just wanted to do it now if there was an obvious need based on using ViewModel. So I should be okay.
|
Mark M. |
yes, you should be fine!
|
Mark M. |
sudokai: your turn! do you have another question?
|
sudokai |
Yes
|
sudokai |
I asked this a few days ago in the forum but I'm still dealing with the issue
|
Mar 17 | 8:00 PM |
sudokai |
I had some singletons in my Application class that are then used everywhere in the app
|
sudokai |
The singletons are initialized in onCreate
|
Mark M. |
this is tied to https://community.commonsware.com/t/jobservice-... ?
|
sudokai |
Yes!
|
Mark M. |
OK, just making sure I had the background
|
sudokai |
So, I'm starting to move stuff to Koin, but the app is huge and in the mean time, I moved some of the singletons to a external class outside of Application
|
sudokai |
I called it SharedContext
|
sudokai |
So inside I have
|
sudokai |
val contextRef: AtomicReference<Context> = AtomicReference()
|
sudokai |
View paste
|
sudokai |
Then the first thing I do in the JobService or the Application class is to set the contextRef with the context value if it's null
|
sudokai |
This is done in a synchronized function with double null check
|
sudokai |
But!!
|
sudokai |
In my Google Play Store ANR/Crashes pages, I'm still seeing crashes inside Activites
|
sudokai |
When they try to access SharedContext.sharedPreferences
|
sudokai |
How is that possible??
|
Mark M. |
what is the exception?
|
sudokai |
The order of creation should be Application oncreate, then Activity onCreate
|
Mark M. |
yes
|
Mar 17 | 8:05 PM |
sudokai |
Give me a sec, if you want to switch to Jan
|
sudokai |
I'll look for the exception
|
Mark M. |
OK, let me know when you're ready
|
Mark M. |
in the meantime... Jan: do you have another question?
|
Jan S. |
No. Not right now.
|
Mark M. |
OK -- if you think of one, let me know!
|
sudokai |
It's an NPE!
|
Mark M. |
do you have the stack trace? is it directly on the getSharedPreferences() call, or is it somewhere deeper?
|
sudokai |
View paste
(8 more lines)
|
Mar 17 | 8:10 PM |
sudokai |
It only happened to 1 person, but 13 times
|
sudokai |
I wonder if it's an Android bug
|
Mark M. |
and line 30 is the contextRef.get().getSharedPreferences("prefs", Activity.MODE_PRIVATE) line?
|
sudokai |
Yes, exactly
|
Mark M. |
what is the make and model of this user's device? some device manufacturers... let's just say that "quality assurance" isn't just a phrase, but rather a long-term objective
|
sudokai |
Galaxy A40
|
Mark M. |
well, that's pretty conventional
|
sudokai |
Not the most high end Samsung
|
sudokai |
I've pasting a bit more of context
|
sudokai |
View paste
|
Mark M. |
I assume that SplashActivity is a splash screen and has your MAIN/LAUNCHER <intent-filter>
|
sudokai |
Yes
|
sudokai |
View paste
|
sudokai |
That's the inner null check
|
sudokai |
Outside of that there's a outer null check:
|
Mark M. |
that seems like overkill
|
Mark M. |
at least for this case
|
Mark M. |
just call set(context) and be done with the matter
|
Mar 17 | 8:15 PM |
sudokai |
View paste
|
Mark M. |
even if for some completely bizarre reason, get() returned a non-null value... do you care?
|
sudokai |
Well, I'm seeing ghosts everywhere, so I don't want to risk a double initialization lol
|
sudokai |
Sorry, switched names
|
Mark M. |
well, at least for this case, it's not really an initialization -- you're just populating this AtomicReference
|
sudokai |
Yeah, well, there's more stuff afterwards, like Crashlytics and broadcast receivers and whatnot
|
Mark M. |
but that too should just be stuff that you have in onCreate() of the Application subclass
|
Mark M. |
what you have would not explain the crash, but it adds complexity and clutter that might be hiding something else that would explain the crash
|
Mark M. |
frankly, if it is just one user, for a weird case like this, I tend to assume it is somebody reverse-engineering the app or something
|
Mark M. |
and they are monkeying around with the app initialization
|
sudokai |
Okay, sounds fair.
|
sudokai |
I've been pulling my hair out.
|
Mark M. |
I don't really recommend that
|
Mar 17 | 8:20 PM |
Mark M. |
(speaking as somebody who can practically count the individual hairs left on my head)
|
Mark M. |
so, I'd try to streamline your initialization logic, if possible, along with your migration to Koin
|
Mark M. |
but, to get to your question: there is no scenario in which onCreate() of an Activity should be called where onCreate() of the corresponding Application would not have been called
|
sudokai |
Yeah, this is basically a stopgap measure. I pulled the logic out of Application into a ShareContext so that my JobService could initialize it
|
Mark M. |
which also shouldn't be needed, insofar as I would expect onCreate() of the Application to be invoked before your JobService gets to run its job
|
Mark M. |
there is a very short list of things that happen before onCreate() of the Application
|
Mark M. |
1. onAttach() on the Application
|
Mark M. |
2. constructors for any ContentProvider implementations
|
Mark M. |
and AFAIK, that's it
|
sudokai |
Okay, make sense. Thanks.
|
sudokai |
Can I stil ask another question?
|
Mark M. |
Jan: do you have another question, or can sudokai continue?
|
Mar 17 | 8:25 PM |
Jan S. |
I have 2 things. First page 149 of Room book. Last sentence. Did you mean unnecessarily? Didn't read all context so wasn't sure.
|
Mark M. |
no, I think I have that right
|
Jan S. |
Second: article on medium says " Note: Room uses its own dispatcher to run queries on a background thread. Your code should not use withContext(Dispatchers.IO) to call suspending room queries. It will complicate the code and make your queries run slower." Your bookRepository import function uses it though. So confused.
|
Mark M. |
the import() function is doing more than Room I/O, though
|
Mark M. |
I am reading in assets, in particular, which is I/O
|
Mark M. |
when I make the insert() call, Room will switch the CoroutineContext to one that uses its own dispatcher
|
Jan S. |
okay. nevermind then. Just saw the brackets enclosed only that.
|
Mark M. |
if I were creating the entities purely from memory, I could skip my own withContext() call -- it's the I/O that triggers my need for withContext(Dispatchers.IO), even though Room will use its own dispatcher for its portion of the import() work
|
Jan S. |
That's all I have for now. Thanks.
|
Mark M. |
OK
|
Mark M. |
sudokai: we're almost out of time -- got something quick?
|
sudokai |
Yes
|
sudokai |
If you launch multiple Workrequests in parallel and chain another one after it
|
Mar 17 | 8:30 PM |
sudokai |
Then you replace one of the parallel workrequests, the chained workrequest gets cancelled as well?
|
Mark M. |
I have no idea, sorry
|
Mark M. |
I haven't tried doing that
|
sudokai |
Okay, I will try it and see what happens
|
Mark M. |
my WorkManager use cases have been very simple
|
sudokai |
Will let you know
|
Mark M. |
sounds good!
|
Mark M. |
and, that's a wrap for today's chat
|
Mark M. |
the transcript will go up on https://commonsware.com/office-hours/ shortly
|
sudokai |
Thanks Mark!
|
Mark M. |
the next chat is Thursday at 8:30am US Eastern
|
Jan S. |
Thank you so much for your time.
|
Mark M. |
thanks for attending, have a pleasant evening, and stay healthy!
|
sudokai |
Stay healthy!
|
Jan S. | has left the room |
sudokai | has left the room |
Mark M. | turned off guest access |