I have a button in an activity that when pressed adds 1 to a text view in another activity. However, when I try to access that variable in a different activity, I cannot. How could I solve this without passing intents (making the variable open for use in the whole program)?
You can use companion object in the Activity where you set the desired variable:
class ShowCaseActivity : Activity {
companion object {
var yourVariable = 0
}
}
Now you can use this variable in every file in the project, you just have to import it accordingly like this:
import ShowCaseActivity.Companion.yourVariable
....
val number = ShowCaseActivity.yourVariable
It should be said that this is not a recommended way of solving this issue. You should pass data between Activities with intents.
There is a wall between Activities and you can only pass data over that wall with Intents. For this reason, it is recommended to use only one Activity. You can use Fragments to represent your different screens. Use an Activity-scoped ViewModel to hold properties and LiveData you need to access from both screens.
In your case, you would have a function in the ViewModel that the button calls. The function would increment a private variable and set its value to a public LiveData. The TextView would observe that LiveData so it is automatically updated when the value is changed.
Related
currently, my Kotlin based application consists of a single activity, 3 fragments and a navigation (with navigation drawer) between them.
How do I add a variable, which will be initialized in the start of the application, will be visible in all fragments, and can be updated in one of them?
a simple int or string for that matter so it should be with little overhead as possible, yet i'd like to follow correct coding conventions. Please elaborate on the correct function to perform the initial variable value, how to bind each fragment textview to it, and the correct way to set the new value.
Thanks!
Create ViewModel and fragments use viewmodel like this
val viewModel: YourViewModel by activityViewModels()
in that case your viewmodel's scope is the same as activity scope.
For more information please refer to this link
I'm new to android, I wanted to know if it is okay to access properties initialized in activity / call activity functions from fragment like this or is it bad practice and I should avoid it.
(requireContext() as BaseActivity).viewModel
(requireContext() as BaseActivity).countryList
(requireContext() as BaseActivity).getSomething()
Your instincts can be right. Breaking changes can be caused by name conflicts, variable shadowing, wrong imports, wrong assignment to values. But these days, the demand for features is increasing, in such that you need the public accessor. Just have this rules in your conscience:
Interfaces are powerful at class to class communication
Inherit what is important, override what is implemented already, pass to param to lessen global var damage
If a variable can be stored in another global form, consider it with regard to size(ram matters), speed of access(ux matters), security(keys matter) and volatility(nulls matter).
Now looking at your code, I can see you have a fragment system that is based on values/functions stored in the main activity, that provides the context for the fragment. If you apply the first point: Your fragment will implement a BaseFragment that already some context cast i.e. lateinit var mainActivity: MainActivity then you can mainActivity.viewModel anywhere in your fragment without casting. And this is cleaner
Applying the second point: in the BaseFragment (that will be inherited by AnotherFragment)
abstract var viewModel: ViewModel
abstract fun initList()
open var countryList = mutableListOf()
open fun onScale(detector: ScaleGestureDetector) { //pinch: increase visible country list like some nice zoom effect .. etc }
if most or all of your fragments need similar functions or variables, make abstract to something you can forget will crash the app, make open for those 'features but I dont need to rewrite so I'll call super.function' functions. Make a var open if some super function overrides it, and just put var if you seriously dont know when you want it and when to change it.
On the third point, Android in the early stages, we learnt the hard way that context doesn't last forever even if your app is running. Rotation and lifecycle functions will swap it rough and fast. So consider other storage ways. I still dont trust requireContext/Activity/view for context, so cast with caution.
A big NO. It is a bad practice to use hardcode references to activity from fragments.
I see that you are using viewmodel, which indicates that you are using MVVM, you should use Sharing data between Activities and Fragments in MVVM made simple with SharedViewModel concept for communicating to viewmodel of the activity.
For communicating to the Activity which hosts your Fragment you should use an interface pattern of communication. from fragment to the activity
Let's say one of your activity won't extend the base activity as it has to extend one activity from a library lets say YouTubeBaseActivity, and it will host a particular fragment now the cast to BaseActivity will never succeed in your activity.
I'm not sure if what I'm asking is correct, so please forgive my ignorance.
I need to use an object initialised in the main activity in another one.
I serialized the object class with implements Serializable and sent it to the new activity putting into an extra, retrieving it later with intent.getSerializableExtra.
The other way would be access directly the previously public declared object from the activity using the following:
MainActivity mainActivity = (MainActivity) this.getParent();
Object object = mainActivity.object;
Is any of this correct?
If yes, which one?
If both which is better?
I am not sure if you have the right design in the first place. Trying to cast the MainActivity is not a good practice. This is a step towards making it a God class.
I am not sure what is the point of sending a Serializable object. In general more likely is to use some primative values or Strings. If is something heavy why to pass it? And what bothers me more is that you are saying that you want to use something "initialized " in the MainActivity which makes me think that you are not trying to pass data, but access the state of MainActivity from another Activity which is a bad practice.
The Views needs to present data and notify for events like clicks some "other code". Nothing more. You can have some singleton class and change its state as appropriate depending on the actions in the First Activity. Then from the Second Activity, let's say you have MVVM, you create the ViewModel and observe it, the ViewModel in its init{} can call the shared singleton and can propagate the data back to the second Activity through LiveData, or better just to tell it what to "show" to the user.
I hope it helps.
MyApp need hold a User object in whole context,A,B,C activities'xml use this User object,when A edit User,I want B and C notifyChange,how to deal this problem with databinding,livedata and viewModel?
Formerly I make User.class extend BaseObservable,but POJO will be very troublesome and must not be a null,sometimes User maybe null such as not login.
Now I Change to use LiveData, make Pojo simple and not extend BaseObservable,but when A edit,B and C not work,I think i need ABC use same viewModel instance in memory,but this will cause viewModel's onClear() trigger manytimes.
Another way is to have one singleton repository to hold your user data and each viewModel can have that repository and share the same data between activities.
Based on this part of the documentation:
https://developer.android.com/topic/libraries/architecture/livedata#extend_livedata
The fact that LiveData objects are lifecycle-aware means that you can share them between multiple activities, fragments, and services. To keep the example simple, you can implement the LiveData class as a singleton as follows:
You can create a singleton for your view model like I did here:
companion object{
private lateinit var instance: ViewModelProfile
#MainThread
fun getInstance(userId: String): ViewModelProfile{
instance = if(::instance.isInitialized) instance else ViewModelProfile(userId)
return instance
}
}
Then I call it and get instance anywhere like this:
val profileVModel = ViewModelProfile.getInstance(user.uid)
If you want to share common ViewModel between ABC activities, then it is suggested to keep them as 3 fragments in a single Activity, create ViewModel of that Activity which can be shared among all three fragments A, B, and C.
Also what you are trying to achieve with activities is like this, suppose you have done some operation in activity A, if you want Activity B and C to get notified about them then they need to be running to get notified, which won't be happening, so instead you should use Intent or Bundle to pass needed information when the activity get started.
Updated
There are other ways as well to achieve similar kind of functionality like,
Event Bus
RxJava Subjects (refer this)
Android State by Evernote (refer this)
This will allow you to have application level access of state, which can be accessed by any Activity or Fragment
Using itent.putExtra makes a copy of my object. So changes make on this object on the activity that recive this itent does no reflect on others activity.
In my case will be good have reference to this object instead a copy, this is possible ?
ps: I know i can use onActivityResult to retrive the changes make on the object, but in my case the changes make on the object need to be done before the end of the activity.
Other way is: set a reference to a common property from activity1 (I am using MyApplication). The activity2 knows where to find that reference via a getter and it will use it. Decide if you want to modify the properties in activity2, when finished / every time you can have the reference in activity1.
This way is not needed to serialize / deserialize the object either. ( performance improvement too)
You can just store a reference to the object in a static member variable, like this:
public class Globals {
public static MyObject myObject;
}
Now, in the code that has the object, you just do:
Globals.myObject = object;
and in the new activity, you can get it like this:
doSomethingWith(Globals.myObject);
Now, having said that, you need to be aware of the following:
Android can kill your process if your application is in the background pretty much any time it wants to. When the user then returns to your application, Android will create a new process for your application and then it will recreate only the activity that was on the top of the activity stack (ie: the one that was showing). In that case, the newly created activity will not be able to get the iobject by accesing Globals.myObject because the process has been newly created and that member variable is null.
You should use an Application class, which you can get a reference to using the getApplicationContext() method.
Bottom line, create an Application class, which you will be able to reference from any class in your App, then you can reference a variable that is local to that class.
Here is a good SO question about this:
Using the Android Application class to persist data