Keep reference of an activity in the companion object - android

I have 2 activities: MainActivity and MySecondActivity.
I start MySecondActivity, but while I'm working in it, I receive a callback from an external library in MainActivity. This callback should update some information in MySecondActivity.
Question: Can I keep a reference of MySecondActivity in MainActivity in order to update it later ?
Basically it would be like this:
val mySecondIntent = Intent(applicationContext, MySecondActivity::class.java)
startActivity(mySecondIntent)
(...)
fun MyCallBackFunction(newInfo: Integer)
mySecondIntent.updateMyInfo(newInfo) <-- here I cannot access "MySecondIntent"
I tried to store the reference of mySecondIntent in the companion object but I could not make it work.
Do you have any suggestion ?
Thank you !

Here is my suggestion. Don't let any activity keeps the other activity reference, it's not the best practice.
Suggestion
If the callback you receive has impact on multiple activities, then make it global. I usually create a singleton manager class for handling each global event's logic that may have impact on many places in the app. Anywhere in your app need to listen for the event, register the event to the manager.
For example, my AppLocationManager is a singleton class, responsible for making gps refresh and other location stuff. When location change, fire events to all registered listeners. In your case, both MainActivity and SecondActivity knows the changes and update itself, remember to unregister the listener in activity onDestroy, or you will have a memory leak.
Workaround for quick implementation
If you still want to keep the activity reference for quick modification, which is not recommended, use WeakReference
Store a WeakReference of MySecondActivity as a static global variable:
public static WeakReference<Activity> mTmpAtivity
Unlike default StrongReference, this variable will release the instance if the activity is GC so it's memory safe.

Related

Is it a good practice to save activity instance in a WeakReference

Here, in this answer Activity instance is saved in WeakReference<Activity> variable. So that it will avoid memory leaks. Is it a good practice to do so?
public class BackgroundService extends IntentService {
private static WeakReference<Activity> mActivityRef;
public static void updateActivity(Activity activity) {
mActivityRef = new WeakReference<>(activity);
}
}
I'm using mActivityRef.get() and casting it to required activity object. Using that object, accessing the methods in activity.
The purpose is to access Activity methods from service, this code does the work but as per the comments I'm confused whether to use it or not
I've referred the document yet not clear.
Is it a good practice to do so?
No.
The purpose is to access Activity methods from service
That activity may not exist. For example, the user could press BACK and destroy the activity while the service is running. Calling methods on a destroyed activity will likely lead to crashes.
Use an event bus (LocalBroadcastManager, greenrobot's EventBus, etc.) for loosely-coupled communications between components, such as between services and activities. Have the activity register for events when it is visible, and have the service post events as needed.
No its not a good practice to store a reference of Activity anywhere in your project but if you want do, create an interface implement your activity with interface and pass that interface as a communication way between your activity and IntentService to your service.
Now your service has a reference of your activity's (selected) methods. Access your data through that interface and clear reference after its usage.

Are there any cons of creating Singleton class with static field to communicate between activities in Android?

There are 3 activities: A->B->C. Each contains a Button (to open next activity) and a EditText.
For example: if I type some text in C and go back to A(by pressing Back Button), how can I see the same text there?
I know 3 solution:
LocalBroadcastManager
SharedPreferences
Create Singleton class with static field and then get this field in onStart method of A - which cons of this solution?
IMHO, there are always cons in using Singleton design pattern in your applications. Some of them are (from the top of my head):
Coupling between otherwise unrelated objects and flows through Singleton's instance
Emergence of a "global state", which makes debug a lot harder
Inability to mock static fields and methods through "conventional" mocking
The fact that a reference to Singleton can be easily obtained in any part of the application leads to a total mess (people stop thinking about dependency graph)
Singletons tend to breed: you introduce one, then another one, then you find yourself with 10 singletons which hold app's state in a "global cloud state"
Note that what you're trying to do is against Android guidelines - if the user taps on "back" button, then he should find the previous Activity or Fragment in the exact same state it had the last time the user saw it, without any additions (unless you explicitly don't want to save it in the back-stack).
If you still want to do it, then I could suggest several options:
Use SharedPreferences and store the value there. Get the value in each Activity and diplay it in onResume()
Use startActivityForResult() call in order to start new Activities and pass the value back in the result. Note that by default press on "back" cancels the action, therefore you'll have to override onBackPressed() method.
Override onBackPressed() method in Activity in such a way that it starts another Activity (instead of just popping the back-stack) and pass the value in the Intent that you use. You might want to use FLAG_ACTIVITY_CLEAR_TOP in this case.
Use some event bus that supports "sticky" events. When user inputs the text you post a sticky event to event bus. In onResume() of Activity you check whether event of this type exists and if it is - you update UI.
Once again - the fact that you CAN do what you want doesn't mean it SHOULD be done.
Simply set into onResume() method of your class A, a call to the Singleton class instance you want to save (or istance of Application class which is the same)
LocalBroadcastManager is not a reliable option. It assumes the bottom activity to still be alive, which might not be the case. While you use B, A might be collected to free resources. Also, you should unregister LocalBroadcastManager receivers onResume(). So, no.
Singletons with static fields are generally to be avoided. It’s not worrying for a single string of text, but singleton fields are easily forgotten and can lead to memory leaks. Better to avoid this pattern if possible, and in your case it is.
Possible options.
If the field is something that must persist, e.g. user editing his username, use SharedPreferences or another storing solution (saving to a server, saving to cache, saving to device SQLite database).
If the field is temporary and is the result of activity B, you can start activity B with startActivityForResult() and then send the result back to activity A through an Intent.
For your problem the simpliest solution - store your value in Application class. Any activity can access it and read/write values.
Cons is that if you accidentally store static reference to activity, it will cause memory leak.
You may try using EventBus for horizontal communication: Activity-> Service, Service -> Fragment, etc.
It has static instance by default plus you can subscribe/unsubscribe to it in onPause and onResume methods.
Another advantage is a STICKY EVENTS - you can post event from Service and it will wait until something handle it - Activity will receive this event when it is ready - after onResume().

