I'm having trouble understanding the problem in this scenario.
I have a class used for Analytics Tracking, lets call it FlurryTracker,
it has 2 methods StartTrackingScreen(ScreenName) and StopTrackingScreen().
Now if I have a static var called screenName and each time start tracking screen is called screenName is reassigned.
startTrackingScreen(activity: Activity, screen: DhTracker.Screen<T>) {
screenName = screen.getName()
val lastScreen = Singleton.getLastScreen()
//If last screen is not same as current screen
FlurryAgent.logEvent(screenName, true)
}
}
override fun stopTrackingScreen() {
//New screen will start tracking before lastScreen tracking is stopped.
if (enabled) {
FlurryAgent.endTimedEvent(Singleton.getLastScreen()?.getName())
}
}
companion object{
lateinit var screenName : String
}
These methods are called in onStart() and onStop() in the app itself.
So with that being said, we are only tracking 1 screen at a time because when the user transitions to a new screen, onStop() and onStart() will be called.
So even though screenName is static, every time the lifecycle methods are called, that static var is being reassigned. Since a phone can't have 2 activites running at the same time, there will only be 1 instance of my tracker active at a time.
I don't really see the problem with using a static here, although I know best practices say that screenName should be created with every new instance. Am I looking at this the wrong way?
You can do that.
The two main patterns for something like this are static variables & methods or a singleton (That is often static so you can use it from different paths without passing it around). Both of these approaches are functionally identical.
The negatives for the static class are:
Difficult to test due to the fact that you have to replace the static method
Difficult to create a second instance
Difficult to pass around if you decide you want to (Some people like to know what classes are used by a given path for testing purposes)
These aren't that bad, you can live with them--however none of these issues exist if you use a singleton. You can easily pass it around, change it's behavior, convert it to use injection instead of the singleton pattern, …
So I can't really come up with a reason to ever use the static approach, there aren't any advantages.
Related
I'm working on an Android app that uses some background tasks (AsyncTasks) and I want to use best practices regarding data persistence across app lifecycle and tasks callbacks.
Up to now, I have a mix of practices regarding this:
1) I have some static fields in classes where AsyncTasks are used in the form of:
private static String str1;
private static String str2;
private static int int1;
...//=>no more than 6 static fields
2) I use a sinleton App instance with many getters/setters in the form of:
package xxx.xxx.xxx
import xxx.xxx.xxx
...
public class AppSettings {
private static AppSettings singleton;
private String _field1;
...//=>many fields
public void setField1(String field1) { _field1 = field1; }
public String getField1() { return _field1; }
...//=>many getters/setters
private AppSettings() {}
public AppSettings getInstance(){
if (instance== null) {
synchronized(AppSettings.class) {
if (instance == null)
instance = new AppSettings();
}
}
return instance;
}
}
I definitely know that abusing of static fields is not good at all, so I decided to replace them all, but I'm not completely sure if my second approach -having an application instance in a singleton with many getters/setters- is considered a good way to go, and in case not, I would like to know about better alternatives.
Thank you very much.
Edit 1: Just to clarify.
In order for you to understand more clearly what I use my AppSettings singleton class for I'll give you two examples:
1) I use it to store app setting/configuration values (that's why the name) to be available anywhere. For example, font color, font size, whatever.
2) I use it to store temporary data/values. For example, my main activity creates a small video in the backgroung using "VideoHelper" class and called through an AsyncTask, and as video generation process needs some parameters from main activity, I use AppSettings getters/setters to send them through.
Edit 2: Better explanation of everything.
Thanks to #a_local_nobody I realized my "case of use" was not so clear so I'll add a few things more.
My AppSettings is not being used to store user settings, I use SharedPreferences for that, but instead app default configuration parameters.
To give an example, I store activities background color (and this is just an example) so if in the future I change my mind and decide to use another background color this setting (and many more) are centralized there. It's like a "container" for many default app settings.
Regarding the use of getters and setters in this app singleton class, I think I'll foloww #a_local_nobody suggestion related to define some static variables in each class and use them as needed instead of having a bunch of unrelated getters/setters globally.
Anyway, all comments are welcome.
Well, you are talking about persisting data across app lifecycle which, in my mind, sounds like you're looking for a ViewModel:
The ViewModel class is designed to store and manage UI-related data in
a lifecycle conscious way. The ViewModel class allows data to survive
configuration changes such as screen rotations.
as well as:
The purpose of the ViewModel is to acquire and keep the information
that is necessary for an Activity or a Fragment. The Activity or the
Fragment should be able to observe changes in the ViewModel.
ViewModels form part of the MVVM design pattern, with loads of examples available online.
For more info, have a look at the documentation
on a side-note, perhaps you can have a look at the google sunflower project for some ideas on how to implement the new architecture components, which includes usages of ViewModels.
Also worth adding, is that what you've created with your AppSettings solution, is a big dependency. Various things will depend on this single object and it will be needed throughout your application, most likely. You might consider, instead of creating it like this, to rather use dependency injection with your options, for android, probably being either Dagger 2 or Koin for kotlin (if you ever swap over to kotlin) or perhaps your own form of dependency injection without having to use these frameworks.
Hope this helps
Edit based on feedback from OP:
I use it to store app setting/configuration values (that's why the
name) to be available anywhere. For example, font color, font size,
whatever.
this sounds like a better use case for Shared preferences, especially if these are settings defined by a user, otherwise you should be savings these into strings.xml etc. and making use of localization
I use it to store temporary data/values. For example, my main activity
creates a small video in the background using "VideoHelper" class and
called through an AsyncTask, and as video generation process needs
some parameters from main activity, I use AppSettings getters/setters
to send them through.
if you have a VideoHelper class, you might be better off either creating a Builder design pattern for this object, or having static variables for this helper to change its functionality as you need to, if these are variables for your VideoHelper, then they should be located with your VideoHelper.
Things which change together should usually stay together.
Your approach doesn't qualify as "best practices" in modern android development.
The recommended way of handling configuration changes is by using the new architecture component: ViewModel
It have the property of surviving the onDestroy triggered when a configuration change occurs.
Basically, you will need to move this AppSettings code to a ViewModel.
I don't know if this is a stupid question. This may defeat the purpose of LiveData/ViewModel.
Can I make the LiveData static? My reason is I have a listener from a Service which updates the information. So I need to have a way from a service to "set/change" the LiveData.
I used to do following and it works:
1. Service changes the DB
2. ViewModel listens to the DB change
3. UI updates from the liveData change
I found this way is too slow. To increase the performance, I want something like:
1. Service changes the class object directly
2. ViewModel listens to the the class object changes
3. UI updates from the liveData change
In order to achieve what I want, either I need to make the MutableLiveData static or make the ViewModel class to share the same instance of ViewModel between Activities.
Is this good idea?
public class MyViewModel extends AndroidViewModel {
// Note: this MutableLiveData is static
private static MutableLiveData<MyModel> mutableLiveData;
public MyViewModel(#NonNull Application application) {
super(application);
}
LiveData<MyModel> getLiveDataList() {
if (mutableLiveData == null) {
mutableLiveData = new MutableLiveData<>();
loadDataFromDb();
}
return mutableLiveData;
}
private void loadDataFromDb() {
// load data from DB
// mutableLiveData.setValue(MyModelFromDb); // Omit the real implementation
}
// Note: this method is static
public static void setData(MyModel newData) {
mutableLiveData.setValue(newData);
}
#Override
protected void onCleared() {
super.onCleared();
}
}
The whole point of ViewModel from Android Jetpack (as opposed to other versions) is for the ViewModel to be lifecycle aware and perform magic like destroying itself when observer is destroyed (activity/fragment), or surviving configuration changes (for example, orientation) without initialising itself all over again thereby making it much easier to deal with issues related to configuration changes.
So if you made the ViewModel or LiveData static you would actually beat their purpose and most likely leak ViewModel's data, though the need to do this is understandable. So this requires you to engineer your way around it, and the first way you mentioned is probably the best way you can do it. I don't understand why you have an issue with the first solution. The way I see it, it provides the best user experience:
You init ViewModel in your fragment or activity in onCreate and add an Observer to the data.
If database already has some data, your observer will receive it instantly and UI will be updated with existing data instantly.
Service makes the API request and changes the DB
DB changes triggers an update to the data in ViewModel
Observer refreshes received data and you pass this to your views/adapters
UI updates with latest data with some nice animations that indicate addition/removal of items.
From what I can see it cant get better than this. Since your question is from months ago, I am curious to know what you ended up doing?
I think if MyViewModel will have lots of LiveData fields it will grow with large amount of getters and setters. And what even worst, as for me, you will break the testablity of your code, because if you will create a new instance of MyViewModel you will expect that your LiveData objects are stateless at this point of time, but as it's a static object you don't know in what exactly state it is after simple creation.
As well static methods can't be overriden. And about fields: if you will want to have common field, suppose errorMessage, in class A and class B while both of them extend class C(which contains your common field) you can have unexpected behavior. On the other hand you can duplicate this code in other classes(what is bad).
The memory issue: if a large number of static variables/methods are used. Because they will not be GC until program ends.
But it just my opinion.
According to the information I can find, the MvvmCross Viewmodel life cycle is
Construction - using IoC for Dependency Injection
Init() - initialisation of navigation parameters
ReloadState() - rehydration after tombstoning
Start() - called when initialisation and rehydration are complete
I have implemented mine as follows:
public async Task Init(Guid ID)
{
await MPS_Mobile_Driver.Droid.DataModel.ShipmentDataSource.GetShipmentInventory(ID);
ShipmentInventory = ShipmentDataSource.CurrInventory;
ShipmentLots = await MPS_Mobile_Driver.Droid.DataModel.ShipmentDataSource.GetShipmentLotList((int)ShipmentInventory.idno, (short)ShipmentInventory.idsub);
Inv_DamageList = await ListDataSource.GetInv_Damage();
}
protected override void SaveStateToBundle(IMvxBundle bundle)
{
base.SaveStateToBundle(bundle);
bundle.Data["ShipmentInventory"] = StringSerializer.SerializeObject(ShipmentInventory);
bundle.Data["ShipmentLots"] = StringSerializer.SerializeObject(ShipmentLots);
bundle.Data["Inv_DamageList"] = StringSerializer.SerializeObject(Inv_DamageList);
}
protected override void ReloadFromBundle(IMvxBundle state)
{
base.ReloadFromBundle(state);
ShipmentInventory = StringSerializer.DeserializeObject<ShipmentInventory>(state.Data["ShipmentInventory"]);
ShipmentLots = StringSerializer.DeserializeObject<ShipmentLotList>(state.Data["ShipmentLots"]);
Inv_DamageList = StringSerializer.DeserializeObject<Inv_DamageList>(state.Data["Inv_DamageList"]);
state.Data.Clear();
}
First off, I can't seem to get the emulator to actually destroy the Activity when I hit the Home key even though I have that option checked. The activity seems to hang around in the background anyway.
That being said, when I do hit the home key, It calls SaveStateToBundle. What it never seems to do is to call ReloadFromBundle. This makes it hard to test What is actually going on. My fear is that according to the life cycle, if the app does recover from a tombstone, it will call Init before it calls ReloadFromBundle. Since Init is the preferred way to pass a parameter to a ViewModel, it only makes sense to it should use the parameter to initialize the data. If after this happens, it calls ReloadFromBundle, It would overwrite the data initialized in Init. This is fine but very inefficient. Is there a way to know in Init if the ViewModel is being created due to a new navigation or if it is recovering from a tombstone?
Thanks for any help with this.
Jim
I have the same issue with ReloadFromBundle(IMvxBundle state), it is never called. Only the "SaveState" pattern seems to work.
I can answer to your second question though:
You shouldn't use Init to load your data. You should only use it to pass ids of data to be retrieved. The "loading" phase should happen in the Start method.
I am looking for how to share functions and data across multiple activities within a single application. I researched the daylights out of it and find some ideology war between overriding the extend for the application and doing a singleton, neither of which I can find examples sufficient to make me understand. Basically I want to share data and share functions. All activities need the same functions and data so this is not one activity sharing data with another activity. It is all activities needing to have access to the same functions and data.
What I want to know is what is the way to go and how do I do it. I need to see what I need to do in my 34 activities, what the class that is going to be common looks like, and what the Manifest entry needs to be. I also need to be sure the common data area will not be closed by the OS.
This is my first Android - Java program and now find my 15,000 line, 34 activity application needs some structure. I know, should have done things differently but the app works really well with two exceptions. One is that it is structurally a mess. Two is that the fact it is a mess is making it hard to fix one behavior I would like to fix.
This is a GPS based application for racing sailboats. It is timing critical and every activity basically runs a once a second loop inside the location manager onLocationChanged function. That part is fine and I do not want to put the GPS code in one place. The problem is that most activities need to filter the data so a lot of code is copied and pasted to the activities. The filter needs history so it needs to remember a state. There are other functions that are used by several activities so these have been copied as well. Think of a function that averages the last three GPS speed readings. It needs to save some history, do its thing, and give a result. All activities need to do the exact same thing. All this works but the problem is that the averaging starts over every time I switch activities because every activity has its own filter. That gives a glitch in the data that I need to get rid of. I need common place to save the data and hopefully a common place to run the filtering and other functions that are common. If every activity can call the filter function that is using common state data, there will be no glitch across activity changes.
I would appreciate some guidance.
Why you don't just make a Class with only static functions, passing needed Parameters? An example if you want to show an ErrorDialog
public class SharedHelper{
public static Dialog showErrorDialog(Context ctx, String message, String title, DialogInterface.OnClickListener okListener, DialogInterface.OnClickListener cancelListener){
AlertDialog.Builder builder = new AlertDialog.Builder(ctx);
builder.setMessage(message).setTitle(tilte);
if (okListener != null){
builder.setPositiveButton(R.string.button_positive, okListener);
}
if (cancelListener != null){
builder.setNegativeButton(R.string.button_negative, cancelListener);
}
return builder.show();
}
}
Singletons are (from my point of view) one of the uglyest design pattern and will bite you sooner or later. Putting anything in Application requires you to cast it everytime to the Special Application class you designed. A class with only statics however is very flexible in its usage and doesn't need an instance to work.
For the storage-issue:
lookup "SharedPreferences" & "SQLite" and decide afterwards which storage-type suits your needs more.
For the methods-issue:
This question is a bit more complex and there are different ways to do it. For example you could write a parent-class that implements all your globally needed questions and you let all your activity-classes inherit from it.
public class MyParentActivity extends Activity {
public void myMethod() {
}
}
and:
public class Activity1of34 extends MyParentActivity {
myMethod();
}
I think what this comes down to is not an Android problem but an Object-Oriented Programming problem. If I understand the situation correctly, I'm betting the best solution would be to take your shared filter and create a new Filter class that is instantiated within each Activity (this is likely more manageable than a singleton, but not having seen your use case, it's hard to say for sure). If you need to centrally track the averaging, you can simply create a static variable within the Filter class that maintains the same value during the life of the application. If you really want to maintain that average (even past the application's current lifecycle), you can persist it in a database or other local data options. However, I don't see any reason to put everything in a singleton just to maintain that average. Singletons (and all static data structures) can be potentially troublesome if used incorrectly.
I, for one, do not mind the singleton pattern. Of course as everything else it should not be abused.
This is the construction I use for my shared objects. My app is divided into modules this way but can just as well be used in your case.
public class SharedDataObject {
private Context context;
private static SharedDataObject instance;
public static SharedDataObject getInstance() {
if (instance == null) throw new RuntimeException("Reference to SharedDataObject was null");
return instance;
}
public static SharedDataObject createInstance(Context context) {
if (instance != null) {
return instance;
}
return instance = new SharedDataObject(context.getApplicationContext());
}
// notice the constructor is private
private SharedDataObject(Context context) {
this.context = context;
}
...
public void myMethod() {
// do stuff
}
}
Notice that it uses the application context, that means among other things, means that the context owned by SharedDataObject cannot be used for GUI operations. But, the context will live for the entire lifetime of the application, which is nice.
Furthermore I hate having to pass a context everytime I wish to call methods on my SharedDataObject, thus I have a splashscreen calling SharedDataObject.createInstance() on all my modules.
Once an instance is create, I can call:
SharedDataObject.getInstance().myMethod();
Anywhere in my code, regardless of a context being present or not (from the place calling this code that is).
When I'm writing a method or using a member variable, I often find I need to share them across an app. But where should they go?
I can subclass Activity, but that falls over as soon as I use a MapView and am forced to use MapActivity, so not all my activities inherit from my subclass. Is there I way around this?
Where inheritance isn't applicable, I am tending to put generic methods and member variables into a subclass of the Application object, but I'm finding it's creating a mess of code as every class needs to either grab access to the application object through via context, or I have to pass it down.
I suspect I would be better off creating MyApplication.getInstance() and keeping everything in a singleton, instead of passing the application object down through the app classes. but before I wanted to see what you guys had to say.
If you want to access the "Global Singleton" outside of an activity and you don't want to pass the Context through all the involved objects to obtain the singleton, you can just define, as you described, a static attribute in your application class, which holds the reference to itself. Just initialize the attribute in the onCreate() method.
For example:
public class ApplicationController extends Application {
private static ApplicationController _appCtrl;
public static ApplicationController getAppCtrl()
{
return _appCtrl;
}
}
One example with resources: Because subclasses of Application also can obtain the Resources, you could access them simply when you define a static method, which returns them, like:
public static Resources getAppResources()
{
return _appCtrl.getResources();
}
For global methods, use a static Util class with static methods. If you can't use static methods, then the methods shouldn't be global in the first place, and put them in the class that makes sense.
First read this:
How to declare global variables in Android?
Now why you shouldn't use a static singleton. Using a singleton is a the same thing as a global variable. Global variables reduce your maintainability because everywhere you use the global variable you break modularity or introduce global details and assumptions about your overall design. Your program can't have two of these variables because it only looks in one place for it. This means your program can't adapt easily when you have two instances instead of one.
For example, say I have a method called playTurn() and I implement it like so:
public void playTurn() {
globalPlayer.incrementClock();
globalPlayer.doSomething();
globalPlayer.doSomethingElse();
}
Now let's say I want to add a second player to the mix. Uh oh my playTurn() method assumes one player only when it used globalPlayer. If I want to add a second player to the program I have to change that method. Do this a lot and your program is very rigid and inflexible to change. Instead what if I did this:
public void playTurn(Player player) {
player.incrementClock();
player.doSomething();
player.doSomethingElse();
}
Now can do this:
playTurn( player1 );
playTurn( player2 );
I can reuse playTurn() for both player1 and player2 and I didn't have to change it. I just had to change the client of that method.
Most of the time you're being lazy and you want to get a reference to some object, and global variables are fast ways to get references to well known objects. Instead it's better to have one class that resolves the dependencies across your application at start up or the time when it makes sense. Then only that one place understands how your code is put together. For example,
public class Game {
Player player1;
Player player2;
Board board;
public void startGame() {
BlueTooth blueTooth = BlueTooth.getChannel();
player1 = new LocalPlayer();
player2 = new NetworkedPlayer( blueTooth );
board = new Board();
player1.setOpponent( player2 );
player1.setBoard( board );
player2.setOpponent( player1 );
player2.setBoard( board );
}
}
Now everyone has their dependencies, and they don't need to use static variables to find references to things. Also player1 doesn't have to know about details like that player2 is over the network, or that it's apart of a Game. What's important to note is that these objects we're connecting have a long life, possibly the entire program, but if they need to create other things at runtime that's ok for them to do.
Say for example, we need to create multiple players at runtime based on who joins the game. Well we might create a PlayerManager that we can instantiate at startup then create Player objects on the fly. PlayerManager is just a plain old object that we create in Game when we start a new game.
I hope you can start seeing this is a much better way to develop software. You might not understand it right off, but if you think about it will make more sense. It's very subtle change, but very powerful.