Keep valid reference of the current Activity - android

I need to show some dialogs as debug in my app.
The structure of the app itself is written so that it is easier to actually call and show the dialog from a static class with static methods. These methods all points toward a bigger method which eventually take care of the requests.
What I'd like to achieve is to call an eventual Dialog (I'm using the Material Dialog library by afollestad on github) which needs a reference to the current activity.
I actually have a private static Activity sActivity; field in the class, and the relative setActivity(Activity activity) method.
Currently, I've got my own CustomApplication from which I call this:
registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacks() {
#Override
public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
MyStaticClass.setActivity(activity);
}
[...]
}
which is not working as intended because this
try {
Utils.showSimpleDialog(sActivity, "Error", message);
} catch (MaterialDialog.DialogException d) {
d.printStackTrace();
}
is always calling the catch case.
My question is, is it possible to avoid the setActivity call from every single Activity? If yes, how? Thank in advance!

It's not a good idea to keep a static reference to an Activity as it can cause memory leaks with leaked contexts.
Edit to answer if it would still be dangerous if setting static activity to null in onDestroy as previously asked in a comment under this answer
Setting to null in onDestroy doesn't always serve as a workaround to this because if you run out of memory you can get into a state where Android can actually stop at the onPause stage of the lifecycle and not even hit onDestroy. Keeping static contexts is generally to be avoided.
It looks like showSimpleDialog already takes an Activity parameter. When you are calling it from an Activity, simply pass this , or from a fragment, pass getActivity(). If this call to showSimpleDialog is called from another utility method you've implemented, just pass an activity to that method also rather than setting a static Activity on the class.

Related

Static Utility class with Context/Activity - Android

Over the development of an Android app I've come to a collection of utility-type methods that I have put into a static class. All these methods are used across multiple Activities and most of them do not require any information from the calling Activity.
However, I now have some methods that require the Context of the Activity and one that requires the Activity itself. Let me exemplify some of them:
getDeviceNaturalOrientation() - Uses an Activity's
getWindow().getWindowManager().getDefaultDisplay() to access the
displays rotation, width, and height to determine the device's
natural orientation.
getDeviceOrientation() - Similar to the above but to get the current
orientation
createFile() - Uses the Context to to access some resources (strings) and to
create and show some Toasts
Now, my big questions regarding this Utils class are:
So far, each function takes a Context parameter which I pass from whatever Activity I'm on, but would it be OK to create a static Context or Activity variable in the Utils class and set it at the beginning of each Activity (like in onCreate)? This variable would be used in whatever functions require a Context or Activity instance.
Assuming the above is not recommended, is it OK to pass an Activity parameter to a method or is there a reason to only pass an Activity as Context? The methods I use for the device orientation functions above are specific to Activity objects, not Context, so either I pass as Activity or pass as Context and cast into Activity (the latter sounding like a terrible idea).
Also, I am very open to the idea that this Util class may not be the way to go for these methods that require Context/Activity, so I would welcome alternatives that would still prevent having copies of these methods in each activity class that uses them.
1)A static link to a context is likely to cause a memory leak. It means that a reference to the Activity will be kept around in the static variable even after its destroyed, so all of the memory of the activity and all its views will remain valid and not be cleaned by gc. You can do this, but you have to be careful to null out the variable when done. Its better just to avoid it.
2)Its a little bit awkward to pass the activity as an Activity, but no technical reason not to. At that point we're arguing over code cleanliness/maintainability. And there are times where the non-clean solution is just easier. Of course in the cases above I'd rather pass the orientation/display/Resources objects to the function than pass the entire context or make special accessors.
I think following design should be fine when you call from Activity
MyUtility utility=new MyUtility();
utility.getDeviceNaturalOrientation(this);
utility.getFile(this);
And you can define these function like
public int getDeviceNaturalOrientation(Activity activity){
//code
return some_oreientation
}
and like this
public File getFile(Context context){
//code
//return file handler
}
Activity is the subclass of Context so you can even change the design to following
MyUtility utility=new MyUtility(this); //this refer to Activity
utility.getDeviceNaturalOrientation();
utility.getFile();
As long as you pass activity you are fine but if you do following from your activity you will get error from first method call
MyUtility utility=new MyUtility(getApplicationContext());
utility.getDeviceNaturalOrientation(); //will throw exception
utility.getFile();
And, yes first idea is not a recommended way.
I would suggest you to send a WeakReference of your Activity or getApplicationContext() (for those works which can work using it) and don't use static method because it cause memory leaks. Read Developer blog also

super.onCreate(savedInstanceState);