Android : Where should a OnSharedPreferenceChangeListener be defined/registered

Design question basically - having a PreferenceActivity should one make it implement OnSharedPreferenceChangeListener or should one define this functionality in another class - say in an inner class ? Is there some reason one would prefer the one over the other approach ?
Also where should one register the listener ? I mean the docs and common sense dictate to register/unregister in onResume/onPause respectively but having seen a zillion registrations in onCreate I just wonder if I am missing something.
Also I am not quite certain if a failure to unregister (so here for instance unregistering may not be called as onStop is not guaranteed to be called) would necessarily lead to a leak. So if I have for instance
class MyPref extends PreferenceActivity implements
OnSharedPreferenceChangeListener {
SharedPreferences sharedPreferences;
// init sharedPreferences
onStart(){
sharedPreferences.registerOnSharedPreferenceChangeListener(this);
}
// no unregistration
}
Would this leak the MyPref instance once I go back to one of my other activities ?
Lastly - would the same considerations apply to OnPreferenceChangeListener ?
Edit : coming back to that I see no way to actually unregister the OnPreferenceChangeListener - am I blind ??
I don't believe there are any major reasons to favour a particular location for the listener, apart from personal preference. Having the Activity implement it, or using an inner class - either anonymous or not - are all OK.
The only thing is, if you're not using an existing object like your Activity as the listener, you'll need to keep a reference to the listener object. As per this answer it will get garbage collected (and thus not actually listen to anything) if you don't.
Having dug into the source a bit, it seems SharedPreferencesImpl uses a WeakHashMap to contain registered listeners (source, lines 72-73, 186-196), meaning that failing to unregister won't cause a leak.
As you say, the docs do recommend using onResume() / onPause(); this is probably nothing to do with leaks, but instead to prevent background apps doing unnecessary processing - so still worth following!
Doing registration and de-registration in onPause/onResume is lots of extra work for nothing.
You could make an anonymous implementation of your listener as part of your class like this:
[class level]
...
OnSharedPreferenceChangeListener mListener = new OnSharedPreferenceChangeListener () {
onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
// your code here
}
};
...
[class level]
Then you set it wherever applicable. If you do this, your listener won't be re-created as a different object between onPause/onResume (unless your app gets killed and your Activity subclass has to be loaded again), so assigning it then is pointless, since you'll always reference the same object. On the other hand, if your app does get killed, then the onCreate will be called again.
With regards to implementing an inner class, or not, I tend to prefer having an anonymous implementation (as shown above) due to the enhanced cleanliness of the code - I don't have to bother with class names and have to type fewer brackets. However, it's really a preference thing, so do whatever you feel is better.

Lifecycle of enum based singleton on Android

