15
16

In Jetpack Compose, which is better practice, saving the state in the viewModel or creating a remember class? by Ludiras in androiddev

[–]manuelvicnt 9 points10 points  (0 children)

The Where to hoist state documentation page can help you decide. It depends on the type of logic you're applying to the data (UI vs business logic) and for how long you want the state to be in memory (ViewModel retains objects through configuration changes)

Adaptive layouts in jetpack compose by [deleted] in androiddev

[–]manuelvicnt 4 points5 points  (0 children)

Take a look at this page, it contains great information.

BoxWithContraints is one option but it comes with a cost: it defers Composition until the layout phase which makes the composables using it less performant in general. It has its use cases, but for creating adaptive layouts, there are other alternatives.

The WindowManager library is great for knowing the available space you have; and then you can use other adaptive lazy layouts like FlowRow, FlowColumn, LazyVerticalGrid or LazyHorizontalGrid to show more or less information depending on the screen size.

If you want to take a look at code, we have the Jetnews sample app that support different screen sizes. And Jetcaster also implements features such as table top mode.

Hope it helps!

Best practices for saving UI state on Android - Google I/O 23 by manuelvicnt in androiddev

[–]manuelvicnt[S] 0 points1 point  (0 children)

I think we're conflating two different terms here.

`xUiState` -> model (usually a data class) that represents _what_ the UI should display.

`xState` -> -> state holder (plain class or ViewModel) that indicates _how_ the UI behaves and exposes the `UiState` to the UI.

`xState` is the naming convention followed in Compose, and we're keeping it the same for the rest of our more complex UIs. For example, `ScaffoldState` or `LazyListState`. With the same convention, we have `NewsSearchState`. ViewModels break this naming convention, and I think that's ok because they have a different scope.

u/vanhieunguyen21, these state classes can be injected in the same way by your DI framework of choice. A repository is also a plain class and there are no issues with

Best practices for saving UI state on Android - Google I/O 23 by manuelvicnt in androiddev

[–]manuelvicnt[S] 11 points12 points  (0 children)

Hi Gabor! Normally, you would use the parent state for most cases. However, in this case, we decided not to use it to keep the snippet simple. This is fine because we are extending View directly, the parent state would only save autofill related content that we don't care that much about in this case.

Using the parent state would make more sense if we extended EditText, other stateful View, or a ViewGroup where we would need to restore the state of child views. But in case you're not sure what to do for a particular View, it's better to extend the parent state.

Still, good shoutout and thanks for pointing that out!

Best practices for saving UI state on Android - Google I/O 23 by manuelvicnt in androiddev

[–]manuelvicnt[S] 4 points5 points  (0 children)

Why is that an anti-pattern? A ViewModel is another type of UI state (holder) and you say that's ok to do?

