In the Android docs i found this article about conditional navigation where a login function returns LiveData to propagate the signIn result back from the viewmodel to the fragment. I assume the viewModel uses LiveDataBuilder to create the LiveData
//Fragment
fun login(username: String, password: String) {
userViewModel.login(username, password).observe(viewLifecycleOwner, Observer { result ->
if (result.success) {
savedStateHandle.set(LOGIN_SUCCESSFUL, true)
findNavController().popBackStack()
} else {
showErrorMessage()
}
})
}
I assume viewmodel does something like this.
fun login(username: String, password: String) = liveData {
//perform login
}
I always thought this is bad practice since it creates a Livedata object on every login attempt. In this case i mostly use another SingleLiveData object to post the login result to. I also used callbacks quite often. Something like this:
fun login(username: String, password: String) {
userViewModel.login(username, password,
onSuccess = {
//DO SOMETHING
}, onError = {
//DO SOMETHING
})
}
Can someone explain which is now the best approach and what is the reason the other appraoches should not be used?
You must not use callbacks in that way. You are leaking the views to the ViewModel.
LiveData used in this way is basically a glorified callback that will automatically be cancelled when the Fragment's view goes out of scope, so it's easier to use safely on the Fragment, and it avoids leaking the Fragment to the ViewModel. It saves you a lot of boilerplate that would be required to do it safely and without leaking the Fragmet.
I always thought this is bad practice since it creates a Livedata object on every login attempt.
I don't see what's bad about that. It's a lightweight object. Probably lighter weight than most of the Strings you work with in an app.
Related
At the moment I have something like;
public class SomeActivity {
void onCreate() {
doSomething();
if (weNeedToDoSomeAsncTask()) {
new SomeAsyncTask(afterSomeAsyncTask).execute();
} else {
new DifferentAsyncTask(afterAnotherAsyncTask).execute();
}
}
PostExecute afterSomeAsyncTask = new PostExecute() {
void Callback(String response) {
doSomethingElse(response);
new DifferentAsyncTask(afterAnotherAsyncTask).execute();
}
}
}
This doesn't look too bad on the face of it, but I have hugely simplified the code and you can imagine how complex it becomes when you have more than a few calls going on and some branching starts to happen.
To help with context, the actual use case is normally something like;
use an sdk to get some device/user information
use the returned information to authenticate with a web service
send some logging info to the web service
get some data from the web service
get some other data from the web service
use that data to generate the UI
I would prefer my code to look a bit more like;
public class SomeActivity {
void onCreate() {
doSomething();
if (weNeedToDoSomeAsncTask()) {
new SomeAsyncTask().execute();
doSomethingElse(response);
}
new DifferentAsyncTask().execute();
afterAnotherAsyncTask();
}
}
I have tried used .get() in the past and had issues but never got to the bottom of why (potentially I was trying to call one AsyncTask inside another, which I've read is a no go- but it was a long time ago so can't be sure). Whilst I realise .get somewhat defeats the object of AsyncTask, I am only using it because Java forces me to (for network communication etc). My application is relying on the result to continue and can't do anything else till it returns anyway.
Should I try the .get() technique again? Or is it a really bad idea?
Should I just suck it up and realise the code is harder to read and I will need to jump to the callback methods to try and understand the code flow?
Or is there a different option?
I have also just learnt of Needle which looks like quite a nice library https://zsoltsafrany.github.io/needle/ and I think I could do more like;
NetworkService networkService = new NetworkService();
Needle.onBackgroundThread().serially().execute(networkService);
String result = doSomethingWithResponse(networkService.response);
AnotherNetworkService anotherNetworkService = new AnotherNetworkService(result);
Needle.onBackgroundThread().serially().execute(anotherNetworkService);
But perhaps this is exactly the same as the AsyncTask.get() approach?
The TL;DR of any answer really will be: Kotlin coroutines.
Take a look at ScheduledThreadPoolExecutor which you can obtain like:
ExecutorService executor = Executors.newScheduledThreadPool(8);
There are numerous methods for scheduling futures.
Your first example might look like:
public class SomeActivity {
void onCreate() {
doSomething();
if (weNeedToDoSomeAsncTask()) {
executor.submit(() -> /* async task */);
doSomethingElse(response);
}
executor.submit(() -> /* different async task */)
afterAnotherAsyncTask();
}
}
(Although you'll need to look at the specific methods for alternatives to block/join.)
Is it possible to provide once own implementation of a ViewModelStore for ViewModelProviders to use instead of the default one?
More precisely, I'm interested in adding fun clear(vm: ViewModel) (or using an index or something similar) functionality to the ViewModelStore so that I can clear a single view model of my choice, not just use the built in ViewModelStore#clear:
public final void clear() {
for (ViewModel vm : mMap.values()) {
vm.onCleared();
}
mMap.clear();
}
which clears all view models.
First, I think you should not consider doing that, because that's an implementation detail of Architecture Components library. Most possibly you should come up with a better solution as a result of adapting your use-case to match guidelines/contracts exposed by ViewModels API.
Nevertheless, let's examine possibilities of doing that.
Here's the code, that we should use in order to obtain a ViewModel implementation:
val viewModel = ViewModelProviders.of(this).get(MyViewModel::class.java)
What will this code result in, is that it will create an instance of HolderFragment, which is a retained fragment, and will attach it to this's fragment manager (might be either FragmentActivity's fragment manager or Fragment's child fragment manager).
This HolderFragment will be added with a HolderFragment.HOLDER_TAG, thus we are able to get an instance of this fragment from the fragment manager.
val holderFragment = supportFragmentManager.findFragmentByTag("android.arch.lifecycle.state.StateProviderHolderFragment") as HolderFragment
It's the HolderFragment, that creates an instance of ViewModelStore and keeps that instance as a private field. There exists a getter for that field, but there does not exist a setter, which means, that the only way to "substitute" this object is by using reflection.
But before doing that, let's try to write a custom implementation of ViewModelStore class:
class MyViewModelStore : ViewModelStore() {
private val mMap = HashMap<String, ViewModel>()
internal fun put(key: String, viewModel: ViewModel) {
val oldViewModel = mMap.put(key, viewModel)
oldViewModel?.onCleared() // COMPILATION ERROR -> Cannot access 'onCleared': it is protected/*protected and package*/ in 'ViewModel'
}
internal operator fun get(key: String): ViewModel? {
return mMap[key]
}
override fun clear() {
for (vm in mMap.values) {
vm.onCleared() // COMPILATION ERROR -> Cannot access 'onCleared': it is protected/*protected and package*/ in 'ViewModel'
}
mMap.clear()
}
}
Unfortunately, we cannot do that, because ViewModel#onCleared() has a protected package access, which makes impossible for us call it outside of the android.arch.lifecycle package. Again, we can use reflection to do that (but how good is that?).
Despite being not advised (by me), seems like that's also not achievable to do (without using reflection).
I am building an Android MVVM application using RxJava2. What I want is to expose an Observable in my ViewModel, which I also can receive the last emitted value (like a BehaviourSubject). I don't want to expose a BehaviourSubject because I don't want the View to be able to call onNext().
For example, in my ViewModel I expose a date. I now want to subscribe a TextView to changes, but I also need to be able to access the current value if I want to show a DatePickerDialog with this date as initial value.
What would be the best way to achieve this?
Delegate:
class TimeSource {
final BehaviorSubject<Long> lastTime = BehaviorSubject.createDefault(
System.currentTimeMillis());
public Observable<Long> timeAsObservable() {
return lastTime;
}
public Long getLastTime() {
return lastTime.getValue();
}
/** internal only */
void updateTime(Long newTime) {
lastTime.onNext(newTime);
}
}
I don't want to expose a BehaviourSubject
Subject, by definition, is "an Observer and an Observable at the same time". Therefore, instead of exposing BehaviorSubject itself just expose Observable, thus client (in this case the view) won't be able to perform onNext(), but will be able to receive last emitted value.
As an alternative to subject approach you can use replay(1).autoConnect() approach. See more details concerning this approach in "RxJava by example" presentation by Kaushik Gopal.
Also, consider cache() operator (see difference of cache and replay().autoConnect() here).
I am having trouble with volley (again). I want to run a volley request in a class, so more than one activity can feed off its results and update their UI's accordingly. I have got it return data and call the request from the UI but now im struggling to update the UI with the new data. I have looked at answers but I'm trying to understand the structure and I am at a loss, can some please advise/ talk me through it
assuming I understand what you mean as being:
A Volley request returns, updates some data set through some activity
In this case, assuming the calling activity contains everything, and reminding that this is a very general example, what you should usually do (usually, since there are exceptions to the case), is just insert the data into the data set contained in your UI holder (e.g. your recycler adapter) and update it, an example would be your adapter holding a method similar to this:
public void updateDataSet(List<Item> items)
{
//mItemList is the adapters member list
if (null != mItemList)
{
mItemList.clear();
mItemList.addAll(items);
}
else
mItemList = items;
notifyDataSetChanged();
}
you call this inside the request callback you fired earlier, just make sure to initialize everything BEFORE you fire the request, e.g.
#Override
public void onResponse(JSONObject response)
{
Log.d(TAG + ": ", "somePostRequest Response : " + response.toString());
// here you need to parse to JSON to a list and then call...
List<Item> items = parseResponse(response);
myAdapter.updateDataSet(items);
}
Now, if what you meant was
A Volley request returns in some Activity, I want it to update stuff in another place
there are a couple of options:
As someone said in the comments - you could go for EventBus.
You could hold a DataManager class, which would be a global singleton, in which case you can either hold the data and update it there, and then every activity (in it's onResume or other relevant lifecycle method) knows to pull that data.
You could do the same as option 2, with the exception of that DataManager holding a reference to other UI parts (e.g. Fragments), and triggering member methods in them that pass the data and trigger the updates.
Personally I find option 3 cumbersome and somewhat bad practice, but if all else fails, (and it shouldn't, but if it does) then you can try.
There are more options out there, it depends and varies according to the data, your app architecture, coding style and other stuff you apply.
Hope this helps!
You can use EventBus. To use EventBus you need to register class where you will receive update and when you publish event for those event all classes will receive it.
Here is an example using greenrobot's EventBus :
Event Model :
public class MessageEvent {
/* Additional fields if needed */
}
Subscribe :
EventBus.getDefault().register(this); // In Activity onCreate method
#subscribe
public void onMessageEvent(MessageEvent event){
// this is the method to receive event
}
Publish event :
EventBus.getDefault().post(new MessageEvent());
Now every class subscribed for this event model will be updated.
Note : subscribed classes have to alive, If anyone destroyed they won't receive update.
This could get a little bit complicated and I'm not that experienced with Observables and the RX pattern so bear with me:
Suppose you've got some arbitrary SDK method which returns an Observable. You consume the method from a class which is - among other things - responsible for retrieving data and, while doing so, does some caching, so let's call it DataProvider. Then you've got another class which wants to access the data provided by DataProvider. Let's call it Consumer for now. So there we've got our setup.
Side note for all the pattern friends out there: I'm aware that this is not MVP, it's just an example for an analogous, but much more complex problem I'm facing in my application.
That being said, in Kotlin-like pseudo code the described situation would look like this:
class Consumer(val provider: DataProvider) {
fun logic() {
provider.getData().subscribe(...)
}
}
class DataProvider(val sdk: SDK) {
fun getData(): Consumer {
val observable = sdk.getData()
observable.subscribe(/*cache data as it passes through*/)
return observable
}
}
class SDK {
fun getData(): Observable {
return fetchDataFromNetwork()
}
}
The problem is, that upon calling sdk.subscribe() in the DataProvider I'm already triggering the Observable's subscribe() method which I don't want. I want the DataProvider to just silently listen - in this example the triggering should be done by the Consumer.
So what's the best RX compatible solution for this problem? The one outlined in the pseudo code above definitely isn't for various reasons one of which is the premature triggering of the network request before the Consumer has subscribed to the Observable. I've experimented with publish().autoComplete(2) before calling subscribe() in the DataProvider, but that doesn't seem to be the canonical way to do this kind of things. It just feels hacky.
Edit: Through SO's excellent "related" feature I've just stumbled across another question pointing in a different direction, but having a solution which could also be applicable here namely flatMap(). I knew that one before, but never actually had to use it. Seems like a viable way to me - what's your opinion regarding that?
If the caching step is not supposed to modify events in the chain, the doOnNext() operator can be used:
class DataProvider(val sdk: SDK) {
fun getData(): Observable<*> = sdk.getData().doOnNext(/*cache data as it passes through*/)
}
Yes, flatMap could be a solution. Moreover you could split your stream into chain of small Observables:
public class DataProvider {
private Api api;
private Parser parser;
private Cache cache;
public Observable<List<User>> getUsers() {
return api.getUsersFromNetwork()
.flatMap(parser::parseUsers)
.map(cache::cacheUsers);
}
}
public class Api {
public Observable<Response> getUsersFromNetwork() {
//makes https request or whatever
}
}
public class Parser {
public Observable<List<User>> parseUsers(Response response) {
//parse users
}
}
public class Cache {
public List<User> cacheUsers(List<User> users) {
//cache users
}
}
It's easy to test, maintain and replace implementations(with usage of interfaces). Also you could easily insert additional step into your stream(for instance log/convert/change data which you receive from server).
The other quite convenient operator is map. Basically instead of Observable<Data> it returns just Data. It could make your code even simpler.