I have created an Android Application Project and in MainActivity.java > onCreate() it is calling super.onCreate(savedInstanceState).
As a beginner, can anyone explain what is the purpose of the above line?
Every Activity you make is started through a sequence of method calls. onCreate() is the first of these calls.
Each and every one of your Activities extends android.app.Activity either directly or by subclassing another subclass of Activity.
In Java, when you inherit from a class, you can override its methods to run your own code in them. A very common example of this is the overriding of the toString() method when extending java.lang.Object.
When we override a method, we have the option of completely replacing the method in our class, or of extending the existing parent class' method. By calling super.onCreate(savedInstanceState);, you tell the Dalvik VM to run your code in addition to the existing code in the onCreate() of the parent class. If you leave out this line, then only your code is run. The existing code is ignored completely.
However, you must include this super call in your method, because if you don't then the onCreate() code in Activity is never run, and your app will run into all sorts of problem like having no Context assigned to the Activity (though you'll hit a SuperNotCalledException before you have a chance to figure out that you have no context).
In short, Android's own classes can be incredibly complex. The code in the framework classes handles stuff like UI drawing, house cleaning and maintaining the Activity and application lifecycles. super calls allow developers to run this complex code behind the scenes, while still providing a good level of abstraction for our own apps.
*Derived class onCreate(bundle) method must call superclass implementation of this method. It will throw an exception SuperNotCalledException if the "super" keyword is not used.
For inheritance in Java, to override the superclass method and also to execute the above class method, use super.methodname() in the overriding derived class method;
Android class works in the same way. By extending the Activity class which have onCreate(Bundle bundle) method in which meaningful code is written and to execute that code in the defined activity, use the super keyword with the method onCreate() like super.onCreate(bundle).
This is a code written in Activity class onCreate() method and Android Dev team might add some more meaningful code to this method later. So, in order to reflect the additions, you are supposed to call the super.onCreate() in your Activity class.
protected void onCreate(Bundle savedInstanceState) {
mVisibleFromClient = mWindow.getWindowStyle().getBoolean(
com.android.internal.R.styleable.Window_windowNoDisplay, true);
mCalled = true;
}
boolean mVisibleFromClient = true;
/**
* Controls whether this activity main window is visible. This is intended
* only for the special case of an activity that is not going to show a
* UI itself, but can't just finish prior to onResume() because it needs
* to wait for a service binding or such. Setting this to false prevents the UI from being shown during that time.
*
* <p>The default value for this is taken from the
* {#link android.R.attr#windowNoDisplay} attribute of the activity's theme.
*/
It also maintains the variable mCalled which means you have called the super.onCreate(savedBundleInstance) in your Activity.
final void performStart() {
mCalled = false;
mInstrumentation.callActivityOnStart(this);
if (!mCalled) {
throw new SuperNotCalledException(
"Activity " + mComponent.toShortString() +
" did not call through to super.onStart()");
}
}
See source code here.
http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/1.5_r4/android/app/Activity.java#Activity.onCreate%28android.os.Bundle%29
Because upon super.onCreate() it will reach the Activity (parent class of any activity) class to load the savedInstanceState,and we normaly don't set any saved instance state, but
android framework made such a way that, we should be calling that.
It is information you want returned to your application, via onCreate(),
if the activity is destroyed and restarted due to some implicit reason
(e.g., not because the user pressed the back button). The most common
use of onSaveInstanceState() is to handle screen rotations, as by
default, activities are destroyed and recreated when the user slides out
the G1 keyboard.
The reason to call super.onCreate(savedInstanceState) is because your
code will not compile otherwise. ;-)

getApplication() and spanning Activities within a given Applications