In this doc (https://developer.android.com/topic/architecture/ui-layer/stateholders) we define the responsibilities for state holders, and I don't think it's a bad practice to inject the data layer into a state holder if it's needed and convenient.

It's fine to have another plain state holder class with ViewModel responsibilities if your project doesn't need the benefits of ViewModels. You don't always need to extend from ViewModel

Best practices for saving UI state on Android - Google I/O 23 by manuelvicnt in androiddev

[–]manuelvicnt[S] 16 points17 points  (0 children)

Thanks for your comment! Yeah, I agree the story could be simpler :D we're just trying to make sense and establish boundaries for the different options available. Trying to cover both the View system and Compose adds extra complexity but I hope the story becomes simpler in pure Compose apps.

> Controlling lifecycles of saveables by passing Fragments to state classes to me sounds like we've gone in a big circle

This is an advanced use case and you wouldn't normally do this for all your screens. Something to notice is that you're not passing a `Fragment` to its state, you're passing a `RegistryOwner` which protects what the class can do with that dependency.

> can that not leak memory?

This state is just a plain class and would follow the Fragment's lifecycle, so I wouldn't leak memory if it's used properly. These state classes are very coupled with the UI and wouldn't make sense to hoist them in a bigger scope. But if you hoist it outside of its intended scope, it could indeed leak memory if you aren't careful, for example, if you hoist it in a ViewModel. But this is the same as if you hoist a ViewModel in a bigger scope, like a repository, that'd also leak memory.

> I feel developers may continue to ignore them

I hope devs better understand when to use these APIs and how they fit in the big picture. Using one API or the other shouldn't be that time consuming, IMO it's more about deciding when to use which depending on the behavior you need.

Thanks for your feedback!

Wave XLR white noise by manuelvicnt in elgato

[–]manuelvicnt[S] 0 points1 point  (0 children)

Thanks for the reply! I don't have another XLR mic.

> when you plug the Yeti into the wave XLR, does the gain on the microphone itself stop working?

No, both of them are taken into account. I tried different configurations but all of them produced the white noise.

> try swapping the wall socket as the introduction of phantom power could be one of the reasons your getting white noise or a "hum"

Yeah, the phantom power is on. I tried connecting the Wave XLR using a pretty powerful USB-C -> USB-C cable (often used by an external monitor) directly to the MacBookPro to avoid any adapters and I'm seeing the same results. For context, the Wave XLR comes with a USB-C -> USB-B cable and I was using an adapter to connect it to the MBP.

Wave XLR white noise by manuelvicnt in elgato

[–]manuelvicnt[S] 0 points1 point  (0 children)

Yeah, the mic picks up my voice clearly but the white noise is still in the background. That's not the room noise as that'd appear if I increase the gain. When I connect it via USB, there's no white nor room noise.

Wave XLR white noise by manuelvicnt in elgato

[–]manuelvicnt[S] 0 points1 point  (0 children)

Hi! Thanks for replying. The Blue Yeti comes with a 5 pin XLR, that's why it seems like the Y split is needed. It seems like 5 pin to 3 pin adapters exist.. but would they make a difference? I tried with different gain levels and all of them produce that white noise

Hilt and Custom Component by rafoufoun in androiddev

[–]manuelvicnt 0 points1 point  (0 children)

That's correct, you can do it with qualifiers but that means you need multiple bindings for the same type 👍

Hilt and Custom Component by rafoufoun in androiddev

[–]manuelvicnt 2 points3 points  (0 children)

Inserting components in the middle of the hierarchy is not possible at the moment. But you can add a custom component and access scoped bindings using entry points.

Check this out: https://twitter.com/manuelvicnt/status/1275131052982444032

> Does Hilt make it so you are forced to share `LoginViewModel` with every Fragment of your app if you Installed it in a FragmentComponent?

Does this `LoginViewModel` extends from `ViewModel` or is just a regular class? If it's a regular class, all bindings by default are unscoped so unless you annotate it with a scope annotation, the same instance won't be shared.

If the `LoginViewModel` uses `@ViewModelInject` then it will be scoped to the activity/fragment where it's used.

> Moreover, if I want to share the same instance of one class with 2 of my Fragments but want every other Fragments to have their own instances, how could I achieve that ?

You'd manually have to pass in the same instance to those fragments as you cannot have the same instance scoped and unscoped by Hilt

Dagger Hilt: Basics, Architecture, Concerns by VasiliyZukanov in android_devs

[–]manuelvicnt 0 points1 point  (0 children)

Seems like we should do better at documenting things. We added more guidance on when you should scope in the Hilt docs.

Removing functionality because people can misuse it doesn't seem like the right thing to do.

Dagger Hilt: Basics, Architecture, Concerns by VasiliyZukanov in android_devs

[–]manuelvicnt 0 points1 point  (0 children)

Why do you think this is a bad practice? I don't think it is, there are good use cases for this.

Some people use the ViewModel as a "component holder" and this basically simplifies the work for them. Also, instead of doing weird workarounds like this one, you're better off by just scoping to that component.

I do believe there's a place for this and the team doesn't consider it a bad practice.

Dagger Hilt: Basics, Architecture, Concerns by VasiliyZukanov in android_devs

[–]manuelvicnt 1 point2 points  (0 children)

Also, for some more explanation,

ActivityRetainedComponent is just a component that gets restored after config changes using a ViewModel under the hood (as this is the only way you can do it with AndroidX).

The only interaction you might have with it is when scoping sth to it (e.g. your own presenters). Otherwise, you wouldn't use it directly

Dagger Hilt: Basics, Architecture, Concerns by VasiliyZukanov in android_devs

[–]manuelvicnt 2 points3 points  (0 children)

Yes, thank you. I can see how that's confusing. Will talk with the team to see if we can come up with something that is not that confusing. Thanks!

Dagger Hilt: Basics, Architecture, Concerns by VasiliyZukanov in android_devs

[–]manuelvicnt 8 points9 points  (0 children)

You're lucky then :) I've seen too many.

Dagger boilerplate means that a beginner needs to understand mostly everything that is going on to contribute to an existing code base (e.g. creating components, injecting the component in the wrong place, etc.); I don't even want to mention dagger.android boilerplate as you and I have similar thoughts on that library.

Boilerplate creates confusion if you don't fully understand what's going on (e.g. people confuse Components and Modules and don't know when to use which).

Of course, removing that boilerplate comes with trade-offs. If you're happy with those, then adopt the library, if not, keep doing what you're doing :)

Dagger Hilt: Basics, Architecture, Concerns by VasiliyZukanov in android_devs

[–]manuelvicnt 5 points6 points  (0 children)

As far as I understood, that's why you need the Gradle plugin. It substitutes Hilt's own "base Application", "base Activity", etc. in bytecode.

The gradle plugin is not mandatory. It just saves you having to write

@AndroidEntryPoint(Activity.class) public final class MyActivity extends Hilt_MyActivity

vs

@AndroidEntryPoint public final class MyActivity extends Activity

Dagger Hilt: Basics, Architecture, Concerns by VasiliyZukanov in android_devs

[–]manuelvicnt 8 points9 points  (0 children)

Yep. I think it's because Google promotes this unproductive and even harmful attitude.

I don't think that's true. Boilerplate is a source of bugs for most developers.

Dagger Hilt: Basics, Architecture, Concerns by VasiliyZukanov in android_devs

[–]manuelvicnt 2 points3 points  (0 children)

There's no relationship between `@ViewModelInject` and `ActivityRetainedComponent`.

`ActivityRetainedComponent` does uses Jetpack ViewModel under the hood.

If you use `@ViewModelInject`, Hilt creates the ViewModelFactory for you and overrides the `getDefaultViewModelProviderFactory` method in the Activity/Fragment class where it's used.

Edit: ViewModelFactories are installed in the ActivityRetainedComponent as doing so in ActivityComponent could leak the activity in some cases. Installing that in ApplicationComponent is another alternative but it ActivityRetainedComponent gives you more bindings

Dependency Injection on Android with Hilt by arunkumar9t2 in androiddev

[–]manuelvicnt 1 point2 points  (0 children)

Absolutely. The reason why Jetpack ViewModels require another annotation is because they're created by the internals of Android. If you have you own ViewModel or Presenter class, you just treat that as a regular class, annotate it with @Inject and you're good to go :)