A few days ago I've discovered that singleton can become anti-pattern in Android. My singleton (class with private constructor and instance stored in static field) was deleted (instance was deleted despite the fact other activities were still using this singleton (via getInstance() method) so another instance had to be created ) because Activity from which it was first invoked was deleted (after invoking finish for only this one activity).
I've read already how this problem can be resolved however I've also just read "Effective Java". There is said that "Single-element enum type is the bast way to implement a singleton".
So now I'm wondering what would be the lifecycle of singleton created this way in Android application? Would it be the same like in case of "standard singleton implementation" so after destroying activity from which it was invoked the first time it will be destroyed (even if it used also in other activities)?
I'm not asking about proper android singleton implemenation or the singleton pattern itself (is it pattern or anti-pattern etc) but I'd like to know what be the lifecycle of such enum singleton object and when it will be destroyed.
In all cases, the classes you use are tied to the ClassLoader that loaded them. This is true in Java in general, not just Android. Android will isolate activities by using new ClassLoaders each time -- at the least, it doesn't promise it won't, and it does as far as I can tell.
Any singleton, or other class-level state, is tied to the Class which is tied to the ClassLoader. This is why your state "disappears"; it's actually that your calling code is seeing a new Class in a new ClassLoader.
So, your enum-based trick, or anything else along these lines, would have exactly the same behavior. You just can't "persist" activity information this way. You can and should write to a SQLite DB. You could probably stash it in the SharedPreferences too.
The application object is a good location to store information which needs to be accessible to various activity or service instances. You can retrieve it like this (where this is an Activity or Service):
private MyApplication app;
in onCreate(...){
...
this.app = (MyApplication) this.getApplication();
...
}
Remember to set the name also in the manifest:
Set the attribute "name" of the "application" tag:
The value is the path to the class relative to the package of your app.
The application object is created when the app is started, you can initialize like in an activity or service in it's onCreate() method.
One thing to remember: The application object can survive closing your "last" activity. In this case you may get the same application object with the state from the previous interaction with your app. If this is a problem you must somehow detect the initial start of your app (e.g. using a special launcher activity without UI, which initializes the application object and then starts a new intent.
BTW, the same may happen with singletons if they have not yet been lost to garbage collection.
My secure singleton implementation is like that:
I create a singleton class which has an attribute of boolean 'didReceiveMemoryWarning';
public class SingleTon(){
public boolean didReceiveMemoryWarning = true;
...
...
}
In application first screen(It is exactly first launch screen)(I have a splash screen that is visible 3 sec)
SingleTon.getInstance().didReceiveMemoryWarning = false;
And in every Activity's onCreate() method, check this boolean data,
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if(SingleTon.getInstance().didReceiveMemoryWarning){
{ Load your data from local to your SingleTon class,
because your app was released by OS};
}
}
it is my implementation.

Android: is it possible to refer to an Activity from a 2nd Activity?

This is a pretty simple question, but I have been unable to find anyway to accomplish what I am trying to do...
I want to launch a new Activity to display some complex information. Because of the complexity, it is undesirable to serialize the information into the intent's parameters. Is it possible for the the new Activity to get a reference to the launching activity, so it can call its methods?
If you use a custom application class, you can store information that will be kept between the activities.
See a tutorial here for instance.
The lifetime of an Activity cannot be depended upon. In this case, one way of sharing data is to have a singleton which holds the data to be shared between the two activities.
You can add a public static field to the first activity containing this (the first activity).
But beware that the first activity could be destroyed by Android while you are using the second activity, so you will have to implement a fallback method if the first activity is destroyed.
And don’t forget to unset the public static variable in the onDestroy() callback of the first activity or you will leak memory.
Is it possible for the the new Activity to get a reference to the launching activity, so it can call its methods?
Please do not do that. Android can and will destroy activities to free up memory.
Complex information like you describe should not be owned by an activity. It should be held in a central data model, like you would in any other application. Whether that central data model is mediated by a Service or a singleton or a custom Application object depends a bit on the type of data, caching models, risks of memory leaks, and so on.
You can make your complex objects public and static in ActivityA, and access them in ActivityB like this:
MyCustomObjectType complexFromA = ActivityA.complexObject;
this will work, however while in ActivityB, you can't always be sure that static objects from ActivityA will exist(they may be null) since Android may terminate your application.
so then maybe add some null checking:
if(null == ActivityA.complexObject) {
//go back to ActivityA, or do something else since the object isn't there
}
else {
//business as usual, access the object
MyCustomObjectType complexFromA = ActivityA.complexObject;
}
You could also use a Singleton object which extends Application. You would have the same problem when Android terminates your application. always need to check if the object actually exists. Using the Singleton extending Application approach seems to be the more organized way - but adds more complexity to implementation. just depends what you need to do and whatever works for your implementation.
You should create a separate class that both the activities can use.
public class HelperClass{
public void sharedFunction(){
//implement function here
}
}
I would recommend staying away from static variable in android. It can cause some unexpected behavior.
Use getParent() from new activity and call parent's method
Android Activity call another Activity method

Categories

Resources