I've spent a while reading up on this and have yet to find an answer.
I have a few activities all within one app. I'd like to share some data between them that is not suitable for intents or serialized data files. I know I can use the overall Application for this purpose, but I'm not fully understanding how it works.
We'll call my overall application class "MyApplication" and the main activity "MyActivity". I would expect that once the app begins from a cold start, it first instantiates and initializes MyApplication (calling onCreate()) and then goes on to instantiate and start my activity (MyActivity) (which was marked with the intent android.intent.action.MAIN). The logs seem to bear this out.
But... if I try to set the following Activity-wide variable, it gets set to null:
private final MyApplication THISAPP = (MyApplication)getApplication();
This is peculiar because the application definitely exists at this point. (Also, I can't delay setting a final variable til MyActivity.onCreate() gets called - that's disallowed ("The final field...cannot be assigned")).
Note that this works if I don't call THISAPP "final" and I assign it in onCreate(). That of course defeats the purpose of protecting a variable with "final" though, and it seems like it should be possible to do.
So, why isn't getApplication() producing a non-null value before onCreate()? Or is this some strange matter of context? (I've found a vague reference to context not being valid til after onCreate() is done, but would that apply to the parent Application's context as seen from a child Activity? I'd expect the parent to already have that set at that point.)
Edit: to emphasize, I can get this to work if I don't try to use final and if I remember to put the "." before the application name in the manifest (a mistake I made and corrected well-before this I wrote this question). It just isn't clear why getApplication() (or getApplicationContext() for that matter) aren't usable before onCreate() in a child Activity... unless "that's just how it is".
I'd like to share some data between them that is not suitable for intents or serialized data files. I know I can use the overall Application for this purpose, but I'm not fully understanding how it works.
I'd just use static data members as a cache for your persistent store, rather than fussing around with Application. Application gives you little advantage over static data members and has one big cost: there can only be one Application, whereas you can organize your static data members/singletons however you like.
I would expect that once the app begins from a cold start, it first instantiates and initializes MyApplication (calling onCreate()) and then goes on to instantiate and start my activity (MyActivity) (which was marked with the intent android.intent.action.MAIN). The logs seem to bear this out.
Correct.
But... if I try to set the following Activity-wide variable, it gets set to null: private final MyApplication THISAPP = (MyApplication)getApplication();
Of course. The activity object has not yet been initialized. You are welcome to use initializers like this for constants or things you get from static methods (e.g., Calendar.getInstance()) or other constructors (e.g., new ArrayList<Foo>()). Do not call a superclass method an expect it to work at this point, since the constructor chain has not yet been called on the object being instantiated. This is just Java, nothing particular to Android.
Have you done it the following way?
Created a class that implements Application
public class MySuperApplication extends Application
{
public String SomeSetting;
}
Defined which is your Application class in manifest (important!)
<application android:name=".MySuperApplication" android:icon="#drawable/icon"
and then accessed it in your activities
app = (MySuperApplication) getApplication();
app.SomeSetting = "Test";
This should work. It does in my case.
AFAIK, You cannot declare a empty final variable and initialize it onCreate. In java it is initialized during declaring the variable or in the constructor.
THISAPP cannot be initialized to Application object during declaration because the Application doesn't exist during compile time. Because onCreate is not a constructor, you cannot declare a empty final variable and initialize it in onCreate.
Instance variables are created when the object (in this case an Activity object) is constructed. For getApplication() to produce a valid value then the Activity object would have to have been provided enough context (little 'c') at instantiation for this to occur.
The source for getApplication() in Activity.java is just this:
/** Return the application that owns this activity. */
public final Application getApplication() {
return mApplication;
}
Here is where it is set:
final void attach(Context context, ActivityThread aThread, Instrumentation instr, IBinder token,
Application application, Intent intent, ActivityInfo info, CharSequence title,
Activity parent, String id, Object lastNonConfigurationInstance,
Configuration config) {
// other code
mApplication = application;
// other code
}
Since mApplication is not set until after attach is called, it won't be available to getApplication() on instantiation of your Activity.

Android: Access method in an activity from another activity

My launch activity starts up another activity whose launch is set to single instance. In this 2nd activity, I have a public method. I then start up a 3rd activity and that activity needs to access the public method in the 2nd activity. I don't want to use startActivity and pass it extras because I assume the onCreate will get called (or am I wrong?) and I need to avoid the 2nd activity from reinitializing itself.
When an activity is started using startActivity, is it possible to gain access to the underlying class instance itself and simply call the method?
I actually came up with a simple solution. As a matter of fact you can access the underlying class of an activity. First, you create a class that is used to hold a public static reference to activity 2. When activity 2 is created, in its onCreate method you store "this" in the static reference. Activity 2 implements an interface with the methods that you want available to any other activity or object. The static reference you hold would be of a data type of this interface. When another activity wants to call a method in this activity, it simply accesses the public static reference and calls the method. This is no hack but is intrinsic to how Java operates and is totally legitimate.
It is not a good idea.
As I can understand method from second activity is actually not connected to particular activity while you want to call it from another one. So carry the method out to other (non-activity) class (maybe static method) and use it from both activities.
It's not directly possible to gain access to activity object started using startActivity (without using some hacks). And frankly you shouldn't even trying to accomplish this.
One Activity component can cycle through several Activity java object while its alive. For example, when user rotates the screen, old object is discarded and new activity object is created. But this is still one Activity component.
From my experience, when you need to do things you described, there is something wrong with your architecture. You either should move part of activity's responsibilities to Service or to ContentProvider, or use Intents, etc. Its hard to recommend anything more specific without knowing more details.
No there is no way to pass a reference via startActivity() however you can use some sort of shared memory to keep reference to your Activity. This is probably a bad design. However passing an extra with your Intent will not cause onCreate, that is completely related to the lifecycle.

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